diff --git a/DEPS b/DEPS
index 82e67db..0cdaaacf 100644
--- a/DEPS
+++ b/DEPS
@@ -121,11 +121,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': '26ccfcc96f989d90db388ddd7fb8604b7511db48',
+  'skia_revision': '0783aca7ba3189092855b373911cffc1b1303222',
   # 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': 'd5b026733ee422f3f4e1a23a922aae7216e0ec14',
+  'v8_revision': '22770bddcb54c97cf3e903404637be7f09fd07c6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -133,7 +133,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': '91d469aae06381ec1608975536a81288fa119df3',
+  'angle_revision': 'd01c504e0c185ab2afa53dce85f9d0a6a31d761e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -141,11 +141,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': 'cd610c9a9dbc9a827900725a0ae2645e37a8a4b2',
+  'swiftshader_revision': '8f71f7311f29a2ef769d3d61bdbc73ecd6ceccb2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '33cc9c60892a0d527bf7aee95c8be8d298119efd',
+  'pdfium_revision': 'fef199e2a2aacb5a328cc40aec03610e8949f1c0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -185,7 +185,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '8af4b4b644430445f8f5b31047f85cf06058c23d',
+  'catapult_revision': 'a308a9443ca9c061ba0e5eba0df40d1c8bfd637e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -233,7 +233,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'spv_tools_revision': '37861ac106027220fa327bd088d1635c352fb5f0',
+  'spv_tools_revision': 'adbbe20241db7f3e8fcc3f02dd970fad486d4191',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -249,7 +249,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
-  'dawn_revision': 'baa3741c9fbe88e83e6d8cbbea1ca44d53e1da24',
+  'dawn_revision': 'd1be0e70770849c093154fd0892f0495328ed8c2',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -694,7 +694,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '45c7f2def3e9c454ccc988ff3445feb5ddd3d4ba',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'e62d2e1b75b03e230935be900d68c45768d917c7',
       'condition': 'checkout_linux',
   },
 
@@ -709,7 +709,7 @@
 
   # For Linux and Chromium OS.
   'src/third_party/cros_system_api': {
-      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '443f025d0c62f17074ba3a625a5d5723ff092d79',
+      'url': Var('chromium_git') + '/chromiumos/platform2/system_api.git' + '@' + '9ae41aa0622c81f337d09e53e41a28674b46a45e',
       'condition': 'checkout_linux',
   },
 
@@ -719,7 +719,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + 'a1fbdff17736899759ae1d320ac684122bef21bd',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '367af22db5a97a21c370c0db974de501673e061a',
 
   'src/third_party/devtools-node-modules':
     Var('chromium_git') + '/external/github.com/ChromeDevTools/devtools-node-modules' + '@' + Var('devtools_node_modules_revision'),
@@ -774,7 +774,7 @@
   'src/third_party/harfbuzz-ng/src':
     Var('chromium_git') + '/external/github.com/harfbuzz/harfbuzz.git' + '@' + Var('harfbuzz_revision'),
 
-  'src/third_party/emoji-segmenter/src/':
+  'src/third_party/emoji-segmenter/src':
     Var('chromium_git') + '/external/github.com/googlei18n/emoji-segmenter.git' + '@' + Var('emoji_segmenter_revision'),
 
   # Chrome OS touchpad gestures library.
@@ -1054,7 +1054,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' +  '1bc7c76b73c8e305bb66b1dbfd40d43bc2d2f245',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' +  'e3e66f120fbb129f396292064cf279149883f1a2',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + 'ac0d98b5cee6c024b0cffeb4f8f45b6fc5ccdb78',
@@ -1217,7 +1217,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'db52df17f0d012983dc281e4864c71485a86bd0e',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + 'a34d7766c56ae5e648c1db91edd380069f2300d6',
+    Var('webrtc_git') + '/src.git' + '@' + '69b761e52b10837c3b9f5f28ace9fe9704693784',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1258,7 +1258,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b4138ddf946e15269718c688bc2a2d3927a3f322',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@f8840fffa6cc539692d142f76ec9aa22e7bd83b3',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/aapt2.config b/android_webview/aapt2.config
index 6e6b76b..c8b63db 100644
--- a/android_webview/aapt2.config
+++ b/android_webview/aapt2.config
@@ -1 +1,2 @@
 string/license_activity_title#no_obfuscate
+drawable/shortcut_incognito#no_obfuscate
diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc
index 7368ae92..4554377 100644
--- a/android_webview/browser/aw_browser_context.cc
+++ b/android_webview/browser/aw_browser_context.cc
@@ -59,8 +59,6 @@
 
 namespace {
 
-const base::FilePath::CharType kChannelIDFilename[] = "Origin Bound Certs";
-
 const void* const kDownloadManagerDelegateKey = &kDownloadManagerDelegateKey;
 
 AwBrowserContext* g_browser_context = NULL;
@@ -144,9 +142,9 @@
   // TODO(ntfschr): set this to nullptr when the NetworkService is disabled,
   // once we remove a dependency on url_request_context_getter_
   // (http://crbug.com/887538).
-  url_request_context_getter_ = new AwURLRequestContextGetter(
-      cache_path, context_storage_path_.Append(kChannelIDFilename),
-      CreateProxyConfigService(), user_pref_service_.get(), net_log);
+  url_request_context_getter_ =
+      new AwURLRequestContextGetter(cache_path, CreateProxyConfigService(),
+                                    user_pref_service_.get(), net_log);
 
   scoped_refptr<base::SequencedTaskRunner> db_task_runner =
       base::CreateSequencedTaskRunnerWithTraits(
diff --git a/android_webview/browser/aw_metrics_service_client.cc b/android_webview/browser/aw_metrics_service_client.cc
index 53193d8..560d41ca 100644
--- a/android_webview/browser/aw_metrics_service_client.cc
+++ b/android_webview/browser/aw_metrics_service_client.cc
@@ -254,8 +254,8 @@
 
 std::unique_ptr<metrics::MetricsLogUploader>
 AwMetricsServiceClient::CreateUploader(
-    base::StringPiece server_url,
-    base::StringPiece insecure_server_url,
+    const GURL& server_url,
+    const GURL& insecure_server_url,
     base::StringPiece mime_type,
     metrics::MetricsLogUploader::MetricServiceType service_type,
     const metrics::MetricsLogUploader::UploadCallback& on_upload_complete) {
diff --git a/android_webview/browser/aw_metrics_service_client.h b/android_webview/browser/aw_metrics_service_client.h
index f9a33b4f9..20d8795 100644
--- a/android_webview/browser/aw_metrics_service_client.h
+++ b/android_webview/browser/aw_metrics_service_client.h
@@ -71,8 +71,8 @@
   std::string GetVersionString() override;
   void CollectFinalMetricsForLog(const base::Closure& done_callback) override;
   std::unique_ptr<metrics::MetricsLogUploader> CreateUploader(
-      base::StringPiece server_url,
-      base::StringPiece insecure_server_url,
+      const GURL& server_url,
+      const GURL& insecure_server_url,
       base::StringPiece mime_type,
       metrics::MetricsLogUploader::MetricServiceType service_type,
       const metrics::MetricsLogUploader::UploadCallback& on_upload_complete)
diff --git a/android_webview/browser/aw_proxying_url_loader_factory.cc b/android_webview/browser/aw_proxying_url_loader_factory.cc
index ae8b18fa8..ec2370b3 100644
--- a/android_webview/browser/aw_proxying_url_loader_factory.cc
+++ b/android_webview/browser/aw_proxying_url_loader_factory.cc
@@ -138,12 +138,14 @@
 
   // TODO(timvolodine): consider factoring this out of this class.
   void OnReceivedErrorToCallback(int error_code);
+  bool ShouldNotInterceptRequest();
 
   const int process_id_;
   const uint64_t request_id_;
   const int32_t routing_id_;
   const uint32_t options_;
   bool input_stream_previously_failed_ = false;
+  bool request_was_redirected_ = false;
 
   network::ResourceRequest request_;
   const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
@@ -247,15 +249,16 @@
   request_.load_flags =
       UpdateLoadFlags(request_.load_flags, io_thread_client.get());
 
-  if (!input_stream_previously_failed_ &&
-      (request_.url.SchemeIs(url::kContentScheme) ||
-       android_webview::IsAndroidSpecialFileUrl(request_.url))) {
-    // Do not call shouldInterceptRequest callback for special android urls,
-    // unless they fail to load on first attempt. Special android urls are urls
-    // such as "file:///android_asset/", "file:///android_res/" urls or
-    // "content:" scheme urls.
+  if (ShouldNotInterceptRequest()) {
+    // equivalent to no interception
     InterceptResponseReceived(nullptr);
   } else {
+    if (request_.referrer.is_valid()) {
+      // intentionally override if referrer header already exists
+      request_.headers.SetHeader(net::HttpRequestHeaders::kReferer,
+                                 request_.referrer.spec());
+    }
+
     // TODO: verify the case when WebContents::RenderFrameDeleted is called
     // before network request is intercepted (i.e. if that's possible and
     // whether it can result in any issues).
@@ -266,6 +269,20 @@
   }
 }
 
+// logic for when not to invoke shouldInterceptRequest callback
+bool InterceptedRequest::ShouldNotInterceptRequest() {
+  if (request_was_redirected_)
+    return true;
+
+  // Do not call shouldInterceptRequest callback for special android urls,
+  // unless they fail to load on first attempt. Special android urls are urls
+  // such as "file:///android_asset/", "file:///android_res/" urls or
+  // "content:" scheme urls.
+  return !input_stream_previously_failed_ &&
+         (request_.url.SchemeIs(url::kContentScheme) ||
+          android_webview::IsAndroidSpecialFileUrl(request_.url));
+}
+
 void SetRequestedWithHeader(net::HttpRequestHeaders& headers) {
   // We send the application's package name in the X-Requested-With header for
   // compatibility with previous WebView versions. This should not be visible to
@@ -440,6 +457,7 @@
     const network::ResourceResponseHead& head) {
   // TODO(timvolodine): handle redirect override.
   // TODO(timvolodine): handle unsafe redirect case.
+  request_was_redirected_ = true;
   target_client_->OnReceiveRedirect(redirect_info, head);
   request_.url = redirect_info.new_url;
   request_.method = redirect_info.new_method;
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 12af6a11..50709653 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter.cc
@@ -183,12 +183,10 @@
 
 AwURLRequestContextGetter::AwURLRequestContextGetter(
     const base::FilePath& cache_path,
-    const base::FilePath& channel_id_path,
     std::unique_ptr<net::ProxyConfigServiceAndroid> config_service,
     PrefService* user_pref_service,
     net::NetLog* net_log)
     : cache_path_(cache_path),
-      channel_id_path_(channel_id_path),
       net_log_(net_log),
       proxy_config_service_(std::move(config_service)),
       proxy_config_service_android_(proxy_config_service_.get()),
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 208b2987..25f0cfd 100644
--- a/android_webview/browser/net/aw_url_request_context_getter.h
+++ b/android_webview/browser/net/aw_url_request_context_getter.h
@@ -39,7 +39,6 @@
  public:
   AwURLRequestContextGetter(
       const base::FilePath& cache_path,
-      const base::FilePath& channel_id_path,
       std::unique_ptr<net::ProxyConfigServiceAndroid> config_service,
       PrefService* pref_service,
       net::NetLog* net_log);
@@ -87,7 +86,6 @@
   void UpdateAndroidAuthNegotiateAccountType();
 
   const base::FilePath cache_path_;
-  const base::FilePath channel_id_path_;
 
   net::NetLog* net_log_;
   std::unique_ptr<net::ProxyConfigServiceAndroid> proxy_config_service_;
diff --git a/android_webview/browser/net/aw_url_request_context_getter_unittest.cc b/android_webview/browser/net/aw_url_request_context_getter_unittest.cc
index 9fe56f7..b8221bae 100644
--- a/android_webview/browser/net/aw_url_request_context_getter_unittest.cc
+++ b/android_webview/browser/net/aw_url_request_context_getter_unittest.cc
@@ -74,8 +74,8 @@
             .release()));
 
     getter_ = base::MakeRefCounted<android_webview::AwURLRequestContextGetter>(
-        temp_dir_.GetPath(), temp_dir_.GetPath().AppendASCII("ChannelID"),
-        std::move(config_service_android), pref_service_.get(), &net_log_);
+        temp_dir_.GetPath(), std::move(config_service_android),
+        pref_service_.get(), &net_log_);
 
     // AwURLRequestContextGetter implicitly depends on having protocol handlers
     // provided for url::kBlobScheme, url::kFileSystemScheme, and
diff --git a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java
index 1f2f9b4..f3b69c4 100644
--- a/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java
+++ b/android_webview/glue/java/src/com/android/webview/chromium/SharedWebViewContentsClientAdapter.java
@@ -206,12 +206,22 @@
             TraceEvent.begin("WebViewContentsClientAdapter.onReceivedHttpError");
             if (TRACE) Log.i(TAG, "onReceivedHttpError=" + request.url);
             if (mSupportLibClient.isFeatureAvailable(Features.RECEIVE_HTTP_ERROR)) {
+                String reasonPhrase = response.getReasonPhrase();
+                if (reasonPhrase == null || reasonPhrase.isEmpty()) {
+                    // We cannot pass a null or empty reasonPhrase, because this version of the
+                    // WebResourceResponse constructor will throw. But we may legitimately not
+                    // receive a reasonPhrase in the HTTP response, since HTTP/2 removed
+                    // Reason-Phrase from the spec (and discourages it). Instead, assign some dummy
+                    // value to avoid the crash. See http://crbug.com/925887.
+                    reasonPhrase = "UNKNOWN";
+                }
+
                 // Note: we do not create an immutable instance here, because that constructor is
                 // not available on L.
                 mSupportLibClient.onReceivedHttpError(mWebView,
                         new WebResourceRequestAdapter(request),
                         new WebResourceResponse(response.getMimeType(), response.getCharset(),
-                                response.getStatusCode(), response.getReasonPhrase(),
+                                response.getStatusCode(), reasonPhrase,
                                 response.getResponseHeaders(), response.getData()));
             } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                 GlueApiHelperForM.onReceivedHttpError(mWebViewClient, mWebView, request, response);
diff --git a/android_webview/tools/run_cts.py b/android_webview/tools/run_cts.py
index 058b1071..2a9fff8 100755
--- a/android_webview/tools/run_cts.py
+++ b/android_webview/tools/run_cts.py
@@ -23,6 +23,7 @@
 from devil.android.sdk import version_codes  # pylint: disable=import-error
 from devil.android.tools import script_common  # pylint: disable=import-error
 from devil.utils import cmd_helper  # pylint: disable=import-error
+from pylib.utils import test_filter # pylint: disable=import-error
 
 # cts test archives for all platforms are stored in this bucket
 # contents need to be updated if there is an important fix to any of
@@ -55,6 +56,9 @@
     abis.ARM: 'arm64',
 }
 
+FILE_FILTER_OPT = '--test-launcher-filter-file'
+TEST_FILTER_OPT = '--test-filter'
+ISOLATED_FILTER_OPT = '--isolated-script-test-filter'
 
 def GetCtsInfo(arch, platform, item):
   """Gets contents of CTS Info for arch and platform.
@@ -70,6 +74,12 @@
                     (item, arch, platform))
 
 
+def GetCTSModuleNames(arch, platform):
+  """Gets the module apk name of the arch and platform"""
+  test_runs = GetCtsInfo(arch, platform, 'test_runs')
+  return [os.path.basename(r['apk']) for r in test_runs]
+
+
 def GetExpectedFailures():
   """Gets list of tests expected to fail in <class>#<method> format.
 
@@ -83,9 +93,23 @@
                               for m in methods])
   return expected_failures
 
-def GetTestRunFilterArg(test_run, skip_expected_failures):
+
+def GetTestRunFilterArg(args, test_run):
+  """ Filters specified in args override others """
+  filter_args = []
+
+  if args.test_filter_file:
+    filter_args.append(FILE_FILTER_OPT + '=' + args.test_filter_file)
+  if args.test_filter:
+    filter_args.append(TEST_FILTER_OPT + '=' + args.test_filter)
+  if args.isolated_script_test_filter:
+    filter_args.append(ISOLATED_FILTER_OPT + '='
+                       + args.isolated_script_test_filter)
+  if filter_args:
+    return filter_args
+
   skips = []
-  if skip_expected_failures:
+  if args.skip_expected_failures:
     skips = GetExpectedFailures()
 
   excludes = test_run.get("excludes", [])
@@ -93,38 +117,24 @@
   assert len(excludes) == 0 or len(includes) == 0, \
          "test_runs error, can't have both includes and excludes: %s" % test_run
   if len(includes) > 0:
-    return ['-f=' + ':'.join([i["match"] for i in includes])]
+    return ['--test-filter=' + ':'.join([i["match"] for i in includes])]
   else:
     skips.extend([i["match"] for i in excludes])
     if len(skips) > 0:
-      return ['-f=' + "-" + ':'.join(skips)]
+      return ['--test-filter=' + "-" + ':'.join(skips)]
     return []
 
-def RunCTS(test_runner_args, local_cts_dir, test_run,
-           skip_expected_failures=True, json_results_file=None):
+
+def RunCTS(test_runner_args, local_cts_dir, apk, json_results_file=None):
   """Run tests in apk using test_runner script at _TEST_RUNNER_PATH.
 
-  Returns the script result code,
-  tests expected to fail will be skipped unless skip_expected_failures
-  is set to False, test results will be stored in
-  the json_results_file file if specified
+  Returns the script result code, test results will be stored in
+  the json_results_file file if specified.
   """
 
-  apk = test_run['apk']
-
   local_test_runner_args = test_runner_args + ['--test-apk',
                                                os.path.join(local_cts_dir, apk)]
 
-  # TODO(mikecase): This doesn't work at all with the
-  # --gtest-filter test runner option currently. The
-  # filter options will just override eachother.
-  # The preferred method is to specify test filters per release in
-  # the CTS_GCS path file.  It will override any
-  # previous filters, including ones in expected failures
-  # file.
-  local_test_runner_args.extend(GetTestRunFilterArg(test_run,
-                                                    skip_expected_failures))
-
   if json_results_file:
     local_test_runner_args += ['--json-results-file=%s' %
                                json_results_file]
@@ -202,19 +212,25 @@
     cts_results_json = {}
     for cts_test_run in cts_test_runs:
       iteration_cts_result = 0
+
+      test_apk = cts_test_run['apk']
+      # If --module-apk is specified then skip tests in all other modules
+      if args.module_apk and os.path.basename(test_apk) != args.module_apk:
+        continue
+
+      iter_test_runner_args = test_runner_args + GetTestRunFilterArg(
+          args, cts_test_run)
+
       if json_results_file:
         with tempfile.NamedTemporaryFile() as iteration_json_file:
-          iteration_cts_result = RunCTS(test_runner_args, local_cts_dir,
-                                        cts_test_run,
-                                        args.skip_expected_failures,
-                                        iteration_json_file.name)
+          iteration_cts_result = RunCTS(iter_test_runner_args, local_cts_dir,
+                                        test_apk, iteration_json_file.name)
           with open(iteration_json_file.name) as f:
             additional_results_json = json.load(f)
             MergeTestResults(cts_results_json, additional_results_json)
       else:
-        iteration_cts_result = RunCTS(test_runner_args, local_cts_dir,
-                                      cts_test_run,
-                                      args.skip_expected_failures)
+        iteration_cts_result = RunCTS(iter_test_runner_args, local_cts_dir,
+                                      test_apk)
       if iteration_cts_result:
         cts_result = iteration_cts_result
     if json_results_file:
@@ -235,6 +251,7 @@
   """
   return _SDK_PLATFORM_DICT.get(device.build_version_sdk)
 
+
 def DetermineArch(device):
   """Determines which architecture to use based on the device properties
 
@@ -254,6 +271,7 @@
                device.product_cpu_abi)
   return arch
 
+
 def main():
   parser = argparse.ArgumentParser()
   parser.add_argument(
@@ -273,7 +291,8 @@
   parser.add_argument(
       '--skip-expected-failures',
       action='store_true',
-      help='Option to skip all tests that are expected to fail.')
+      help="Option to skip all tests that are expected to fail.  Can't be used "
+           "with test filters.")
   parser.add_argument(
       '--apk-dir',
       help='Directory to extract CTS APKs to. '
@@ -287,6 +306,14 @@
       help='If set, will dump results in JSON form to the specified file. '
            'Note that this will also trigger saving per-test logcats to '
            'logdog.')
+  parser.add_argument(
+      '-m',
+      '--module-apk',
+      dest='module_apk',
+      help='CTS module apk name in ' + _WEBVIEW_CTS_GCS_PATH_FILE +
+      ' file, without the path prefix.')
+
+  test_filter.AddFilterOptions(parser)
   script_common.AddDeviceArguments(parser)
 
   args, test_runner_args = parser.parse_known_args()
@@ -307,6 +334,24 @@
 
   arch = args.arch if args.arch else DetermineArch(device)
 
+  if (args.test_filter_file or args.test_filter
+      or args.isolated_script_test_filter):
+    if args.skip_expected_failures:
+      # TODO(aluo): allow both options to be used together so that expected
+      # failures in the filtered test set can be skipped
+      raise Exception('--skip-expected-failures and test filters are mutually'
+                      ' exclusive')
+    # TODO(aluo): auto-determine the module based on the test filter and the
+    # available tests in each module
+    if not args.module_apk:
+      args.module_apk = 'CtsWebkitTestCases.apk'
+
+  platform_modules = GetCTSModuleNames(arch, args.platform)
+  if args.module_apk and args.module_apk not in platform_modules:
+    raise Exception('--module-apk for arch==' + arch + 'and platform=='
+                    + args.platform + ' must be one of: '
+                    + ', '.join(platform_modules))
+
   return RunAllCTSTests(args, arch, test_runner_args)
 
 
diff --git a/android_webview/tools/run_cts_test.py b/android_webview/tools/run_cts_test.py
index babf4ca..f6eab53 100644
--- a/android_webview/tools/run_cts_test.py
+++ b/android_webview/tools/run_cts_test.py
@@ -19,6 +19,19 @@
   """Unittests for the run_cts module.
   """
 
+  _EXCLUDED_TEST = 'bad#test'
+  # This conforms to schema of the test_run entry in
+  # cts_config/webview_cts_gcs_path.json
+  _CTS_RUN = {'apk': 'module.apk', 'excludes': [{'match': _EXCLUDED_TEST}]}
+
+  @staticmethod
+  def _getArgsMock(**kwargs):
+    args = {'test_filter_file': None, 'test_filter': None,
+            'isolated_script_test_filter': None,
+            'skip_expected_failures': False}
+    args.update(kwargs)
+    return mock.Mock(**args)
+
   def testDetermineArch_arm64(self):
     logging_mock = mock.Mock()
     logging.info = logging_mock
@@ -33,6 +46,81 @@
     with self.assertRaises(Exception) as _:
       run_cts.DetermineArch(device)
 
+  def testNoFilter_SkipExpectedFailures(self):
+    mock_args = self._getArgsMock(skip_expected_failures=True)
+    skips = run_cts.GetExpectedFailures()
+    skips.append(self._EXCLUDED_TEST)
+    self.assertEqual([run_cts.TEST_FILTER_OPT + '=-' + ':'.join(skips)],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testNoFilter_ExcludedMatches(self):
+    mock_args = self._getArgsMock(skip_expected_failures=False)
+    self.assertEqual([run_cts.TEST_FILTER_OPT + '=-' + self._EXCLUDED_TEST],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testFilter_OverridesExcludedMatches(self):
+    mock_args = self._getArgsMock(test_filter='good#test',
+                                  skip_expected_failures=False)
+    self.assertEqual([run_cts.TEST_FILTER_OPT + '=good#test'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testFilter_OverridesAll(self):
+    mock_args = self._getArgsMock(test_filter='good#test',
+                                  skip_expected_failures=True)
+    self.assertEqual([run_cts.TEST_FILTER_OPT + '=good#test'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testFilter_ForMultipleTests(self):
+    mock_args = self._getArgsMock(test_filter='good#t1:good#t2',
+                                  skip_expected_failures=True)
+    self.assertEqual([run_cts.TEST_FILTER_OPT + '=good#t1:good#t2'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testIsolatedFilter_OverridesExcludedMatches(self):
+    mock_args = self._getArgsMock(isolated_script_test_filter='good#test',
+                                  skip_expected_failures=False)
+    self.assertEqual([run_cts.ISOLATED_FILTER_OPT + '=good#test'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testIsolatedFilter_OverridesAll(self):
+    mock_args = self._getArgsMock(isolated_script_test_filter='good#test',
+                                  skip_expected_failures=True)
+    self.assertEqual([run_cts.ISOLATED_FILTER_OPT + '=good#test'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testIsolatedFilter_ForMultipleTests(self):
+    # Isolated test filters use :: to separate matches
+    mock_args = self._getArgsMock(
+        isolated_script_test_filter='good#t1::good#t2',
+        skip_expected_failures=True)
+    self.assertEqual([run_cts.ISOLATED_FILTER_OPT + '=good#t1::good#t2'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testFilterFile_OverridesExcludedMatches(self):
+    mock_args = self._getArgsMock(test_filter_file='test.filter',
+                                  skip_expected_failures=False)
+    self.assertEqual([run_cts.FILE_FILTER_OPT + '=test.filter'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testFilterFile_OverridesAll(self):
+    mock_args = self._getArgsMock(test_filter_file='test.filter',
+                                  skip_expected_failures=True)
+    self.assertEqual([run_cts.FILE_FILTER_OPT + '=test.filter'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testNegative_Filter(self):
+    mock_args = self._getArgsMock(test_filter='-good#t1:good#t2',
+                                  skip_expected_failures=True)
+    self.assertEqual([run_cts.TEST_FILTER_OPT + '=-good#t1:good#t2'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
+  def testNegative_IsolatedFilter(self):
+    mock_args = self._getArgsMock(
+        isolated_script_test_filter='-good#t1::good#t2',
+        skip_expected_failures=True)
+    self.assertEqual([run_cts.ISOLATED_FILTER_OPT + '=-good#t1::good#t2'],
+                     run_cts.GetTestRunFilterArg(mock_args, self._CTS_RUN))
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index 4251fa8d..f8c779f0 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -6,7 +6,6 @@
 import("//build/config/ui.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//services/service_manager/public/cpp/service_executable.gni")
-import("//services/service_manager/public/service_manifest.gni")
 import("//testing/test.gni")
 import("//tools/grit/repack.gni")
 import("//ui/base/ui_features.gni")
@@ -1342,7 +1341,6 @@
     "//ui/base",
     "//ui/base:ui_data_pack",
     "//ui/base/ime",
-    "//ui/base/user_activity",
     "//ui/chromeos",
     "//ui/chromeos/events",
     "//ui/chromeos/resources",
@@ -1458,13 +1456,13 @@
     ":ash_shell_lib",
     ":test_support",
     "//ash/components/quick_launch:lib",
-    "//ash/components/quick_launch:manifest",
+    "//ash/components/quick_launch/public/cpp:manifest",
     "//ash/components/quick_launch/public/mojom",
     "//ash/components/shortcut_viewer:lib",
-    "//ash/components/shortcut_viewer:manifest",
+    "//ash/components/shortcut_viewer/public/cpp:manifest",
     "//ash/components/shortcut_viewer/public/mojom",
     "//ash/components/tap_visualizer:lib",
-    "//ash/components/tap_visualizer:manifest",
+    "//ash/components/tap_visualizer/public/cpp:manifest",
     "//ash/components/tap_visualizer/public/mojom",
     "//ash/public/cpp",
     "//ash/public/cpp:manifest",
@@ -1842,8 +1840,7 @@
     "//ash/assistant/util",
     "//ash/components/fast_ink",
     "//ash/components/fast_ink:unit_tests",
-    "//ash/components/quick_launch:manifest",
-    "//ash/components/quick_launch/public/mojom:constants",
+    "//ash/components/quick_launch/public/cpp:manifest",
     "//ash/components/quick_launch/public/mojom:constants",
     "//ash/components/shortcut_viewer:unit_tests",
     "//ash/components/tap_visualizer:unit_tests",
@@ -1904,7 +1901,6 @@
     "//ui/base",
     "//ui/base:test_support",
     "//ui/base/ime",
-    "//ui/base/user_activity",
     "//ui/chromeos",
     "//ui/chromeos/events",
     "//ui/compositor",
diff --git a/ash/DEPS b/ash/DEPS
index 69a3379..630c52c 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -102,7 +102,6 @@
 
 specific_include_rules = {
   "app_launch_unittest.cc": [
-    "+ash/components/quick_launch/manifest.h",
     "+ash/components/quick_launch/public",
   ],
   "ash_service\.*": [
diff --git a/ash/OWNERS b/ash/OWNERS
index 220ab28c..493c2afe 100644
--- a/ash/OWNERS
+++ b/ash/OWNERS
@@ -13,7 +13,4 @@
 # Translation artifacts:
 per-file *.xtb=file://tools/translation/TRANSLATION_OWNERS
 
-per-file manifest.json=set noparent
-per-file manifest.json=file://ipc/SECURITY_OWNERS
-
 # COMPONENT: UI>Shell
diff --git a/ash/app_launch_unittest.cc b/ash/app_launch_unittest.cc
index cad40f3..9ded97f7 100644
--- a/ash/app_launch_unittest.cc
+++ b/ash/app_launch_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 "ash/components/quick_launch/manifest.h"
+#include "ash/components/quick_launch/public/cpp/manifest.h"
 #include "ash/components/quick_launch/public/mojom/constants.mojom.h"
 #include "ash/public/cpp/manifest.h"
 #include "ash/public/cpp/test_manifest.h"
@@ -35,7 +35,7 @@
       : test_service_manager_(
             {service_manager::Manifest(GetManifest())
                  .Amend(GetManifestOverlayForTesting()),
-             quick_launch_app::GetManifest(),
+             quick_launch::GetManifest(),
              service_manager::ManifestBuilder()
                  .WithServiceName(kTestServiceName)
                  .RequireCapability(mojom::kServiceName, "")
diff --git a/ash/app_list/BUILD.gn b/ash/app_list/BUILD.gn
index f9d2f25..b67f8c0 100644
--- a/ash/app_list/BUILD.gn
+++ b/ash/app_list/BUILD.gn
@@ -40,6 +40,10 @@
     "views/apps_grid_view.cc",
     "views/apps_grid_view.h",
     "views/apps_grid_view_folder_delegate.h",
+    "views/assistant/assistant_main_view.cc",
+    "views/assistant/assistant_main_view.h",
+    "views/assistant/assistant_page_view.cc",
+    "views/assistant/assistant_page_view.h",
     "views/contents_view.cc",
     "views/contents_view.h",
     "views/expand_arrow_view.cc",
@@ -95,7 +99,10 @@
   deps = [
     "//ash/app_list/resources",
     "//ash/app_menu",
+    "//ash/assistant/model",
+    "//ash/assistant/ui",
     "//ash/assistant/ui:constants",
+    "//ash/assistant/util",
     "//ash/public/cpp/app_list/vector_icons",
     "//ash/public/cpp/vector_icons",
     "//base",
diff --git a/ash/app_list/DEPS b/ash/app_list/DEPS
index 37fa9b8..30c5d31a 100644
--- a/ash/app_list/DEPS
+++ b/ash/app_list/DEPS
@@ -1,4 +1,7 @@
 include_rules = [
+  "+ash/assistant/model",
+  "+ash/assistant/ui",
+  "+ash/assistant/util",
   "+components/keyed_service/core",
   "+components/sync",
   "+mojo/public/cpp",
diff --git a/ash/app_list/app_list_controller_impl.cc b/ash/app_list/app_list_controller_impl.cc
index 588e617..72bb375 100644
--- a/ash/app_list/app_list_controller_impl.cc
+++ b/ash/app_list/app_list_controller_impl.cc
@@ -18,6 +18,8 @@
 #include "ash/app_list/views/search_box_view.h"
 #include "ash/assistant/assistant_controller.h"
 #include "ash/assistant/assistant_ui_controller.h"
+#include "ash/assistant/ui/assistant_view_delegate.h"
+#include "ash/assistant/util/deep_link_util.h"
 #include "ash/public/cpp/app_list/app_list_features.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/session_controller.h"
@@ -33,6 +35,7 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/metrics/user_metrics.h"
+#include "base/strings/utf_string_conversions.h"
 #include "extensions/common/constants.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/display/manager/display_manager.h"
@@ -48,6 +51,15 @@
       ->IsTabletModeWindowManagerEnabled();
 }
 
+bool IsAssistantEnabled() {
+  if (!chromeos::switches::IsAssistantEnabled())
+    return false;
+
+  auto* controller = Shell::Get()->voice_interaction_controller();
+  return controller->settings_enabled().value_or(false) &&
+         controller->allowed_state() == mojom::AssistantAllowedState::ALLOWED;
+}
+
 }  // namespace
 
 AppListControllerImpl::AppListControllerImpl()
@@ -661,6 +673,10 @@
   return ash::SHELF_ACTION_APP_LIST_SHOWN;
 }
 
+bool AppListControllerImpl::IsShowingEmbeddedAssistantUI() const {
+  return presenter_.IsShowingEmbeddedAssistantUI();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // Methods of |client_|:
 
@@ -705,8 +721,17 @@
     }
   }
 
-  if (client_)
-    client_->OpenSearchResult(result_id, event_flags);
+  if (presenter_.IsVisible() && result->is_omnibox_search() &&
+      IsAssistantEnabled() &&
+      app_list_features::IsEmbeddedAssistantUIEnabled()) {
+    presenter_.ShowEmbeddedAssistantUI(/*show=*/true);
+    Shell::Get()->assistant_controller()->OpenUrl(
+        ash::assistant::util::CreateAssistantQueryDeepLink(
+            base::UTF16ToUTF8(result->title())));
+  } else {
+    if (client_)
+      client_->OpenSearchResult(result_id, event_flags);
+  }
 
   if (IsTabletMode() && presenter_.IsVisible())
     presenter_.GetView()->CloseOpenedPage();
@@ -840,6 +865,10 @@
     client_->GetNavigableContentsFactory(std::move(request));
 }
 
+ash::AssistantViewDelegate* AppListControllerImpl::GetAssistantViewDelegate() {
+  return Shell::Get()->assistant_controller()->view_delegate();
+}
+
 void AppListControllerImpl::AddObserver(AppListControllerObserver* observer) {
   observers_.AddObserver(observer);
 }
@@ -953,13 +982,7 @@
 }
 
 void AppListControllerImpl::UpdateAssistantVisibility() {
-  if (!chromeos::switches::IsAssistantEnabled())
-    return;
-
-  auto* controller = Shell::Get()->voice_interaction_controller();
-  GetSearchModel()->search_box()->SetShowAssistantButton(
-      controller->settings_enabled().value_or(false) &&
-      controller->allowed_state() == mojom::AssistantAllowedState::ALLOWED);
+  GetSearchModel()->search_box()->SetShowAssistantButton(IsAssistantEnabled());
 }
 
 int64_t AppListControllerImpl::GetDisplayIdToShowAppListOn() {
diff --git a/ash/app_list/app_list_controller_impl.h b/ash/app_list/app_list_controller_impl.h
index 4b4a4432..d7f6d17 100644
--- a/ash/app_list/app_list_controller_impl.h
+++ b/ash/app_list/app_list_controller_impl.h
@@ -186,6 +186,7 @@
   bool CanProcessEventsOnApplistViews() override;
   void GetNavigableContentsFactory(
       content::mojom::NavigableContentsFactoryRequest request) override;
+  ash::AssistantViewDelegate* GetAssistantViewDelegate() override;
 
   void AddObserver(AppListControllerObserver* observer);
   void RemoveObserver(AppListControllerObserver* obsever);
@@ -241,6 +242,9 @@
       app_list::AppListShowSource show_source,
       base::TimeTicks event_time_stamp);
 
+  // Returns current visibility of the Assistant page.
+  bool IsShowingEmbeddedAssistantUI() const;
+
  private:
   syncer::StringOrdinal GetOemFolderPos();
   std::unique_ptr<app_list::AppListItem> CreateAppListItem(
diff --git a/ash/app_list/app_list_presenter_delegate_impl.cc b/ash/app_list/app_list_presenter_delegate_impl.cc
index 36761d5..6e1a9c4 100644
--- a/ash/app_list/app_list_presenter_delegate_impl.cc
+++ b/ash/app_list/app_list_presenter_delegate_impl.cc
@@ -89,7 +89,6 @@
   params.initial_apps_page = current_apps_page;
   params.is_tablet_mode = is_tablet_mode;
   params.is_side_shelf = IsSideShelf(root_window);
-
   view->Initialize(params);
 
   SnapAppListBoundsToDisplayEdge();
diff --git a/ash/app_list/app_list_util.cc b/ash/app_list/app_list_util.cc
index c8e2401..89d6e4f 100644
--- a/ash/app_list/app_list_util.cc
+++ b/ash/app_list/app_list_util.cc
@@ -43,17 +43,12 @@
          event.key_code() == ui::VKEY_LEFT || event.key_code() == ui::VKEY_UP;
 }
 
-bool ProcessLeftRightKeyTraversalForTextfield(views::Textfield* textfield,
-                                              const ui::KeyEvent& key_event) {
+bool LeftRightKeyEventShouldExitText(views::Textfield* textfield,
+                                     const ui::KeyEvent& key_event) {
   DCHECK(IsUnhandledLeftRightKeyEvent(key_event));
 
-  const bool move_focus_reverse = base::i18n::IsRTL()
-                                      ? key_event.key_code() == ui::VKEY_RIGHT
-                                      : key_event.key_code() == ui::VKEY_LEFT;
-  if (textfield->text().empty()) {
-    textfield->GetFocusManager()->AdvanceFocus(move_focus_reverse);
+  if (textfield->text().empty())
     return true;
-  }
 
   if (textfield->HasSelection())
     return false;
@@ -79,6 +74,20 @@
     return false;
   }
 
+  return true;
+}
+
+bool ProcessLeftRightKeyTraversalForTextfield(views::Textfield* textfield,
+                                              const ui::KeyEvent& key_event) {
+  DCHECK(IsUnhandledLeftRightKeyEvent(key_event));
+
+  if (!LeftRightKeyEventShouldExitText(textfield, key_event))
+    return false;
+
+  const bool move_focus_reverse = base::i18n::IsRTL()
+                                      ? key_event.key_code() == ui::VKEY_RIGHT
+                                      : key_event.key_code() == ui::VKEY_LEFT;
+
   // Move focus outside the textfield.
   textfield->GetFocusManager()->AdvanceFocus(move_focus_reverse);
   return true;
diff --git a/ash/app_list/app_list_util.h b/ash/app_list/app_list_util.h
index dc951058..857f8d19 100644
--- a/ash/app_list/app_list_util.h
+++ b/ash/app_list/app_list_util.h
@@ -26,7 +26,14 @@
 // (unmodified by ctrl, shift, or alt)
 APP_LIST_EXPORT bool IsUnhandledArrowKeyEvent(const ui::KeyEvent& event);
 
-// Processes left/right key traversal for the given Textfield. Returns true
+// Returns true if the arrow key event should move focus away from the
+// |textfield|. This is usually when the insertion point would move away from
+// text.
+APP_LIST_EXPORT bool LeftRightKeyEventShouldExitText(
+    views::Textfield* textfield,
+    const ui::KeyEvent& key_event);
+
+// Processes left/right key traversal for the given |textfield|. Returns true
 // if focus is moved.
 APP_LIST_EXPORT bool ProcessLeftRightKeyTraversalForTextfield(
     views::Textfield* textfield,
diff --git a/ash/app_list/app_list_view_delegate.h b/ash/app_list/app_list_view_delegate.h
index c0d8ffb..705a012 100644
--- a/ash/app_list/app_list_view_delegate.h
+++ b/ash/app_list/app_list_view_delegate.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "ash/assistant/ui/assistant_view_delegate.h"
 #include "ash/public/cpp/ash_public_export.h"
 #include "ash/public/interfaces/menu.mojom.h"
 #include "base/callback_forward.h"
@@ -133,6 +134,9 @@
   // the app list UI.
   virtual void GetNavigableContentsFactory(
       content::mojom::NavigableContentsFactoryRequest request) = 0;
+
+  // Returns the AssistantViewDelegate.
+  virtual ash::AssistantViewDelegate* GetAssistantViewDelegate() = 0;
 };
 
 }  // namespace app_list
diff --git a/ash/app_list/presenter/app_list_presenter_impl.cc b/ash/app_list/presenter/app_list_presenter_impl.cc
index dfc5c1f6..4362236 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.cc
+++ b/ash/app_list/presenter/app_list_presenter_impl.cc
@@ -270,6 +270,21 @@
               : base::NullCallback());
 }
 
+void AppListPresenterImpl::ShowEmbeddedAssistantUI(bool show) {
+  if (view_)
+    view_->app_list_main_view()->contents_view()->ShowEmbeddedAssistantUI(show);
+}
+
+bool AppListPresenterImpl::IsShowingEmbeddedAssistantUI() const {
+  if (view_) {
+    return view_->app_list_main_view()
+        ->contents_view()
+        ->IsShowingEmbeddedAssistantUI();
+  }
+
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // AppListPresenterImpl, private:
 
diff --git a/ash/app_list/presenter/app_list_presenter_impl.h b/ash/app_list/presenter/app_list_presenter_impl.h
index 0fa947d..6b86060 100644
--- a/ash/app_list/presenter/app_list_presenter_impl.h
+++ b/ash/app_list/presenter/app_list_presenter_impl.h
@@ -105,6 +105,13 @@
   // Schedules animation for app list when overview mode starts or ends.
   void ScheduleOverviewModeAnimation(bool start, bool animate);
 
+  // Shows or hides the Assistant page.
+  // |show| is true to show and false to hide.
+  void ShowEmbeddedAssistantUI(bool show);
+
+  // Returns current visibility of the Assistant page.
+  bool IsShowingEmbeddedAssistantUI() const;
+
  private:
   // Sets the app list view and attempts to show it.
   void SetView(AppListView* view);
diff --git a/ash/app_list/test/app_list_test_view_delegate.cc b/ash/app_list/test/app_list_test_view_delegate.cc
index 06949e0..a27f88a9 100644
--- a/ash/app_list/test/app_list_test_view_delegate.cc
+++ b/ash/app_list/test/app_list_test_view_delegate.cc
@@ -115,6 +115,11 @@
   std::move(callback).Run(ash::menu_utils::GetMojoMenuItemsFromModel(menu));
 }
 
+ash::AssistantViewDelegate*
+AppListTestViewDelegate::GetAssistantViewDelegate() {
+  return nullptr;
+}
+
 bool AppListTestViewDelegate::IsCommandIdChecked(int command_id) const {
   return true;
 }
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 173b9c64..3b2dcfeb 100644
--- a/ash/app_list/test/app_list_test_view_delegate.h
+++ b/ash/app_list/test/app_list_test_view_delegate.h
@@ -89,6 +89,7 @@
   bool CanProcessEventsOnApplistViews() override;
   void GetNavigableContentsFactory(
       content::mojom::NavigableContentsFactoryRequest request) override;
+  ash::AssistantViewDelegate* GetAssistantViewDelegate() override;
 
   // Do a bulk replacement of the items in the model.
   void ReplaceTestModel(int item_count);
diff --git a/ash/app_list/views/app_list_main_view.h b/ash/app_list/views/app_list_main_view.h
index 731744dd..3b0ab11 100644
--- a/ash/app_list/views/app_list_main_view.h
+++ b/ash/app_list/views/app_list_main_view.h
@@ -102,6 +102,7 @@
 
   // Created by AppListView. Owned by views hierarchy.
   SearchBoxView* search_box_view_;
+
   ContentsView* contents_view_;       // Owned by views hierarchy.
   AppListView* const app_list_view_;  // Owned by views hierarchy.
 
diff --git a/ash/app_list/views/app_list_view.cc b/ash/app_list/views/app_list_view.cc
index 6d7b026..274071cd 100644
--- a/ash/app_list/views/app_list_view.cc
+++ b/ash/app_list/views/app_list_view.cc
@@ -1487,6 +1487,10 @@
   if (event->handled())
     return;
 
+  // Allow text input inside the Assistant page.
+  if (app_list_main_view()->contents_view()->IsShowingEmbeddedAssistantUI())
+    return;
+
   views::Textfield* search_box = search_box_view_->search_box();
   const bool is_search_box_focused = search_box->HasFocus();
   const bool is_folder_header_view_focused = GetAppsContainerView()
diff --git a/ash/app_list/views/app_list_view_unittest.cc b/ash/app_list/views/app_list_view_unittest.cc
index 685c62e..043f7ab 100644
--- a/ash/app_list/views/app_list_view_unittest.cc
+++ b/ash/app_list/views/app_list_view_unittest.cc
@@ -801,7 +801,10 @@
       contents_view()
           ->search_result_tile_item_list_view_for_test()
           ->tile_views_for_test();
-  forward_view_list.push_back(tile_views[0]);
+  // We skip the first view when coming from the search box. This is because
+  // the first view is initially highlighted, and would already be activated
+  // upon pressing enter. Hence, we skip adding the tile view to the expected
+  // view list.
   forward_view_list.push_back(contents_view()
                                   ->search_result_answer_card_view_for_test()
                                   ->GetAnswerCardResultViewForTest());
diff --git a/ash/app_list/views/apps_grid_view.cc b/ash/app_list/views/apps_grid_view.cc
index 5fd63ab..3ccebf3 100644
--- a/ash/app_list/views/apps_grid_view.cc
+++ b/ash/app_list/views/apps_grid_view.cc
@@ -36,6 +36,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/utf_string_conversions.h"
 #include "build/build_config.h"
+#include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -487,6 +488,10 @@
     return;
 
   drag_view_ = view;
+
+  // Dragged view should have focus. This also fixed the issue
+  // https://crbug.com/834682.
+  drag_view_->RequestFocus();
   drag_view_init_index_ = GetIndexOfView(drag_view_);
   drag_view_offset_ = location;
   drag_start_page_ = pagination_model_.selected_page();
@@ -502,15 +507,16 @@
 void AppsGridView::TryStartDragAndDropHostDrag(
     Pointer pointer,
     const gfx::Point& grid_location) {
-  drag_pointer_ = pointer;
-  // Move the view to the front so that it appears on top of other views.
-  ReorderChildView(drag_view_, -1);
-  bounds_animator_.StopAnimatingView(drag_view_);
   // Stopping the animation may have invalidated our drag view due to the
   // view hierarchy changing.
   if (!drag_view_)
     return;
 
+  drag_pointer_ = pointer;
+  // Move the view to the front so that it appears on top of other views.
+  ReorderChildView(drag_view_, -1);
+  bounds_animator_.StopAnimatingView(drag_view_);
+
   if (!dragging_for_reparent_item_)
     StartDragAndDropHostDrag(grid_location);
 }
@@ -645,10 +651,13 @@
       UpdateDropTargetRegion();
       if (drop_target_region_ == ON_ITEM && DraggedItemCanEnterFolder() &&
           DropTargetIsValidFolder()) {
+        MaybeCreateFolderDroppingAccessibilityEvent();
         MoveItemToFolder(drag_view_, drop_target_);
         folder_item_view =
             GetViewDisplayedAtSlotOnCurrentPage(drop_target_.slot);
       } else if (IsValidReorderTargetIndex(drop_target_)) {
+        // Ensure reorder event has already been announced by the end of drag.
+        MaybeCreateReorderAccessibilityEvent();
         MoveItemInModel(drag_view_, drop_target_);
       }
     }
@@ -753,6 +762,10 @@
                           false /* is_in_folder */);
   AddChildView(view);
   drag_view_ = view;
+
+  // Dragged view should have focus. This also fixed the issue
+  // https://crbug.com/834682.
+  drag_view_->RequestFocus();
   drag_view_->SetBoundsRect(drag_view_rect);
   drag_view_->SetDragUIState();  // Hide the title of the drag_view_.
 
@@ -795,6 +808,8 @@
 }
 
 void AppsGridView::ClearDragState() {
+  last_folder_dropping_a11y_event_location_ = GridIndex();
+  last_reorder_a11y_event_location_ = GridIndex();
   drop_target_region_ = NO_TARGET;
   drag_pointer_ = NONE;
   drop_target_ = GridIndex();
@@ -854,6 +869,11 @@
   return "AppsGridView";
 }
 
+void AppsGridView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
+  node_data->role = ax::mojom::Role::kAlert;
+  node_data->SetName(accessible_name_);
+}
+
 void AppsGridView::Layout() {
   if (bounds_animator_.IsAnimating())
     bounds_animator_.Cancel();
@@ -1447,6 +1467,7 @@
 
 void AppsGridView::OnReorderTimer() {
   reorder_placeholder_ = drop_target_;
+  MaybeCreateReorderAccessibilityEvent();
   AnimateToIdealBounds();
 }
 
@@ -1463,10 +1484,16 @@
     // Do not observe any data change since it is going to be hidden.
     item_list_->RemoveObserver(this);
     item_list_ = nullptr;
+
+    // Announce the folder close for dragging to the outside of the folder.
+    accessible_name_ = l10n_util::GetStringUTF16(
+        IDS_APP_LIST_FOLDER_CLOSE_FOLDER_ACCESSIBILE_NAME);
+    NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
   }
 }
 
 void AppsGridView::OnFolderDroppingTimer() {
+  MaybeCreateFolderDroppingAccessibilityEvent();
   SetAsFolderDroppingTarget(drop_target_, true);
 }
 
@@ -1643,6 +1670,8 @@
     if (drop_target_region_ == ON_ITEM && DropTargetIsValidFolder() &&
         DraggedItemCanEnterFolder()) {
       cancel_reparent = !ReparentItemToAnotherFolder(drag_view_, drop_target_);
+      // Announce folder dropping event before end of drag of reparented item.
+      MaybeCreateFolderDroppingAccessibilityEvent();
       if (!cancel_reparent) {
         folder_item_view =
             GetViewDisplayedAtSlotOnCurrentPage(drop_target_.slot);
@@ -1650,6 +1679,9 @@
     } else if (drop_target_region_ != NO_TARGET &&
                IsValidReorderTargetIndex(drop_target_)) {
       ReparentItemForReorder(drag_view_, drop_target_);
+      // Announce accessibility event before the end of drag for reparented
+      // item.
+      MaybeCreateReorderAccessibilityEvent();
     } else {
       NOTREACHED();
     }
@@ -2712,4 +2744,60 @@
   animation_view->TransformView();
 }
 
+void AppsGridView::MaybeCreateFolderDroppingAccessibilityEvent() {
+  if (drop_target_region_ != ON_ITEM || !DropTargetIsValidFolder() ||
+      IsFolderItem(drag_view_->item()) || folder_delegate_ ||
+      drop_target_ == last_folder_dropping_a11y_event_location_) {
+    return;
+  }
+
+  last_folder_dropping_a11y_event_location_ = drop_target_;
+  last_reorder_a11y_event_location_ = GridIndex();
+
+  AppListItemView* drop_view =
+      GetViewDisplayedAtSlotOnCurrentPage(drop_target_.slot);
+  DCHECK(drop_view);
+
+  // Set a11y name to announce possible move to folder or creation of folder.
+  accessible_name_ = l10n_util::GetStringFUTF16(
+      IsFolderItem(drop_view->item())
+          ? IDS_APP_LIST_APP_DRAG_MOVE_TO_FOLDER_ACCESSIBILE_NAME
+          : IDS_APP_LIST_APP_DRAG_CREATE_FOLDER_ACCESSIBILE_NAME,
+      drag_view_->title()->text(), drop_view->title()->text());
+  NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
+}
+
+void AppsGridView::MaybeCreateReorderAccessibilityEvent() {
+  if (drop_target_region_ == ON_ITEM && !IsFolderItem(drag_view_->item()))
+    return;
+
+  // If app was dragged out of folder, no need to announce location for the
+  // now closed folder.
+  if (drag_out_of_folder_container_)
+    return;
+
+  // If drop_target is not set or was already reset, then return.
+  if (drop_target_ == GridIndex())
+    return;
+
+  // Don't create a11y event if |drop_target| has not changed.
+  if (last_reorder_a11y_event_location_ == drop_target_)
+    return;
+
+  last_folder_dropping_a11y_event_location_ = GridIndex();
+  last_reorder_a11y_event_location_ = drop_target_;
+
+  const int row_number =
+      ((drop_target_.slot - (drop_target_.slot % cols_)) / cols_) + 1;
+  const int col_number = (drop_target_.slot % cols_) + 1;
+
+  // Set accessible name to announce drop target location by row and column.
+  accessible_name_ = l10n_util::GetStringFUTF16(
+      IDS_APP_LIST_APP_DRAG_LOCATION_ACCESSIBILE_NAME,
+      base::NumberToString16(drop_target_.page + 1),
+      base::NumberToString16(row_number), base::NumberToString16(col_number));
+
+  NotifyAccessibilityEvent(ax::mojom::Event::kAlert, true);
+}
+
 }  // namespace app_list
diff --git a/ash/app_list/views/apps_grid_view.h b/ash/app_list/views/apps_grid_view.h
index fc24ffe..cb385d68 100644
--- a/ash/app_list/views/apps_grid_view.h
+++ b/ash/app_list/views/apps_grid_view.h
@@ -183,6 +183,7 @@
   bool CanDrop(const OSExchangeData& data) override;
   int OnDragUpdated(const ui::DropTargetEvent& event) override;
   const char* GetClassName() const override;
+  void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
 
   // Updates the visibility of app list items according to |app_list_state| and
   // |is_in_drag|.
@@ -590,6 +591,14 @@
                                     AppListItem* drag_item,
                                     const gfx::Rect& source_bounds);
 
+  // During an app drag, creates an a11y event to verbalize dropping onto a
+  // folder or creating a folder with two apps.
+  void MaybeCreateFolderDroppingAccessibilityEvent();
+
+  // During an app drag, creates an a11y event to verbalize drop target
+  // location.
+  void MaybeCreateReorderAccessibilityEvent();
+
   AppListModel* model_ = nullptr;         // Owned by AppListView.
   AppListItemList* item_list_ = nullptr;  // Not owned.
 
@@ -708,6 +717,15 @@
   int horizontal_tile_padding_ = 0;
   int vertical_tile_padding_ = 0;
 
+  // Name used for app dragging accessibility events.
+  base::string16 accessible_name_;
+
+  // The drop location of the most recent reorder related accessibility event.
+  GridIndex last_reorder_a11y_event_location_;
+
+  // The location of the most recent foldering drag related accessibility event.
+  GridIndex last_folder_dropping_a11y_event_location_;
+
   DISALLOW_COPY_AND_ASSIGN(AppsGridView);
 };
 
diff --git a/ash/app_list/views/apps_grid_view_unittest.cc b/ash/app_list/views/apps_grid_view_unittest.cc
index 2cc8e0bb..84fbbf5 100644
--- a/ash/app_list/views/apps_grid_view_unittest.cc
+++ b/ash/app_list/views/apps_grid_view_unittest.cc
@@ -47,6 +47,7 @@
 #include "ui/events/event_utils.h"
 #include "ui/keyboard/keyboard_controller.h"
 #include "ui/views/controls/label.h"
+#include "ui/views/controls/textfield/textfield.h"
 #include "ui/views/test/views_test_base.h"
 
 namespace app_list {
@@ -1020,6 +1021,28 @@
             model_->GetModelContent());
 }
 
+// Test focus change before and after dragging an item. (See
+// https://crbug.com/834682)
+TEST_F(AppsGridViewTest, FocusOfDraggedView) {
+  model_->PopulateApps(1);
+  contents_view_->GetAppsContainerView()->Layout();
+  auto* search_box = contents_view_->GetSearchBoxView()->search_box();
+  auto* item_view = apps_grid_view_->view_model()->view_at(0);
+  EXPECT_TRUE(search_box->HasFocus());
+  EXPECT_FALSE(item_view->HasFocus());
+
+  // Dragging the item towards its right.
+  const gfx::Point from = GetItemRectOnCurrentPageAt(0, 0).CenterPoint();
+  const gfx::Point to = GetItemRectOnCurrentPageAt(0, 1).CenterPoint();
+  SimulateDrag(AppsGridView::MOUSE, from, to);
+  EXPECT_FALSE(search_box->HasFocus());
+  EXPECT_TRUE(item_view->HasFocus());
+
+  apps_grid_view_->EndDrag(false);
+  EXPECT_FALSE(search_box->HasFocus());
+  EXPECT_TRUE(item_view->HasFocus());
+}
+
 // Test various dragging behaviors only allowed when apps grid gap (part of
 // home launcher feature) is enabled.
 class AppsGridGapTest : public AppsGridViewTest {
diff --git a/ash/app_list/views/assistant/assistant_main_view.cc b/ash/app_list/views/assistant/assistant_main_view.cc
new file mode 100644
index 0000000..b2e93e3
--- /dev/null
+++ b/ash/app_list/views/assistant/assistant_main_view.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 "ash/app_list/views/assistant/assistant_main_view.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "ash/assistant/ui/assistant_ui_constants.h"
+#include "ash/assistant/ui/assistant_view_delegate.h"
+#include "ash/assistant/ui/dialog_plate/dialog_plate.h"
+#include "ash/assistant/ui/main_stage/assistant_main_stage.h"
+#include "ash/assistant/util/assistant_util.h"
+#include "base/time/time.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace app_list {
+
+AssistantMainView::AssistantMainView(ash::AssistantViewDelegate* delegate)
+    : delegate_(delegate) {
+  InitLayout();
+
+  for (ash::DialogPlateObserver* observer :
+       delegate_->GetDialogPlateObservers()) {
+    dialog_plate_->AddObserver(observer);
+  }
+}
+
+AssistantMainView::~AssistantMainView() {
+  for (ash::DialogPlateObserver* observer :
+       delegate_->GetDialogPlateObservers()) {
+    dialog_plate_->RemoveObserver(observer);
+  }
+}
+
+const char* AssistantMainView::GetClassName() const {
+  return "AssistantMainView";
+}
+
+gfx::Size AssistantMainView::CalculatePreferredSize() const {
+  return gfx::Size(ash::kPreferredWidthDip,
+                   GetHeightForWidth(ash::kPreferredWidthDip));
+}
+
+void AssistantMainView::ChildPreferredSizeChanged(views::View* child) {
+  PreferredSizeChanged();
+
+  // Even though the preferred size for |main_stage_| may change, its bounds
+  // may not actually change due to height restrictions imposed by its parent.
+  // For this reason, we need to explicitly trigger a layout pass so that the
+  // children of |main_stage_| are properly updated.
+  if (child == main_stage_) {
+    Layout();
+    SchedulePaint();
+  }
+}
+
+void AssistantMainView::ChildVisibilityChanged(views::View* child) {
+  PreferredSizeChanged();
+}
+
+views::View* AssistantMainView::FindFirstFocusableView() {
+  // In those instances in which we want to override views::FocusSearch
+  // behavior, DialogPlate will identify the first focusable view.
+  return dialog_plate_->FindFirstFocusableView();
+}
+
+void AssistantMainView::InitLayout() {
+  views::BoxLayout* layout_manager =
+      SetLayoutManager(std::make_unique<views::BoxLayout>(
+          views::BoxLayout::Orientation::kVertical));
+
+  // Main stage.
+  main_stage_ = new ash::AssistantMainStage(delegate_);
+  AddChildView(main_stage_);
+
+  layout_manager->SetFlexForView(main_stage_, 1);
+
+  // Dialog plate.
+  dialog_plate_ = new ash::DialogPlate(delegate_);
+
+  // The dialog plate will be animated on its own layer.
+  dialog_plate_->SetPaintToLayer();
+  dialog_plate_->layer()->SetFillsBoundsOpaquely(false);
+
+  AddChildView(dialog_plate_);
+}
+
+void AssistantMainView::RequestFocus() {
+  dialog_plate_->RequestFocus();
+}
+
+}  // namespace app_list
diff --git a/ash/app_list/views/assistant/assistant_main_view.h b/ash/app_list/views/assistant/assistant_main_view.h
new file mode 100644
index 0000000..654788b
--- /dev/null
+++ b/ash/app_list/views/assistant/assistant_main_view.h
@@ -0,0 +1,48 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_APP_LIST_VIEWS_ASSISTANT_ASSISTANT_MAIN_VIEW_H_
+#define ASH_APP_LIST_VIEWS_ASSISTANT_ASSISTANT_MAIN_VIEW_H_
+
+#include "ash/app_list/app_list_export.h"
+#include "base/macros.h"
+#include "ui/views/view.h"
+
+namespace ash {
+class AssistantMainStage;
+class AssistantViewDelegate;
+class DialogPlate;
+}  // namespace ash
+
+namespace app_list {
+
+class APP_LIST_EXPORT AssistantMainView : public views::View {
+ public:
+  explicit AssistantMainView(ash::AssistantViewDelegate* delegate);
+  ~AssistantMainView() override;
+
+  // views::View:
+  const char* GetClassName() const override;
+  gfx::Size CalculatePreferredSize() const override;
+  void ChildPreferredSizeChanged(views::View* child) override;
+  void ChildVisibilityChanged(views::View* child) override;
+  void RequestFocus() override;
+
+  // Returns the first focusable view or nullptr to defer to views::FocusSearch.
+  views::View* FindFirstFocusableView();
+
+ private:
+  void InitLayout();
+
+  ash::AssistantViewDelegate* const delegate_;
+
+  ash::DialogPlate* dialog_plate_;       // Owned by view hierarchy.
+  ash::AssistantMainStage* main_stage_;  // Owned by view hierarchy.
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantMainView);
+};
+
+}  // namespace app_list
+
+#endif  // ASH_APP_LIST_VIEWS_ASSISTANT_ASSISTANT_MAIN_VIEW_H_
diff --git a/ash/app_list/views/assistant/assistant_page_view.cc b/ash/app_list/views/assistant/assistant_page_view.cc
new file mode 100644
index 0000000..27a78f7
--- /dev/null
+++ b/ash/app_list/views/assistant/assistant_page_view.cc
@@ -0,0 +1,86 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/app_list/views/assistant/assistant_page_view.h"
+
+#include <memory>
+#include <utility>
+
+#include "ash/app_list/app_list_view_delegate.h"
+#include "ash/app_list/views/assistant/assistant_main_view.h"
+#include "ash/app_list/views/contents_view.h"
+#include "ui/chromeos/search_box/search_box_constants.h"
+#include "ui/views/background.h"
+#include "ui/views/focus/focus_manager.h"
+#include "ui/views/layout/fill_layout.h"
+
+namespace app_list {
+
+namespace {
+
+constexpr int kHeight = 440;
+constexpr int kWidth = 640;
+
+}  // namespace
+
+AssistantPageView::AssistantPageView(
+    ash::AssistantViewDelegate* assistant_view_delegate) {
+  assistant_main_view_ = new AssistantMainView(assistant_view_delegate);
+  AddChildView(assistant_main_view_);
+  InitLayout();
+}
+
+AssistantPageView::~AssistantPageView() = default;
+
+void AssistantPageView::InitLayout() {
+  SetPaintToLayer();
+  layer()->SetFillsBoundsOpaquely(false);
+
+  SetBackground(views::CreateBackgroundFromPainter(
+      views::Painter::CreateSolidRoundRectPainter(
+          SK_ColorWHITE, search_box::kSearchBoxBorderCornerRadius)));
+
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+}
+
+const char* AssistantPageView::GetClassName() const {
+  return "AssistantPageView";
+}
+
+gfx::Size AssistantPageView::CalculatePreferredSize() const {
+  return gfx::Size(kWidth, kHeight);
+}
+
+void AssistantPageView::RequestFocus() {
+  assistant_main_view_->RequestFocus();
+}
+
+gfx::Rect AssistantPageView::GetPageBoundsForState(
+    ash::AppListState state) const {
+  gfx::Rect onscreen_bounds;
+
+  if (state != ash::AppListState::kStateEmbeddedAssistant) {
+    // Hides this view behind the search box by using the same bounds.
+    onscreen_bounds =
+        AppListPage::contents_view()->GetSearchBoxBoundsForState(state);
+  } else {
+    onscreen_bounds = AppListPage::GetSearchBoxBounds();
+    onscreen_bounds.Offset((onscreen_bounds.width() - kWidth) / 2, 0);
+    onscreen_bounds.set_size(GetPreferredSize());
+  }
+
+  return onscreen_bounds;
+}
+
+views::View* AssistantPageView::GetFirstFocusableView() {
+  return GetFocusManager()->GetNextFocusableView(
+      this, GetWidget(), /*reverse=*/false, /*dont_loop=*/false);
+}
+
+views::View* AssistantPageView::GetLastFocusableView() {
+  return GetFocusManager()->GetNextFocusableView(
+      this, GetWidget(), /*reverse=*/true, /*dont_loop=*/false);
+}
+
+}  // namespace app_list
diff --git a/ash/app_list/views/assistant/assistant_page_view.h b/ash/app_list/views/assistant/assistant_page_view.h
new file mode 100644
index 0000000..91886e4
--- /dev/null
+++ b/ash/app_list/views/assistant/assistant_page_view.h
@@ -0,0 +1,49 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_APP_LIST_VIEWS_ASSISTANT_ASSISTANT_PAGE_VIEW_H_
+#define ASH_APP_LIST_VIEWS_ASSISTANT_ASSISTANT_PAGE_VIEW_H_
+
+#include "ash/app_list/app_list_export.h"
+#include "ash/app_list/views/app_list_page.h"
+#include "ash/public/cpp/app_list/app_list_types.h"
+#include "base/macros.h"
+
+namespace ash {
+class AssistantViewDelegate;
+}  // namespace ash
+
+namespace app_list {
+
+class AssistantMainView;
+
+// The Assistant page for the app list.
+class APP_LIST_EXPORT AssistantPageView : public AppListPage {
+ public:
+  explicit AssistantPageView(
+      ash::AssistantViewDelegate* assistant_view_delegate);
+  ~AssistantPageView() override;
+
+  void InitLayout();
+
+  // views::View:
+  const char* GetClassName() const override;
+  gfx::Size CalculatePreferredSize() const override;
+  void RequestFocus() override;
+
+  // AppListPage:
+  gfx::Rect GetPageBoundsForState(ash::AppListState state) const override;
+  views::View* GetFirstFocusableView() override;
+  views::View* GetLastFocusableView() override;
+
+ private:
+  // Owned by the views hierarchy.
+  AssistantMainView* assistant_main_view_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(AssistantPageView);
+};
+
+}  // namespace app_list
+
+#endif  // ASH_APP_LIST_VIEWS_ASSISTANT_ASSISTANT_PAGE_VIEW_H_
diff --git a/ash/app_list/views/contents_view.cc b/ash/app_list/views/contents_view.cc
index 9b27607..4883017 100644
--- a/ash/app_list/views/contents_view.cc
+++ b/ash/app_list/views/contents_view.cc
@@ -13,6 +13,7 @@
 #include "ash/app_list/views/app_list_view.h"
 #include "ash/app_list/views/apps_container_view.h"
 #include "ash/app_list/views/apps_grid_view.h"
+#include "ash/app_list/views/assistant/assistant_page_view.h"
 #include "ash/app_list/views/expand_arrow_view.h"
 #include "ash/app_list/views/horizontal_page_container.h"
 #include "ash/app_list/views/search_box_view.h"
@@ -110,6 +111,14 @@
   AddLauncherPage(search_results_page_view_,
                   ash::AppListState::kStateSearchResults);
 
+  if (app_list_features::IsEmbeddedAssistantUIEnabled()) {
+    assistant_page_view_ =
+        new AssistantPageView(view_delegate->GetAssistantViewDelegate());
+    assistant_page_view_->SetVisible(false);
+    AddLauncherPage(assistant_page_view_,
+                    ash::AppListState::kStateEmbeddedAssistant);
+  }
+
   AddLauncherPage(horizontal_page_container_, ash::AppListState::kStateApps);
 
   int initial_page_index = GetPageIndexForState(ash::AppListState::kStateStart);
@@ -155,9 +164,10 @@
   if (IsStateActive(state))
     return;
 
-  // The primary way to set the state to search results should be via
-  // |ShowSearchResults|
-  DCHECK(state != ash::AppListState::kStateSearchResults);
+  // The primary way to set the state to search or Assistant results should be
+  // via |ShowSearchResults| or |ShowEmbeddedAssistantUI|.
+  DCHECK(state != ash::AppListState::kStateSearchResults &&
+         state != ash::AppListState::kStateEmbeddedAssistant);
 
   SetActiveStateInternal(GetPageIndexForState(state), false, animate);
 }
@@ -205,12 +215,12 @@
 }
 
 void ContentsView::SetActiveStateInternal(int page_index,
-                                          bool show_search_results,
+                                          bool show_search_or_assistant_results,
                                           bool animate) {
   if (!GetPageView(page_index)->visible())
     return;
 
-  if (!show_search_results)
+  if (!show_search_or_assistant_results)
     page_before_search_ = page_index;
 
   app_list_pages_[GetActivePageIndex()]->OnWillBeHidden();
@@ -236,6 +246,8 @@
   GetAppListMainView()->model()->SetState(state);
 
   UpdateExpandArrowFocusBehavior(state);
+
+  UpdateSearchBoxVisibility(state);
 }
 
 void ContentsView::ShowSearchResults(bool show) {
@@ -254,6 +266,24 @@
   return IsStateActive(ash::AppListState::kStateSearchResults);
 }
 
+void ContentsView::ShowEmbeddedAssistantUI(bool show) {
+  int assistant_page =
+      GetPageIndexForState(ash::AppListState::kStateEmbeddedAssistant);
+  DCHECK_GE(assistant_page, 0);
+
+  // Hide or Show results.
+  GetPageView(assistant_page)->SetVisible(show);
+  if (show)
+    GetPageView(assistant_page)->RequestFocus();
+
+  SetActiveStateInternal(show ? assistant_page : page_before_search_, show,
+                         !AppListView::ShortAnimationsForTesting());
+}
+
+bool ContentsView::IsShowingEmbeddedAssistantUI() const {
+  return IsStateActive(ash::AppListState::kStateEmbeddedAssistant);
+}
+
 void ContentsView::UpdatePageBounds() {
   // The bounds calculations will potentially be mid-transition (depending on
   // the state of the PaginationModel).
@@ -271,7 +301,6 @@
 
   ash::AppListState current_state = GetStateForPageIndex(current_page);
   ash::AppListState target_state = GetStateForPageIndex(target_page);
-
   // Update app list pages.
   for (AppListPage* page : app_list_pages_) {
     gfx::Rect to_rect = page->GetPageBoundsForState(target_state);
@@ -370,6 +399,12 @@
       ax::mojom::Event::kTreeChanged);
 }
 
+void ContentsView::UpdateSearchBoxVisibility(ash::AppListState current_state) {
+  const bool show_search_box =
+      current_state != ash::AppListState::kStateEmbeddedAssistant;
+  GetSearchBoxView()->SetVisible(show_search_box);
+}
+
 PaginationModel* ContentsView::GetAppsPaginationModel() {
   return GetAppsContainerView()->apps_grid_view()->pagination_model();
 }
@@ -463,7 +498,11 @@
       GetSearchBoxView()->SetSearchBoxActive(false, ui::ET_UNKNOWN);
       ShowSearchResults(false);
       break;
-    case ash::AppListState::kStateCustomLauncherPageDeprecated:
+    case ash::AppListState::kStateEmbeddedAssistant:
+      GetSearchBoxView()->ClearSearch();
+      GetSearchBoxView()->SetSearchBoxActive(false, ui::ET_UNKNOWN);
+      ShowEmbeddedAssistantUI(false);
+      break;
     case ash::AppListState::kInvalidState:  // Falls through.
       NOTREACHED();
       break;
@@ -605,6 +644,11 @@
              target_state == ash::AppListState::kStateSearchResults));
   }
 
+  if (page == assistant_page_view_) {
+    return current_state == ash::AppListState::kStateEmbeddedAssistant ||
+           target_state == ash::AppListState::kStateEmbeddedAssistant;
+  }
+
   return false;
 }
 
diff --git a/ash/app_list/views/contents_view.h b/ash/app_list/views/contents_view.h
index 822f505..3e62cbc 100644
--- a/ash/app_list/views/contents_view.h
+++ b/ash/app_list/views/contents_view.h
@@ -33,6 +33,7 @@
 class AppListMainView;
 class AppsContainerView;
 class AppsGridView;
+class AssistantPageView;
 class ExpandArrowView;
 class HorizontalPageContainer;
 class PaginationModel;
@@ -71,6 +72,12 @@
   void ShowSearchResults(bool show);
   bool IsShowingSearchResults() const;
 
+  // Shows/hides the Assistant page. Hiding the Assistant page will
+  // cause the app list to return to the page that was displayed before
+  // ShowSearchResults(true) was invoked.
+  void ShowEmbeddedAssistantUI(bool show);
+  bool IsShowingEmbeddedAssistantUI() const;
+
   void ShowFolderContent(AppListFolderItem* folder);
 
   // Sets the active launcher page and animates the pages into place.
@@ -175,7 +182,7 @@
   // Sets the active launcher page, accounting for whether the change is for
   // search results.
   void SetActiveStateInternal(int page_index,
-                              bool show_search_results,
+                              bool show_search_or_assistant_results,
                               bool animate);
 
   // Invoked when active view is changed.
@@ -201,6 +208,9 @@
   // Updates the expand arrow's focus behavior based on the current state.
   void UpdateExpandArrowFocusBehavior(ash::AppListState current_state);
 
+  // Updates search box visibility based on the current state.
+  void UpdateSearchBoxVisibility(ash::AppListState current_state);
+
   // Adds |view| as a new page to the end of the list of launcher pages. The
   // view is inserted as a child of the ContentsView. There is no name
   // associated with the page. Returns the index of the new page.
@@ -229,6 +239,7 @@
   AppListModel* model_ = nullptr;
 
   // Sub-views of the ContentsView. All owned by the views hierarchy.
+  AssistantPageView* assistant_page_view_ = nullptr;
   HorizontalPageContainer* horizontal_page_container_ = nullptr;
   SearchResultPageView* search_results_page_view_ = nullptr;
   SearchResultAnswerCardView* search_result_answer_card_view_ = nullptr;
diff --git a/ash/app_list/views/search_box_view.cc b/ash/app_list/views/search_box_view.cc
index b5eaf8e8..b29d56f 100644
--- a/ash/app_list/views/search_box_view.cc
+++ b/ash/app_list/views/search_box_view.cc
@@ -247,21 +247,25 @@
   if (!IsUnhandledUpDownKeyEvent(*event))
     return;
 
-  // If focus is in search box view, up key moves focus to the last element of
-  // contents view if new style launcher is not enabled while it moves focus to
-  // expand arrow if the feature is enabled. Down key moves focus to the first
-  // element of contents view.
+  // Handles arrow key events from the search box while the search box is
+  // inactive. This covers both folder traversal and apps grid traversal. Search
+  // result traversal is handled in |HandleKeyEvent|
   AppListPage* page =
       contents_view_->GetPageView(contents_view_->GetActivePageIndex());
   views::View* arrow_view = contents_view_->expand_arrow_view();
-  views::View* v = event->key_code() == ui::VKEY_UP
-                       ? (arrow_view && arrow_view->IsFocusable()
-                              ? arrow_view
-                              : page->GetLastFocusableView())
-                       : page->GetFirstFocusableView();
+  views::View* next_view = nullptr;
 
-  if (v)
-    v->RequestFocus();
+  if (event->key_code() == ui::VKEY_UP) {
+    if (arrow_view && arrow_view->IsFocusable())
+      next_view = arrow_view;
+    else
+      next_view = page->GetLastFocusableView();
+  } else {
+    next_view = page->GetFirstFocusableView();
+  }
+
+  if (next_view)
+    next_view->RequestFocus();
   event->SetHandled();
 }
 
@@ -518,26 +522,6 @@
 
 bool SearchBoxView::HandleKeyEvent(views::Textfield* sender,
                                    const ui::KeyEvent& key_event) {
-  if (search_box()->HasFocus() && is_search_box_active() &&
-      !search_box()->text().empty() && ShouldProcessAutocomplete()) {
-    // If the search box has no text in it currently, autocomplete should not
-    // work.
-    last_key_pressed_ = key_event.key_code();
-    if (key_event.type() == ui::ET_KEY_PRESSED &&
-        key_event.key_code() != ui::VKEY_BACK) {
-      if (key_event.key_code() == ui::VKEY_TAB && HasAutocompleteText()) {
-        AcceptAutocompleteText();
-        return true;
-      } else if ((key_event.key_code() == ui::VKEY_UP ||
-                  key_event.key_code() == ui::VKEY_DOWN ||
-                  key_event.key_code() == ui::VKEY_LEFT ||
-                  key_event.key_code() == ui::VKEY_RIGHT) &&
-                 HasAutocompleteText()) {
-        ClearAutocompleteText();
-        return true;
-      }
-    }
-  }
   if (key_event.type() == ui::ET_KEY_PRESSED &&
       key_event.key_code() == ui::VKEY_RETURN) {
     if (!IsSearchBoxTrimmedQueryEmpty()) {
@@ -557,9 +541,85 @@
     return false;
   }
 
-  if (IsUnhandledLeftRightKeyEvent(key_event))
+  // Events occurring over an inactive search box are handled elsewhere.
+  if (!is_search_box_active())
+    return false;
+
+  // Handles autocomplete text confirmation/deletion
+  // TODO(ginko) fix logic for arrow keys in autocomplete
+  if (search_box()->HasFocus() && !search_box()->text().empty() &&
+      ShouldProcessAutocomplete()) {
+    // If the search box has no text in it currently, autocomplete should not
+    // work.
+    last_key_pressed_ = key_event.key_code();
+    if (key_event.type() == ui::ET_KEY_PRESSED &&
+        key_event.key_code() != ui::VKEY_BACK) {
+      if (key_event.key_code() == ui::VKEY_TAB && HasAutocompleteText()) {
+        AcceptAutocompleteText();
+        return true;
+      } else if ((key_event.key_code() == ui::VKEY_UP ||
+                  key_event.key_code() == ui::VKEY_DOWN ||
+                  key_event.key_code() == ui::VKEY_LEFT ||
+                  key_event.key_code() == ui::VKEY_RIGHT) &&
+                 HasAutocompleteText()) {
+        ClearAutocompleteText();
+        return true;
+      }
+    }
+  }
+
+  // Only arrow key events intended for traversal within search results should
+  // be handled from here.
+  if (!IsUnhandledArrowKeyEvent(key_event))
+    return false;
+
+  SearchResultPageView* search_page =
+      contents_view_->search_results_page_view();
+
+  // Left/Right arrow keys are handled elsewhere, unless the first result is a
+  // tile, in which case right will be handled below.
+  if (key_event.key_code() == ui::VKEY_LEFT ||
+      (key_event.key_code() == ui::VKEY_RIGHT &&
+       !search_page->IsFirstResultTile())) {
     return ProcessLeftRightKeyTraversalForTextfield(search_box(), key_event);
-  return false;
+  }
+
+  // Right arrow key should not be handled if the cursor is within text.
+  if (key_event.key_code() == ui::VKEY_RIGHT &&
+      !LeftRightKeyEventShouldExitText(search_box(), key_event)) {
+    return false;
+  }
+
+  views::View* result_view = nullptr;
+
+  // The up arrow will loop focus to the last result.
+  // The down and right arrows will be treated the same, moving focus along to
+  // the 'next' result. If a result is highlighted, we treat that result as
+  // though it already had focus.
+  if (key_event.key_code() == ui::VKEY_UP) {
+    result_view = search_page->GetLastFocusableView();
+  } else if (search_page->IsFirstResultHighlighted()) {
+    result_view = search_page->GetFirstFocusableView();
+
+    // Give the parent container a chance to handle the event. This lets the
+    // down arrow escape the tile result container.
+    if (!result_view->parent()->OnKeyPressed(key_event)) {
+      // If the parent container doesn't handle |key_event|, get the next
+      // focusable view.
+      result_view = result_view->GetFocusManager()->GetNextFocusableView(
+          result_view, result_view->GetWidget(), false, false);
+    } else {
+      // Return early if the parent container handled the event.
+      return true;
+    }
+  } else {
+    result_view = search_page->GetFirstFocusableView();
+  }
+
+  if (result_view)
+    result_view->RequestFocus();
+
+  return true;
 }
 
 bool SearchBoxView::HandleMouseEvent(views::Textfield* sender,
diff --git a/ash/app_list/views/search_result_base_view.cc b/ash/app_list/views/search_result_base_view.cc
index 0573e18..6ac992d 100644
--- a/ash/app_list/views/search_result_base_view.cc
+++ b/ash/app_list/views/search_result_base_view.cc
@@ -7,7 +7,9 @@
 
 namespace app_list {
 
-SearchResultBaseView::SearchResultBaseView() : Button(this) {}
+SearchResultBaseView::SearchResultBaseView() : Button(this) {
+  SetInstallFocusRingOnFocus(false);
+}
 
 SearchResultBaseView::~SearchResultBaseView() = default;
 
diff --git a/ash/app_list/views/search_result_page_view.cc b/ash/app_list/views/search_result_page_view.cc
index c44abbc..f5e2d12 100644
--- a/ash/app_list/views/search_result_page_view.cc
+++ b/ash/app_list/views/search_result_page_view.cc
@@ -203,6 +203,19 @@
   result_container->set_delegate(this);
 }
 
+bool SearchResultPageView::IsFirstResultTile() const {
+  // |kRecommendation| result type refers to tiles in Zero State.
+  return first_result_view_->result()->display_type() ==
+             ash::SearchResultDisplayType::kTile ||
+         first_result_view_->result()->display_type() ==
+             ash::SearchResultDisplayType::kRecommendation;
+}
+
+bool SearchResultPageView::IsFirstResultHighlighted() const {
+  DCHECK(first_result_view_);
+  return first_result_view_->background_highlighted();
+}
+
 bool SearchResultPageView::OnKeyPressed(const ui::KeyEvent& event) {
   // Let the FocusManager handle Left/Right keys.
   if (!IsUnhandledUpDownKeyEvent(event))
diff --git a/ash/app_list/views/search_result_page_view.h b/ash/app_list/views/search_result_page_view.h
index 4c1898b..b3740e4 100644
--- a/ash/app_list/views/search_result_page_view.h
+++ b/ash/app_list/views/search_result_page_view.h
@@ -34,6 +34,9 @@
     return result_container_views_;
   }
 
+  bool IsFirstResultTile() const;
+  bool IsFirstResultHighlighted() const;
+
   // Overridden from views::View:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   const char* GetClassName() const override;
diff --git a/ash/assistant/assistant_controller.cc b/ash/assistant/assistant_controller.cc
index bf103dfb..da404755 100644
--- a/ash/assistant/assistant_controller.cc
+++ b/ash/assistant/assistant_controller.cc
@@ -277,12 +277,14 @@
 
 void AssistantController::SendAssistantFeedback(
     bool assistant_debug_info_allowed,
-    const std::string& feedback_description) {
+    const std::string& feedback_description,
+    const std::string& screenshot_png) {
   chromeos::assistant::mojom::AssistantFeedbackPtr assistant_feedback =
       chromeos::assistant::mojom::AssistantFeedback::New();
   assistant_feedback->assistant_debug_info_allowed =
       assistant_debug_info_allowed;
   assistant_feedback->description = feedback_description;
+  assistant_feedback->screenshot_png = screenshot_png;
   assistant_->SendAssistantFeedback(std::move(assistant_feedback));
 }
 
diff --git a/ash/assistant/assistant_controller.h b/ash/assistant/assistant_controller.h
index 2b48988..4ffc9fb 100644
--- a/ash/assistant/assistant_controller.h
+++ b/ash/assistant/assistant_controller.h
@@ -85,7 +85,8 @@
   void OpenAssistantSettings() override;
   void StartSpeakerIdEnrollmentFlow() override;
   void SendAssistantFeedback(bool assistant_debug_info_allowed,
-                             const std::string& feedback_description) override;
+                             const std::string& feedback_description,
+                             const std::string& screenshot_png) override;
 
   // AssistantControllerObserver:
   void OnDeepLinkReceived(
diff --git a/ash/assistant/assistant_ui_controller.cc b/ash/assistant/assistant_ui_controller.cc
index 9ffac793..99686dd 100644
--- a/ash/assistant/assistant_ui_controller.cc
+++ b/ash/assistant/assistant_ui_controller.cc
@@ -4,6 +4,7 @@
 
 #include "ash/assistant/assistant_ui_controller.h"
 
+#include "ash/app_list/app_list_controller_impl.h"
 #include "ash/assistant/assistant_controller.h"
 #include "ash/assistant/assistant_interaction_controller.h"
 #include "ash/assistant/assistant_screen_context_controller.h"
@@ -317,6 +318,9 @@
     return;
   }
 
+  if (Shell::Get()->app_list_controller()->IsShowingEmbeddedAssistantUI())
+    return;
+
   // TODO(dmblack): Show a more helpful message to the user.
   if (Shell::Get()->voice_interaction_controller()->voice_interaction_state() ==
       mojom::VoiceInteractionState::NOT_READY) {
diff --git a/ash/assistant/assistant_view_delegate_impl.cc b/ash/assistant/assistant_view_delegate_impl.cc
index 6559aa62..640e797 100644
--- a/ash/assistant/assistant_view_delegate_impl.cc
+++ b/ash/assistant/assistant_view_delegate_impl.cc
@@ -103,7 +103,7 @@
   assistant_controller_->DownloadImage(url, std::move(callback));
 }
 
-wm::CursorManager* AssistantViewDelegateImpl::GetCursorManager() {
+::wm::CursorManager* AssistantViewDelegateImpl::GetCursorManager() {
   return Shell::Get()->cursor_manager();
 }
 
diff --git a/ash/assistant/assistant_view_delegate_impl.h b/ash/assistant/assistant_view_delegate_impl.h
index 60ceb27a..cab462e 100644
--- a/ash/assistant/assistant_view_delegate_impl.h
+++ b/ash/assistant/assistant_view_delegate_impl.h
@@ -47,7 +47,7 @@
   void DownloadImage(
       const GURL& url,
       mojom::AssistantImageDownloader::DownloadCallback callback) override;
-  wm::CursorManager* GetCursorManager() override;
+  ::wm::CursorManager* GetCursorManager() override;
   void GetNavigableContentsFactoryForView(
       content::mojom::NavigableContentsFactoryRequest request) override;
   aura::Window* GetRootWindowForNewWindows() override;
diff --git a/ash/assistant/ui/assistant_container_view.cc b/ash/assistant/ui/assistant_container_view.cc
index f2782a8..9ec04c9 100644
--- a/ash/assistant/ui/assistant_container_view.cc
+++ b/ash/assistant/ui/assistant_container_view.cc
@@ -29,7 +29,6 @@
 #include "ui/views/bubble/bubble_frame_view.h"
 #include "ui/views/layout/layout_manager.h"
 #include "ui/views/view.h"
-#include "ui/views/window/dialog_client_view.h"
 
 namespace ash {
 
@@ -49,22 +48,22 @@
 // paint to a higher level in the layer tree than do direct children of
 // AssistantContainerView. This allows AssistantMainView, for example, to
 // pseudo-parent overlays that draw over top of Assistant cards.
-class AssistantContainerClientView : public views::DialogClientView,
+class AssistantContainerClientView : public views::ClientView,
                                      public views::ViewObserver {
  public:
   AssistantContainerClientView(views::Widget* widget,
                                views::View* contents_view)
-      : views::DialogClientView(widget, contents_view) {}
+      : views::ClientView(widget, contents_view) {}
 
   ~AssistantContainerClientView() override = default;
 
-  // views::DialogClientView:
+  // views::ClientView:
   const char* GetClassName() const override {
     return "AssistantContainerClientView";
   }
 
   void Layout() override {
-    views::DialogClientView::Layout();
+    views::ClientView::Layout();
     for (AssistantOverlay* overlay : overlays_) {
       AssistantOverlay::LayoutParams layout_params = overlay->GetLayoutParams();
       gfx::Size preferred_size = overlay->GetPreferredSize();
diff --git a/ash/assistant/ui/assistant_view_delegate.h b/ash/assistant/ui/assistant_view_delegate.h
index f15f05c..73b7eeab0 100644
--- a/ash/assistant/ui/assistant_view_delegate.h
+++ b/ash/assistant/ui/assistant_view_delegate.h
@@ -112,7 +112,7 @@
       mojom::AssistantImageDownloader::DownloadCallback callback) = 0;
 
   // Returns the cursor_manager.
-  virtual wm::CursorManager* GetCursorManager() = 0;
+  virtual ::wm::CursorManager* GetCursorManager() = 0;
 
   // Acquires a NavigableContentsFactory from the Content Service to allow
   // Assistant to display embedded web contents.
diff --git a/ash/assistant/ui/caption_bar.cc b/ash/assistant/ui/caption_bar.cc
index c7f7229..b8a6c38 100644
--- a/ash/assistant/ui/caption_bar.cc
+++ b/ash/assistant/ui/caption_bar.cc
@@ -58,10 +58,13 @@
     case ui::VKEY_BROWSER_BACK:
       HandleButton(AssistantButtonId::kBack);
       break;
+    case ui::VKEY_ESCAPE:
+      HandleButton(AssistantButtonId::kClose);
+      break;
     case ui::VKEY_W:
-      if (accelerator.IsCtrlDown())
+      if (accelerator.IsCtrlDown()) {
         HandleButton(AssistantButtonId::kClose);
-      else {
+      } else {
         NOTREACHED();
         return false;
       }
@@ -71,7 +74,7 @@
       return false;
   }
 
-  // Don't let DialogClientView handle the accelerator.
+  // Don't let ClientView handle the accelerator.
   return true;
 }
 
@@ -127,9 +130,10 @@
                           AssistantButtonId::kClose, this);
   AddChildView(close_button);
 
-  // Add a keyboard accelerator Ctrl + W to close Assistant UI.
-  AddAccelerator(ui::Accelerator(ui::VKEY_W, ui::EF_CONTROL_DOWN));
-  AddAccelerator(ui::Accelerator(ui::VKEY_BROWSER_BACK, ui::EF_NONE));
+  // Add accelerators for keyboard shortcuts that behave like caption buttons.
+  AddAccelerator(ui::Accelerator(ui::VKEY_BROWSER_BACK, ui::EF_NONE));  // Back
+  AddAccelerator(ui::Accelerator(ui::VKEY_ESCAPE, ui::EF_NONE));        // Close
+  AddAccelerator(ui::Accelerator(ui::VKEY_W, ui::EF_CONTROL_DOWN));     // Close
 }
 
 void CaptionBar::HandleButton(AssistantButtonId id) {
diff --git a/ash/components/quick_launch/BUILD.gn b/ash/components/quick_launch/BUILD.gn
index f8f4b0f..ea314eb 100644
--- a/ash/components/quick_launch/BUILD.gn
+++ b/ash/components/quick_launch/BUILD.gn
@@ -5,7 +5,6 @@
 import("//build/config/ui.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 import("//services/service_manager/public/cpp/service_executable.gni")
-import("//services/service_manager/public/service_manifest.gni")
 import("//tools/grit/repack.gni")
 
 source_set("lib") {
@@ -43,8 +42,3 @@
     "//ui/views/mus:resources",
   ]
 }
-
-service_manifest("manifest") {
-  name = "quick_launch_app"
-  source = "manifest.json"
-}
diff --git a/ash/components/quick_launch/DEPS b/ash/components/quick_launch/DEPS
index b485565..bac08dc 100644
--- a/ash/components/quick_launch/DEPS
+++ b/ash/components/quick_launch/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+mash/public",
   "+services/catalog/public",
+  "+services/ws/public",
   "+url",
 ]
diff --git a/ash/components/quick_launch/OWNERS b/ash/components/quick_launch/OWNERS
deleted file mode 100644
index 59dfd4b3..0000000
--- a/ash/components/quick_launch/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file manifest.json=set noparent
-per-file manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/ash/components/quick_launch/manifest.json b/ash/components/quick_launch/manifest.json
deleted file mode 100644
index fb4c153..0000000
--- a/ash/components/quick_launch/manifest.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "name": "quick_launch_app",
-  "display_name": "Quick Launch Bar",
-  "sandbox_type": "none",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "requires": {
-        "*": [ "app", "mash:launchable" ],
-        "catalog": [
-          "catalog:catalog",
-          "directory"
-        ]
-      }
-    }
-  }
-}
diff --git a/ash/components/quick_launch/public/cpp/BUILD.gn b/ash/components/quick_launch/public/cpp/BUILD.gn
new file mode 100644
index 0000000..a331858
--- /dev/null
+++ b/ash/components/quick_launch/public/cpp/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("manifest") {
+  sources = [
+    "manifest.cc",
+    "manifest.h",
+  ]
+
+  deps = [
+    "//ash/components/quick_launch/public/mojom",
+    "//base",
+    "//services/service_manager/public/cpp",
+    "//services/ws/public/mojom:constants",
+  ]
+}
diff --git a/ash/components/quick_launch/public/cpp/OWNERS b/ash/components/quick_launch/public/cpp/OWNERS
new file mode 100644
index 0000000..6faeaa47
--- /dev/null
+++ b/ash/components/quick_launch/public/cpp/OWNERS
@@ -0,0 +1,4 @@
+per-file manifest.cc=set noparent
+per-file manifest.cc=file://ipc/SECURITY_OWNERS
+per-file manifest.h=set noparent
+per-file manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/ash/components/quick_launch/public/cpp/manifest.cc b/ash/components/quick_launch/public/cpp/manifest.cc
new file mode 100644
index 0000000..80245b6
--- /dev/null
+++ b/ash/components/quick_launch/public/cpp/manifest.cc
@@ -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.
+
+#include "ash/components/quick_launch/public/cpp/manifest.h"
+
+#include "ash/components/quick_launch/public/mojom/constants.mojom.h"
+#include "base/no_destructor.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+
+namespace quick_launch {
+
+const service_manager::Manifest& GetManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .WithServiceName(mojom::kServiceName)
+          .WithDisplayName("Quick Launch Bar")
+          .WithOptions(service_manager::ManifestOptionsBuilder()
+                           .WithSandboxType("none")
+                           .Build())
+          .RequireCapability(ws::mojom::kServiceName, "app")
+          .RequireCapability("catalog", "catalog:catalog")
+          .RequireCapability("catalog", "directory")
+          .RequireCapability("*", "mash:launchable")
+          .Build()};
+  return *manifest;
+}
+}  // namespace quick_launch
diff --git a/ash/components/quick_launch/public/cpp/manifest.h b/ash/components/quick_launch/public/cpp/manifest.h
new file mode 100644
index 0000000..a9b9003a
--- /dev/null
+++ b/ash/components/quick_launch/public/cpp/manifest.h
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMPONENTS_QUICK_LAUNCH_PUBLIC_CPP_MANIFEST_H_
+#define ASH_COMPONENTS_QUICK_LAUNCH_PUBLIC_CPP_MANIFEST_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+namespace quick_launch {
+
+const service_manager::Manifest& GetManifest();
+
+}  // namespace quick_launch
+
+#endif  // ASH_COMPONENTS_QUICK_LAUNCH_PUBLIC_CPP_MANIFEST_H_
diff --git a/ash/components/shortcut_viewer/BUILD.gn b/ash/components/shortcut_viewer/BUILD.gn
index 96eee1e..961a084 100644
--- a/ash/components/shortcut_viewer/BUILD.gn
+++ b/ash/components/shortcut_viewer/BUILD.gn
@@ -2,8 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//services/service_manager/public/service_manifest.gni")
-
 # KSV: Keyboard Shortcut Viewer
 source_set("lib") {
   sources = [
@@ -51,11 +49,6 @@
   ]
 }
 
-service_manifest("manifest") {
-  name = "shortcut_viewer_app"
-  source = "manifest.json"
-}
-
 source_set("unit_tests") {
   testonly = true
   sources = [
diff --git a/ash/components/shortcut_viewer/DEPS b/ash/components/shortcut_viewer/DEPS
index bbc2665..b4de305 100644
--- a/ash/components/shortcut_viewer/DEPS
+++ b/ash/components/shortcut_viewer/DEPS
@@ -2,6 +2,7 @@
   # KSV is intended to be a small app with restrictive DEPS in order to make it
   # easy to be migrated to a completely independent mojo app under mustash.
   "+ash/components/strings",
+  "+services/ws/public",
   "+ui/accessibility",
   "+ui/chromeos/events",
   "+ui/chromeos/search_box",
@@ -11,6 +12,5 @@
   "keyboard_shortcut_view_unittest\.cc": [
     "+ash/shell.h",
     "+ash/test/ash_test_base.h",
-    "+services/ws/public/cpp/input_devices",
   ],
 }
diff --git a/ash/components/shortcut_viewer/OWNERS b/ash/components/shortcut_viewer/OWNERS
index cf4e897..2e5455a 100644
--- a/ash/components/shortcut_viewer/OWNERS
+++ b/ash/components/shortcut_viewer/OWNERS
@@ -1,5 +1,2 @@
 afakhry@chromium.org
 wutao@chromium.org
-
-per-file manifest.json=set noparent
-per-file manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/ash/components/shortcut_viewer/manifest.json b/ash/components/shortcut_viewer/manifest.json
deleted file mode 100644
index c4aa822..0000000
--- a/ash/components/shortcut_viewer/manifest.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "name": "shortcut_viewer_app",
-  "display_name": "Keyboard Shortcut Viewer",
-  "sandbox_type": "none",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "provides": {
-        "shortcut_viewer": [ "shortcut_viewer.mojom.ShortcutViewer" ]
-      },
-      "requires": {
-        "*": [ "app" ],
-        "service_manager": [ "service_manager:service_manager" ]
-      }
-    }
-  }
-}
diff --git a/ash/components/shortcut_viewer/public/cpp/BUILD.gn b/ash/components/shortcut_viewer/public/cpp/BUILD.gn
new file mode 100644
index 0000000..01e5b6f
--- /dev/null
+++ b/ash/components/shortcut_viewer/public/cpp/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.
+
+source_set("manifest") {
+  sources = [
+    "manifest.cc",
+    "manifest.h",
+  ]
+
+  deps = [
+    "//ash/components/shortcut_viewer/public/mojom",
+    "//base",
+    "//services/service_manager/public/cpp",
+    "//services/ws/public/mojom:constants",
+    "//ui/accessibility/mojom",
+  ]
+}
diff --git a/ash/components/shortcut_viewer/public/cpp/OWNERS b/ash/components/shortcut_viewer/public/cpp/OWNERS
new file mode 100644
index 0000000..6faeaa47
--- /dev/null
+++ b/ash/components/shortcut_viewer/public/cpp/OWNERS
@@ -0,0 +1,4 @@
+per-file manifest.cc=set noparent
+per-file manifest.cc=file://ipc/SECURITY_OWNERS
+per-file manifest.h=set noparent
+per-file manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/ash/components/shortcut_viewer/public/cpp/manifest.cc b/ash/components/shortcut_viewer/public/cpp/manifest.cc
new file mode 100644
index 0000000..e245b14
--- /dev/null
+++ b/ash/components/shortcut_viewer/public/cpp/manifest.cc
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/components/shortcut_viewer/public/cpp/manifest.h"
+
+#include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
+#include "base/no_destructor.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+#include "ui/accessibility/mojom/ax_host.mojom.h"
+
+namespace shortcut_viewer {
+
+const service_manager::Manifest& GetManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .WithServiceName(mojom::kServiceName)
+          .WithDisplayName("Keyboard Shortcut Viewer")
+          .WithOptions(service_manager::ManifestOptionsBuilder()
+                           .WithSandboxType("none")
+                           .Build())
+          .ExposeCapability(
+              mojom::kToggleUiCapability,
+              service_manager::Manifest::InterfaceList<mojom::ShortcutViewer>())
+          .RequireCapability(ax::mojom::kAXHostServiceName, "app")
+          .RequireCapability(ws::mojom::kServiceName, "app")
+          .Build()};
+  return *manifest;
+}
+
+}  // namespace shortcut_viewer
diff --git a/ash/components/shortcut_viewer/public/cpp/manifest.h b/ash/components/shortcut_viewer/public/cpp/manifest.h
new file mode 100644
index 0000000..d44bf8a
--- /dev/null
+++ b/ash/components/shortcut_viewer/public/cpp/manifest.h
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMPONENTS_SHORTCUT_VIEWER_PUBLIC_CPP_MANIFEST_H_
+#define ASH_COMPONENTS_SHORTCUT_VIEWER_PUBLIC_CPP_MANIFEST_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+namespace shortcut_viewer {
+
+const service_manager::Manifest& GetManifest();
+
+}  // namespace shortcut_viewer
+
+#endif  // ASH_COMPONENTS_SHORTCUT_VIEWER_PUBLIC_CPP_MANIFEST_H_
diff --git a/ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom b/ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom
index 57218f3..d3020a1 100644
--- a/ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom
+++ b/ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom
@@ -8,6 +8,10 @@
 
 const string kServiceName = "shortcut_viewer_app";
 
+// Grants a client the ability to toggle the Keyboard Shortcut Viewer window
+// through the ShortcutViewer interface defined below.
+const string kToggleUiCapability = "toggle_ui";
+
 // Used to toggle the Keyboard Shortcut Viewer window.
 interface ShortcutViewer {
   // |user_gesture_time| is the time of the user gesture that caused the window
diff --git a/ash/components/tap_visualizer/BUILD.gn b/ash/components/tap_visualizer/BUILD.gn
index 137d8de..71c07a9 100644
--- a/ash/components/tap_visualizer/BUILD.gn
+++ b/ash/components/tap_visualizer/BUILD.gn
@@ -2,8 +2,6 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import("//services/service_manager/public/service_manifest.gni")
-
 source_set("lib") {
   sources = [
     "tap_renderer.cc",
@@ -31,11 +29,6 @@
   ]
 }
 
-service_manifest("manifest") {
-  name = "tap_visualizer_app"
-  source = "manifest.json"
-}
-
 source_set("unit_tests") {
   testonly = true
   sources = [
diff --git a/ash/components/tap_visualizer/OWNERS b/ash/components/tap_visualizer/OWNERS
deleted file mode 100644
index 59dfd4b3..0000000
--- a/ash/components/tap_visualizer/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-per-file manifest.json=set noparent
-per-file manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/ash/components/tap_visualizer/manifest.json b/ash/components/tap_visualizer/manifest.json
deleted file mode 100644
index ccc2753c..0000000
--- a/ash/components/tap_visualizer/manifest.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  "name": "tap_visualizer_app",
-  "display_name": "Show Taps",
-  "sandbox_type": "none",
-  "interface_provider_specs": {
-    "service_manager:connector": {
-      "provides": {
-        "tap_visualizer": [ "tap_visualizer.mojom.TapVisualizer" ]
-      },
-      "requires": {
-        "*": [ "app" ],
-        "service_manager": [ "service_manager:service_manager" ]
-      }
-    }
-  }
-}
diff --git a/ash/components/tap_visualizer/public/cpp/BUILD.gn b/ash/components/tap_visualizer/public/cpp/BUILD.gn
new file mode 100644
index 0000000..74eaab7
--- /dev/null
+++ b/ash/components/tap_visualizer/public/cpp/BUILD.gn
@@ -0,0 +1,17 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("manifest") {
+  sources = [
+    "manifest.cc",
+    "manifest.h",
+  ]
+
+  deps = [
+    "//ash/components/tap_visualizer/public/mojom",
+    "//base",
+    "//services/service_manager/public/cpp",
+    "//services/ws/public/mojom:constants",
+  ]
+}
diff --git a/ash/components/tap_visualizer/public/cpp/OWNERS b/ash/components/tap_visualizer/public/cpp/OWNERS
new file mode 100644
index 0000000..6faeaa47
--- /dev/null
+++ b/ash/components/tap_visualizer/public/cpp/OWNERS
@@ -0,0 +1,4 @@
+per-file manifest.cc=set noparent
+per-file manifest.cc=file://ipc/SECURITY_OWNERS
+per-file manifest.h=set noparent
+per-file manifest.h=file://ipc/SECURITY_OWNERS
diff --git a/ash/components/tap_visualizer/public/cpp/manifest.cc b/ash/components/tap_visualizer/public/cpp/manifest.cc
new file mode 100644
index 0000000..a330fcf
--- /dev/null
+++ b/ash/components/tap_visualizer/public/cpp/manifest.cc
@@ -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.
+
+#include "ash/components/tap_visualizer/public/cpp/manifest.h"
+
+#include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
+#include "base/no_destructor.h"
+#include "services/service_manager/public/cpp/manifest_builder.h"
+#include "services/ws/public/mojom/constants.mojom.h"
+
+namespace tap_visualizer {
+
+const service_manager::Manifest& GetManifest() {
+  static base::NoDestructor<service_manager::Manifest> manifest{
+      service_manager::ManifestBuilder()
+          .WithServiceName(mojom::kServiceName)
+          .WithDisplayName("Show Taps")
+          .WithOptions(service_manager::ManifestOptionsBuilder()
+                           .WithSandboxType("none")
+                           .Build())
+          .ExposeCapability(
+              mojom::kShowUiCapability,
+              service_manager::Manifest::InterfaceList<mojom::TapVisualizer>())
+          .RequireCapability(ws::mojom::kServiceName, "app")
+          .Build()};
+  return *manifest;
+}
+}  // namespace tap_visualizer
diff --git a/ash/components/tap_visualizer/public/cpp/manifest.h b/ash/components/tap_visualizer/public/cpp/manifest.h
new file mode 100644
index 0000000..a3da540a
--- /dev/null
+++ b/ash/components/tap_visualizer/public/cpp/manifest.h
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_COMPONENTS_TAP_VISUALIZER_PUBLIC_CPP_MANIFEST_H_
+#define ASH_COMPONENTS_TAP_VISUALIZER_PUBLIC_CPP_MANIFEST_H_
+
+#include "services/service_manager/public/cpp/manifest.h"
+
+namespace tap_visualizer {
+
+const service_manager::Manifest& GetManifest();
+
+}  // namespace tap_visualizer
+
+#endif  // ASH_COMPONENTS_TAP_VISUALIZER_PUBLIC_CPP_MANIFEST_H_
diff --git a/ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom b/ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom
index ecbc4c3..f0716d86 100644
--- a/ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom
+++ b/ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom
@@ -6,6 +6,10 @@
 
 const string kServiceName = "tap_visualizer_app";
 
+// Grants a client the ability to show this service's UI through the
+// TapVisualizer interface defined below.
+const string kShowUiCapability = "show_ui";
+
 interface TapVisualizer {
   // Show the UI.
   Show();
diff --git a/ash/public/cpp/app_list/app_list_features.cc b/ash/public/cpp/app_list/app_list_features.cc
index 54bcea5..b08bff2 100644
--- a/ash/public/cpp/app_list/app_list_features.cc
+++ b/ash/public/cpp/app_list/app_list_features.cc
@@ -30,6 +30,10 @@
     "EnableAppListSearchAutocomplete", base::FEATURE_ENABLED_BY_DEFAULT};
 const base::Feature kEnableAppSearchResultRanker{
     "EnableAppSearchResultRanker", base::FEATURE_ENABLED_BY_DEFAULT};
+const base::Feature kEnableAppReinstallZeroState{
+    "EnableAppReinstallZeroState", base::FEATURE_DISABLED_BY_DEFAULT};
+const base::Feature kEnableEmbeddedAssistantUI{
+    "EnableEmbeddedAssistantUI", base::FEATURE_DISABLED_BY_DEFAULT};
 
 bool IsAnswerCardEnabled() {
   // Not using local static variable to allow tests to change this value.
@@ -73,6 +77,14 @@
   return base::FeatureList::IsEnabled(kEnableAppSearchResultRanker);
 }
 
+bool IsAppReinstallZeroStateEnabled() {
+  return base::FeatureList::IsEnabled(kEnableAppReinstallZeroState);
+}
+
+bool IsEmbeddedAssistantUIEnabled() {
+  return base::FeatureList::IsEnabled(kEnableEmbeddedAssistantUI);
+}
+
 std::string AnswerServerUrl() {
   const std::string experiment_url =
       base::GetFieldTrialParamValueByFeature(kEnableAnswerCard, "ServerUrl");
diff --git a/ash/public/cpp/app_list/app_list_features.h b/ash/public/cpp/app_list/app_list_features.h
index 133ae98..c69d435 100644
--- a/ash/public/cpp/app_list/app_list_features.h
+++ b/ash/public/cpp/app_list/app_list_features.h
@@ -51,6 +51,13 @@
 // Enables the feature to rank app search result using AppSearchResultRanker.
 ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppSearchResultRanker;
 
+// Enables the feature to include a single reinstallation candidate in
+// zero-state.
+ASH_PUBLIC_EXPORT extern const base::Feature kEnableAppReinstallZeroState;
+
+// Enables the embedded Assistant UI in the app list.
+ASH_PUBLIC_EXPORT extern const base::Feature kEnableEmbeddedAssistantUI;
+
 bool ASH_PUBLIC_EXPORT IsAnswerCardEnabled();
 bool ASH_PUBLIC_EXPORT IsAppShortcutSearchEnabled();
 bool ASH_PUBLIC_EXPORT IsBackgroundBlurEnabled();
@@ -61,6 +68,8 @@
 bool ASH_PUBLIC_EXPORT IsZeroStateSuggestionsEnabled();
 bool ASH_PUBLIC_EXPORT IsAppListSearchAutocompleteEnabled();
 bool ASH_PUBLIC_EXPORT IsAppSearchResultRankerEnabled();
+bool ASH_PUBLIC_EXPORT IsAppReinstallZeroStateEnabled();
+bool ASH_PUBLIC_EXPORT IsEmbeddedAssistantUIEnabled();
 
 std::string ASH_PUBLIC_EXPORT AnswerServerUrl();
 std::string ASH_PUBLIC_EXPORT AnswerServerQuerySuffix();
diff --git a/ash/public/cpp/app_list/app_list_struct_traits.h b/ash/public/cpp/app_list/app_list_struct_traits.h
index 499e767..e7394aa 100644
--- a/ash/public/cpp/app_list/app_list_struct_traits.h
+++ b/ash/public/cpp/app_list/app_list_struct_traits.h
@@ -27,7 +27,8 @@
         return ash::mojom::AppListState::kStateSearchResults;
       case ash::AppListState::kStateStart:
         return ash::mojom::AppListState::kStateStart;
-      case ash::AppListState::kStateCustomLauncherPageDeprecated:
+      case ash::AppListState::kStateEmbeddedAssistant:
+        return ash::mojom::AppListState::kStateEmbeddedAssistant;
       case ash::AppListState::kInvalidState:
         break;
     }
@@ -47,6 +48,9 @@
       case ash::mojom::AppListState::kStateStart:
         *out = ash::AppListState::kStateStart;
         return true;
+      case ash::mojom::AppListState::kStateEmbeddedAssistant:
+        *out = ash::AppListState::kStateEmbeddedAssistant;
+        return true;
     }
     NOTREACHED();
     return false;
diff --git a/ash/public/cpp/app_list/app_list_types.h b/ash/public/cpp/app_list/app_list_types.h
index bba4c9a..13e4212 100644
--- a/ash/public/cpp/app_list/app_list_types.h
+++ b/ash/public/cpp/app_list/app_list_types.h
@@ -22,7 +22,7 @@
   kStateApps = 0,
   kStateSearchResults,
   kStateStart,
-  kStateCustomLauncherPageDeprecated,  // Don't use over IPC
+  kStateEmbeddedAssistant,
   // Add new values here.
 
   kInvalidState,               // Don't use over IPC
diff --git a/ash/public/cpp/ash_features.cc b/ash/public/cpp/ash_features.cc
index 809caaa..0e5a5a6 100644
--- a/ash/public/cpp/ash_features.cc
+++ b/ash/public/cpp/ash_features.cc
@@ -20,7 +20,7 @@
     "EnableOverviewRoundedCorners", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kLockScreenNotifications{"LockScreenNotifications",
-                                             base::FEATURE_ENABLED_BY_DEFAULT};
+                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
 const base::Feature kLockScreenInlineReply{"LockScreenInlineReply",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/ash/public/cpp/shelf_types.h b/ash/public/cpp/shelf_types.h
index 16015f6b..ed21eaf 100644
--- a/ash/public/cpp/shelf_types.h
+++ b/ash/public/cpp/shelf_types.h
@@ -71,6 +71,9 @@
   // The background when login/lock/user-add is active and the wallpaper is not
   // blurred.
   SHELF_BACKGROUND_LOGIN_NONBLURRED_WALLPAPER,
+
+  // The background when overview is active.
+  SHELF_BACKGROUND_OVERVIEW,
 };
 
 // Source of the launch or activation request, for tracking.
diff --git a/ash/public/interfaces/app_list.mojom b/ash/public/interfaces/app_list.mojom
index 8e6515f..112067d 100644
--- a/ash/public/interfaces/app_list.mojom
+++ b/ash/public/interfaces/app_list.mojom
@@ -88,6 +88,7 @@
   kStateApps = 0,
   kStateSearchResults,
   kStateStart,
+  kStateEmbeddedAssistant,
 };
 
 // The status of the app list model.
diff --git a/ash/public/interfaces/assistant_controller.mojom b/ash/public/interfaces/assistant_controller.mojom
index 12e5b78..75ec09c 100644
--- a/ash/public/interfaces/assistant_controller.mojom
+++ b/ash/public/interfaces/assistant_controller.mojom
@@ -32,7 +32,9 @@
   // true then the user gives permission to attach Assistant debug info.
   // |feedback_description| is user's feedback input.
   SendAssistantFeedback(
-    bool pii_allowed, string feedback_description);
+    bool pii_allowed,
+    string feedback_description,
+    string screenshot_png);
 };
 
 // Interface to the AssistantAlarmTimerController which is owned by the
diff --git a/ash/shelf/shelf_background_animator.cc b/ash/shelf/shelf_background_animator.cc
index dc40995..162baff 100644
--- a/ash/shelf/shelf_background_animator.cc
+++ b/ash/shelf/shelf_background_animator.cc
@@ -143,6 +143,7 @@
     ShelfBackgroundType background_type) const {
   switch (background_type) {
     case SHELF_BACKGROUND_DEFAULT:
+    case SHELF_BACKGROUND_OVERVIEW:
       return kShelfTranslucentAlpha;
     case SHELF_BACKGROUND_MAXIMIZED:
       return kShelfTranslucentMaximizedWindow;
@@ -230,6 +231,7 @@
     case SHELF_BACKGROUND_OOBE:
     case SHELF_BACKGROUND_LOGIN:
     case SHELF_BACKGROUND_LOGIN_NONBLURRED_WALLPAPER:
+    case SHELF_BACKGROUND_OVERVIEW:
       duration_ms = 250;
       break;
   }
@@ -267,6 +269,7 @@
   switch (background_type) {
     case SHELF_BACKGROUND_DEFAULT:
     case SHELF_BACKGROUND_APP_LIST:
+    case SHELF_BACKGROUND_OVERVIEW:
       shelf_target_color = darken_wallpaper(kShelfTranslucentColorDarkenAlpha);
       break;
     case SHELF_BACKGROUND_MAXIMIZED:
diff --git a/ash/shelf/shelf_layout_manager.cc b/ash/shelf/shelf_layout_manager.cc
index 278ca44..af81f82 100644
--- a/ash/shelf/shelf_layout_manager.cc
+++ b/ash/shelf/shelf_layout_manager.cc
@@ -487,6 +487,11 @@
   if (Shell::Get()->IsSplitViewModeActive())
     return SHELF_BACKGROUND_SPLIT_VIEW;
 
+  if (Shell::Get()->overview_controller() &&
+      Shell::Get()->overview_controller()->IsSelecting()) {
+    return SHELF_BACKGROUND_OVERVIEW;
+  }
+
   return SHELF_BACKGROUND_DEFAULT;
 }
 
@@ -544,8 +549,6 @@
   UpdateVisibilityState();
 }
 
-
-
 void ShelfLayoutManager::OnOverviewModeStartingAnimationComplete(
     bool canceled) {
   UpdateVisibilityState();
@@ -602,6 +605,7 @@
     return;
 
   is_home_launcher_shown_ = shown;
+  is_home_launcher_target_position_shown_ = false;
   MaybeUpdateShelfBackground(AnimationChangeType::IMMEDIATE);
 }
 
diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc
index 5140c31..a374c90 100644
--- a/ash/shelf/shelf_layout_manager_unittest.cc
+++ b/ash/shelf/shelf_layout_manager_unittest.cc
@@ -1280,7 +1280,7 @@
   overview_controller->ToggleOverview();
   EXPECT_EQ(SHELF_AUTO_HIDE, shelf->GetVisibilityState());
   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->GetAutoHideState());
-  EXPECT_EQ(SHELF_BACKGROUND_DEFAULT, GetShelfWidget()->GetBackgroundType());
+  EXPECT_EQ(SHELF_BACKGROUND_OVERVIEW, GetShelfWidget()->GetBackgroundType());
 
   // Test that on exiting overview mode, the shelf returns to auto hide state.
   overview_controller->ToggleOverview();
diff --git a/ash/shell/content/client/shell_content_browser_client.cc b/ash/shell/content/client/shell_content_browser_client.cc
index 5b289a8d..86a035c 100644
--- a/ash/shell/content/client/shell_content_browser_client.cc
+++ b/ash/shell/content/client/shell_content_browser_client.cc
@@ -7,11 +7,11 @@
 #include <utility>
 
 #include "ash/ash_service.h"
-#include "ash/components/quick_launch/manifest.h"
+#include "ash/components/quick_launch/public/cpp/manifest.h"
 #include "ash/components/quick_launch/public/mojom/constants.mojom.h"
-#include "ash/components/shortcut_viewer/manifest.h"
+#include "ash/components/shortcut_viewer/public/cpp/manifest.h"
 #include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"
-#include "ash/components/tap_visualizer/manifest.h"
+#include "ash/components/tap_visualizer/public/cpp/manifest.h"
 #include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"
 #include "ash/public/cpp/manifest.h"
 #include "ash/public/cpp/test_manifest.h"
@@ -50,9 +50,9 @@
       service_manager::ManifestBuilder()
           .RequireCapability(device::mojom::kServiceName, "device:fingerprint")
           .RequireCapability(shortcut_viewer::mojom::kServiceName,
-                             "shortcut_viewer")
+                             shortcut_viewer::mojom::kToggleUiCapability)
           .RequireCapability(tap_visualizer::mojom::kServiceName,
-                             "tap_visualizer")
+                             tap_visualizer::mojom::kShowUiCapability)
           .Build()};
   return *manifest;
 }
@@ -62,9 +62,9 @@
       service_manager::ManifestBuilder()
           .PackageService(service_manager::Manifest(ash::GetManifest())
                               .Amend(ash::GetManifestOverlayForTesting()))
-          .PackageService(quick_launch_app::GetManifest())
-          .PackageService(shortcut_viewer_app::GetManifest())
-          .PackageService(tap_visualizer_app::GetManifest())
+          .PackageService(quick_launch::GetManifest())
+          .PackageService(shortcut_viewer::GetManifest())
+          .PackageService(tap_visualizer::GetManifest())
           .PackageService(test_ime_driver::GetManifest())
           .Build()};
   return *manifest;
diff --git a/ash/system/audio/unified_volume_view.cc b/ash/system/audio/unified_volume_view.cc
index c280c64..6fe3630 100644
--- a/ash/system/audio/unified_volume_view.cc
+++ b/ash/system/audio/unified_volume_view.cc
@@ -143,7 +143,8 @@
   // Indicate that the slider is inactive when it's muted.
   slider()->UpdateState(!is_muted);
 
-  button()->SetToggled(is_muted);
+  // The button should be gray whay muted and colored otherwise.
+  button()->SetToggled(!is_muted);
   button()->SetVectorIcon(is_muted ? kUnifiedMenuVolumeMuteIcon
                                    : GetVolumeIconForLevel(level));
 
diff --git a/ash/wm/splitview/split_view_controller.cc b/ash/wm/splitview/split_view_controller.cc
index f7e8b654..fef3dd2f 100644
--- a/ash/wm/splitview/split_view_controller.cc
+++ b/ash/wm/splitview/split_view_controller.cc
@@ -676,6 +676,12 @@
   SnapWindow(gained_active, (default_snap_position_ == LEFT) ? RIGHT : LEFT);
 }
 
+void SplitViewController::OnPinnedStateChanged(aura::Window* pinned_window) {
+  // Disable split view for pinned windows.
+  if (wm::GetWindowState(pinned_window)->IsPinned() && IsSplitViewModeActive())
+    EndSplitView(EndReason::kUnsnappableWindowActivated);
+}
+
 void SplitViewController::OnOverviewModeStarting() {
   DCHECK(IsSplitViewModeActive());
 
diff --git a/ash/wm/splitview/split_view_controller.h b/ash/wm/splitview/split_view_controller.h
index 58fc638..91b552813 100644
--- a/ash/wm/splitview/split_view_controller.h
+++ b/ash/wm/splitview/split_view_controller.h
@@ -167,6 +167,7 @@
                          aura::Window* lost_active) override;
 
   // ShellObserver:
+  void OnPinnedStateChanged(aura::Window* pinned_window) override;
   void OnOverviewModeStarting() override;
   void OnOverviewModeEnding(OverviewSession* overview_session) override;
 
diff --git a/ash/wm/splitview/split_view_controller_unittest.cc b/ash/wm/splitview/split_view_controller_unittest.cc
index c13db53..e824cc81 100644
--- a/ash/wm/splitview/split_view_controller_unittest.cc
+++ b/ash/wm/splitview/split_view_controller_unittest.cc
@@ -1764,6 +1764,30 @@
   EXPECT_TRUE(always_on_top_window->GetProperty(aura::client::kAlwaysOnTopKey));
 }
 
+// Test that pinning a window ends split view mode.
+TEST_F(SplitViewControllerTest, PinningWindowEndsSplitView) {
+  const gfx::Rect bounds(0, 0, 400, 400);
+  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+
+  split_view_controller()->SnapWindow(window1.get(), SplitViewController::LEFT);
+  EXPECT_TRUE(split_view_controller()->IsSplitViewModeActive());
+
+  wm::PinWindow(window1.get(), true);
+  EXPECT_FALSE(split_view_controller()->IsSplitViewModeActive());
+}
+
+// Test that split view mode is disallowed while we're in pinned mode (there is
+// a pinned window).
+TEST_F(SplitViewControllerTest, PinnedWindowDisallowsSplitView) {
+  const gfx::Rect bounds(0, 0, 400, 400);
+  std::unique_ptr<aura::Window> window1(CreateWindow(bounds));
+
+  EXPECT_TRUE(ShouldAllowSplitView());
+
+  wm::PinWindow(window1.get(), true);
+  EXPECT_FALSE(ShouldAllowSplitView());
+}
+
 // Test the tab-dragging related functionalities in tablet mode. Tab(s) can be
 // dragged out of a window and then put in split view mode or merge into another
 // window.
diff --git a/ash/wm/splitview/split_view_utils.cc b/ash/wm/splitview/split_view_utils.cc
index cd39ccf9..b9b9bca 100644
--- a/ash/wm/splitview/split_view_utils.cc
+++ b/ash/wm/splitview/split_view_utils.cc
@@ -8,6 +8,7 @@
 #include "ash/public/cpp/ash_switches.h"
 #include "ash/screen_util.h"
 #include "ash/shell.h"
+#include "ash/wm/screen_pinning_controller.h"
 #include "ash/wm/splitview/split_view_constants.h"
 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
 #include "ash/wm/window_state.h"
@@ -211,6 +212,10 @@
     return false;
   }
 
+  // Don't allow split view if we're in pinned mode.
+  if (Shell::Get()->screen_pinning_controller()->IsPinned())
+    return false;
+
   // TODO(crubg.com/853588): Disallow window dragging and split screen while
   // ChromeVox is on until they are in a usable state.
   if (Shell::Get()->accessibility_controller()->spoken_feedback_enabled())
diff --git a/ash/wm/tablet_mode/tablet_mode_controller.cc b/ash/wm/tablet_mode/tablet_mode_controller.cc
index 302e7ee..8273fc1 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller.cc
@@ -629,13 +629,14 @@
   // Enter clamshell mode whenever an external pointing device is attached.
   if (has_external_pointing_device) {
     AttemptLeaveTabletMode();
-  } else if (LidAngleIsInTabletModeRange() || tablet_mode_switch_is_on_) {
-    // If there is no external pointing device, only enter tablet mode if 1) the
-    // lid angle can be detected and is in tablet mode angle range. or 2) if the
-    // lid angle can't be detected (e.g., tablet device or clamshell device) and
-    // |tablet_mode_switch_is_on_| is true (it can only happen for tablet device
-    // as |tablet_mode_switch_is_on_| should never be true for a clamshell
-    // device).
+  } else if (HasActiveInternalDisplay() &&
+             (LidAngleIsInTabletModeRange() || tablet_mode_switch_is_on_)) {
+    // If there is no external pointing device, only enter tablet mode if docked
+    // mode is inactive and 1) the lid angle can be detected and is in tablet
+    // mode angle range. or 2) if the lid angle can't be detected (e.g., tablet
+    // device or clamshell device) and |tablet_mode_switch_is_on_| is true (it
+    // can only happen for tablet device as |tablet_mode_switch_is_on_| should
+    // never be true for a clamshell device).
     AttemptEnterTabletMode();
   }
 }
diff --git a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
index 8cec93b..e0fa102 100644
--- a/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
+++ b/ash/wm/tablet_mode/tablet_mode_controller_unittest.cc
@@ -724,6 +724,8 @@
 TEST_F(TabletModeControllerTest, CannotEnterTabletModeWithExternalMouse) {
   // Set the current list of devices to empty so that they don't interfere
   // with the test.
+  // TODO(sammiequon): Investigate whether RunUntilIdle() calls like this one
+  // are really necessary. Remove them or add a comment explaining the purpose.
   base::RunLoop().RunUntilIdle();
   ws::InputDeviceClientTestApi().SetMouseDevices({});
   base::RunLoop().RunUntilIdle();
@@ -803,6 +805,50 @@
   EXPECT_FALSE(AreEventsBlocked());
 }
 
+// Test that docked mode prevents entering tablet mode on detaching an external
+// mouse while in tablet position.
+TEST_F(TabletModeControllerTest, ExternalMouseInDockedMode) {
+  // Set the current list of devices to empty so that they don't interfere
+  // with the test.
+  base::RunLoop().RunUntilIdle();
+  ws::InputDeviceClientTestApi().SetMouseDevices({});
+  base::RunLoop().RunUntilIdle();
+
+  UpdateDisplay("800x600, 800x600");
+  const int64_t internal_display_id =
+      display::test::DisplayManagerTestApi(display_manager())
+          .SetFirstDisplayAsInternalDisplay();
+
+  // Set the current list of devices with an external mouse.
+  ws::InputDeviceClientTestApi().SetMouseDevices(
+      {ui::InputDevice(3, ui::InputDeviceType::INPUT_DEVICE_USB, "mouse")});
+  base::RunLoop().RunUntilIdle();
+
+  // Deactivate internal display to simulate Docked Mode.
+  std::vector<display::ManagedDisplayInfo> all_displays;
+  all_displays.push_back(display_manager()->GetDisplayInfo(
+      display_manager()->GetDisplayAt(0).id()));
+  std::vector<display::ManagedDisplayInfo> secondary_only;
+  display::ManagedDisplayInfo secondary_display =
+      display_manager()->GetDisplayInfo(
+          display_manager()->GetDisplayAt(1).id());
+  all_displays.push_back(secondary_display);
+  secondary_only.push_back(secondary_display);
+  display_manager()->OnNativeDisplaysChanged(secondary_only);
+  ASSERT_FALSE(display_manager()->IsActiveDisplayId(internal_display_id));
+
+  // Enter tablet position.
+  SetTabletMode(true);
+  ASSERT_FALSE(IsTabletModeStarted());
+
+  // Detach the external mouse.
+  ws::InputDeviceClientTestApi().SetMouseDevices({});
+  base::RunLoop().RunUntilIdle();
+
+  // Still expect clamshell mode.
+  EXPECT_FALSE(IsTabletModeStarted());
+}
+
 // Test that the ui mode and input event blocker should be both correctly
 // updated when there is a change in external mouse and lid angle.
 TEST_F(TabletModeControllerTest, ExternalMouseWithLidAngleTest) {
diff --git a/ash/wm/wm_toplevel_window_event_handler.cc b/ash/wm/wm_toplevel_window_event_handler.cc
index 2db4efe..97e3cc9b 100644
--- a/ash/wm/wm_toplevel_window_event_handler.cc
+++ b/ash/wm/wm_toplevel_window_event_handler.cc
@@ -484,6 +484,11 @@
 
   end_closure_ = std::move(end_closure);
   in_gesture_drag_ = (source == ::wm::WINDOW_MOVE_SOURCE_TOUCH);
+  // |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_)
+    UpdateGestureTarget(window);
+
   return true;
 }
 
diff --git a/base/BUILD.gn b/base/BUILD.gn
index b74dc76..9a6d8fb3 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -177,6 +177,8 @@
     "compiler_specific.h",
     "component_export.h",
     "containers/adapters.h",
+    "containers/any_internal.cc",
+    "containers/any_internal.h",
     "containers/checked_iterators.h",
     "containers/circular_deque.h",
     "containers/flat_map.h",
@@ -189,6 +191,8 @@
     "containers/span.h",
     "containers/stack.h",
     "containers/stack_container.h",
+    "containers/unique_any.cc",
+    "containers/unique_any.h",
     "containers/unique_ptr_adapters.h",
     "containers/util.h",
     "containers/vector_buffer.h",
@@ -2296,6 +2300,7 @@
     "command_line_unittest.cc",
     "component_export_unittest.cc",
     "containers/adapters_unittest.cc",
+    "containers/any_internal_unittest.cc",
     "containers/circular_deque_unittest.cc",
     "containers/flat_map_unittest.cc",
     "containers/flat_set_unittest.cc",
@@ -2306,6 +2311,7 @@
     "containers/small_map_unittest.cc",
     "containers/span_unittest.cc",
     "containers/stack_container_unittest.cc",
+    "containers/unique_any_unittest.cc",
     "containers/unique_ptr_adapters_unittest.cc",
     "containers/vector_buffer_unittest.cc",
     "cpu_unittest.cc",
@@ -2855,6 +2861,7 @@
       "callback_list_unittest.nc",
       "callback_unittest.nc",
       "containers/span_unittest.nc",
+      "containers/unique_any_unittest.nc",
       "memory/ref_counted_unittest.nc",
       "memory/weak_ptr_unittest.nc",
       "metrics/field_trial_params_unittest.nc",
diff --git a/base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java b/base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java
index dc90f57..907207d 100644
--- a/base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java
+++ b/base/android/java/src/org/chromium/base/memory/MemoryPressureUma.java
@@ -19,13 +19,11 @@
  * Centralizes UMA data collection for Android-specific memory conditions.
  */
 public class MemoryPressureUma implements ComponentCallbacks2 {
-    @IntDef({
-            Notification.UNKNOWN_TRIM_LEVEL, Notification.TRIM_MEMORY_COMPLETE,
+    @IntDef({Notification.UNKNOWN_TRIM_LEVEL, Notification.TRIM_MEMORY_COMPLETE,
             Notification.TRIM_MEMORY_MODERATE, Notification.TRIM_MEMORY_BACKGROUND,
             Notification.TRIM_MEMORY_UI_HIDDEN, Notification.TRIM_MEMORY_RUNNING_CRITICAL,
             Notification.TRIM_MEMORY_RUNNING_LOW, Notification.TRIM_MEMORY_RUNNING_MODERATE,
-            Notification.ON_LOW_MEMORY, Notification.NOTIFICATION_MAX,
-    })
+            Notification.ON_LOW_MEMORY})
     @Retention(RetentionPolicy.SOURCE)
     private @interface Notification {
         // WARNING: These values are persisted to logs. Entries should not be
@@ -42,7 +40,7 @@
         int ON_LOW_MEMORY = 8;
 
         // Must be the last one.
-        int NOTIFICATION_MAX = 9;
+        int NUM_ENTRIES = 9;
     }
 
     private final String mHistogramName;
@@ -108,6 +106,6 @@
 
     private void record(@Notification int notification) {
         RecordHistogram.recordEnumeratedHistogram(
-                mHistogramName, notification, Notification.NOTIFICATION_MAX);
+                mHistogramName, notification, Notification.NUM_ENTRIES);
     }
 }
diff --git a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
index 6e285f9..1629c5d 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
+++ b/base/android/java/src/org/chromium/base/process_launcher/ChildProcessService.java
@@ -28,6 +28,8 @@
 import org.chromium.base.memory.MemoryPressureMonitor;
 import org.chromium.base.metrics.RecordHistogram;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.List;
 import java.util.concurrent.Semaphore;
 
@@ -97,6 +99,7 @@
     @IntDef({SplitApkWorkaroundResult.NOT_RUN, SplitApkWorkaroundResult.NO_ENTRIES,
             SplitApkWorkaroundResult.ONE_ENTRY, SplitApkWorkaroundResult.MULTIPLE_ENTRIES,
             SplitApkWorkaroundResult.TOPLEVEL_EXCEPTION, SplitApkWorkaroundResult.LOOP_EXCEPTION})
+    @Retention(RetentionPolicy.SOURCE)
     public @interface SplitApkWorkaroundResult {
         int NOT_RUN = 0;
         int NO_ENTRIES = 1;
@@ -105,7 +108,7 @@
         int TOPLEVEL_EXCEPTION = 4;
         int LOOP_EXCEPTION = 5;
         // Keep this one at the end and increment appropriately when adding new results.
-        int SPLIT_APK_WORKAROUND_RESULT_COUNT = 6;
+        int NUM_ENTRIES = 6;
     }
 
     private static @SplitApkWorkaroundResult int sSplitApkWorkaroundResult =
@@ -271,8 +274,7 @@
                     if (ContextUtils.isIsolatedProcess()) {
                         RecordHistogram.recordEnumeratedHistogram(
                                 "Android.WebView.SplitApkWorkaroundResult",
-                                sSplitApkWorkaroundResult,
-                                SplitApkWorkaroundResult.SPLIT_APK_WORKAROUND_RESULT_COUNT);
+                                sSplitApkWorkaroundResult, SplitApkWorkaroundResult.NUM_ENTRIES);
                     }
                     if (mActivitySemaphore.tryAcquire()) {
                         mDelegate.runMain();
diff --git a/base/containers/any_internal.cc b/base/containers/any_internal.cc
new file mode 100644
index 0000000..0d9e77d
--- /dev/null
+++ b/base/containers/any_internal.cc
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/any_internal.h"
+
+namespace base {
+namespace internal {
+
+AnyInternal::~AnyInternal() {
+  reset();
+}
+
+void AnyInternal::reset() {
+  if (!has_value())
+    return;
+
+  type_ops_->delete_fn_ptr(this);
+  type_ops_ = nullptr;
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/containers/any_internal.h b/base/containers/any_internal.h
new file mode 100644
index 0000000..e67715a
--- /dev/null
+++ b/base/containers/any_internal.h
@@ -0,0 +1,263 @@
+// 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_CONTAINERS_ANY_INTERNAL_H_
+#define BASE_CONTAINERS_ANY_INTERNAL_H_
+
+#include <memory>
+#include <utility>
+
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/template_util.h"
+#include "base/type_id.h"
+
+namespace base {
+
+namespace internal {
+
+// Common non-templated implementation details for base::unique_any. If we ever
+// want to support base::any this, file could be easily modified to do so
+// (TypeOps would need to support copying).
+class BASE_EXPORT AnyInternal {
+ public:
+  constexpr AnyInternal() noexcept : type_ops_(nullptr), union_({}) {}
+
+  AnyInternal(AnyInternal&& other) noexcept
+      : type_ops_(other.type_ops_), union_({}) {
+    if (other.type_ops_)
+      other.type_ops_->move_fn_ptr(&other, this);
+  }
+
+  ~AnyInternal();
+
+  struct TypeOps;
+
+  constexpr explicit AnyInternal(const TypeOps* type_ops)
+      : type_ops_(type_ops), union_({}) {}
+
+  constexpr void operator=(AnyInternal&& other) noexcept {
+    reset();
+    if (other.type_ops_)
+      other.type_ops_->move_fn_ptr(&other, this);
+    type_ops_ = other.type_ops_;
+  }
+
+  constexpr bool has_value() const noexcept { return !!type_ops_; }
+
+  TypeId type() const noexcept {
+    if (!type_ops_)
+      return TypeId::From<void>();
+
+    return type_ops_->type_fn_ptr();
+  }
+
+  void reset() noexcept;
+
+  template <typename T, bool UseInlineStorage>
+  struct ConstructHelper;
+
+  template <bool UseInlineStorage>
+  struct GetStorageHelper;
+
+  template <typename T, bool UseInlineStorage, bool HasMoveConstructor>
+  struct MoveHelper;
+
+  template <typename T, bool UseInlineStorage>
+  struct DeleteHelper;
+
+  // Where possible we use the small object allocation optimization to avoid
+  // heap allocations.
+  struct OutlineAlloc {
+    void* value;  // Holds a T
+
+    template <typename T>
+    T& value_as() {
+      return *static_cast<T*>(value);
+    }
+
+    template <typename T>
+    const T& value_as() const {
+      return *static_cast<const T*>(value);
+    }
+  };
+
+  struct alignas(sizeof(void*)) InlineAlloc {
+    // Holds a T if small.
+    char bytes[sizeof(void*)];
+
+    template <typename T>
+    T& value_as() {
+      return *reinterpret_cast<T*>(bytes);
+    }
+
+    template <typename T>
+    const T& value_as() const {
+      return *reinterpret_cast<const T*>(bytes);
+    }
+  };
+
+  template <typename T>
+  struct InlineStorageHelper {
+    static constexpr bool kUseInlineStorage =
+        (sizeof(T) <= sizeof(InlineAlloc));
+
+    static_assert(
+        std::alignment_of<T>::value <= sizeof(T),
+        "Type T has alignment requirements that preclude it's storage inline.");
+  };
+
+  template <typename T>
+  constexpr T* GetStorage() {
+    return static_cast<T*>(
+        GetStorageHelper<InlineStorageHelper<T>::kUseInlineStorage>::GetStorage(
+            *this));
+  }
+
+  template <typename T>
+  constexpr const T* GetStorage() const {
+    return static_cast<const T*>(
+        GetStorageHelper<InlineStorageHelper<T>::kUseInlineStorage>::GetStorage(
+            *this));
+  }
+
+  using TypeIdFunctionPtr = TypeId (*)();
+  using MoveFunctionPtr = void (*)(AnyInternal* src, AnyInternal* dest);
+  using DeleteFunctionPtr = void (*)(AnyInternal* object);
+
+  // Similar to a virtual function but we don't need a dynamic memory
+  // allocation. One possible design alternative would be to fold these methods
+  // into T and use T in InlineAlloc (which would now have to
+  // be bigger to accommodate the vtable pointer).
+  struct TypeOps {
+    // TODO(alexclarke): If TypeId can be constexpr store TypeId here directly.
+    TypeIdFunctionPtr type_fn_ptr;
+    MoveFunctionPtr move_fn_ptr;
+    DeleteFunctionPtr delete_fn_ptr;
+  };
+
+  template <typename T>
+  struct TypeOpsHelper {
+    static constexpr TypeOps type_ops = {
+        &TypeId::From<T>,
+        &AnyInternal::MoveHelper<T,
+                                 InlineStorageHelper<T>::kUseInlineStorage,
+                                 std::is_move_constructible<T>::value>::Move,
+        &AnyInternal::
+            DeleteHelper<T, InlineStorageHelper<T>::kUseInlineStorage>::Delete};
+  };
+
+  // Null if the instance has no value.
+  const TypeOps* type_ops_;
+
+  union {
+    OutlineAlloc outline_alloc;
+    InlineAlloc inline_alloc;
+  } union_;
+};
+
+// static
+template <typename T>
+const AnyInternal::TypeOps AnyInternal::TypeOpsHelper<T>::type_ops;
+
+template <typename T>
+struct AnyInternal::ConstructHelper<T, /* UseInlineStorage */ true> {
+  template <typename... Args>
+  static void Construct(AnyInternal* dest, Args&&... args) noexcept {
+    new (dest->union_.inline_alloc.bytes) T(std::forward<Args>(args)...);
+  }
+};
+
+template <typename T>
+struct AnyInternal::ConstructHelper<T, /* UseInlineStorage */ false> {
+  template <typename... Args>
+  static void Construct(AnyInternal* dest, Args&&... args) noexcept {
+    dest->union_.outline_alloc.value = new T(std::forward<Args>(args)...);
+  }
+};
+
+template <>
+struct AnyInternal::GetStorageHelper</* UseInlineStorage */ true> {
+  static void* GetStorage(AnyInternal& any) {
+    return &any.union_.inline_alloc.bytes;
+  }
+
+  static const void* GetStorage(const AnyInternal& any) {
+    return &any.union_.inline_alloc.bytes;
+  }
+};
+
+template <>
+struct AnyInternal::GetStorageHelper</* UseInlineStorage */ false> {
+  static void* GetStorage(AnyInternal& any) {
+    return any.union_.outline_alloc.value;
+  }
+
+  static const void* GetStorage(const AnyInternal& any) {
+    return any.union_.outline_alloc.value;
+  }
+};
+
+template <typename T>
+struct AnyInternal::
+    MoveHelper<T, /* UseInlineStorage */ true, /* HasMoveConstructor */ true> {
+  static void Move(AnyInternal* src, AnyInternal* dest) {
+    DCHECK_NE(src, dest);
+    new (dest->union_.inline_alloc.bytes)
+        T(std::move(src->union_.inline_alloc.value_as<T>()));
+  }
+};
+
+template <typename T>
+struct AnyInternal::
+    MoveHelper<T, /* UseInlineStorage */ false, /* HasMoveConstructor */ true> {
+  static void Move(AnyInternal* src, AnyInternal* dest) {
+    DCHECK_NE(src, dest);
+    dest->union_.outline_alloc.value =
+        new T(std::move(src->union_.outline_alloc.value_as<T>()));
+  }
+};
+
+template <typename T>
+struct AnyInternal::
+    MoveHelper<T, /* UseInlineStorage */ true, /* HasMoveConstructor */ false> {
+  static void Move(AnyInternal* src, AnyInternal* dest) {
+    DCHECK_NE(src, dest);
+    // Fall back to the copy constructor.
+    new (dest->union_.inline_alloc.bytes)
+        T(src->union_.inline_alloc.value_as<T>());
+  }
+};
+
+template <typename T>
+struct AnyInternal::MoveHelper<T,
+                               /* UseInlineStorage */ false,
+                               /* HasMoveConstructor */ false> {
+  static void Move(AnyInternal* src, AnyInternal* dest) {
+    DCHECK_NE(src, dest);
+    // Fall back to the copy constructor.
+    dest->union_.outline_alloc.value =
+        new T(src->union_.outline_alloc.value_as<T>());
+  }
+};
+
+template <typename T>
+struct AnyInternal::DeleteHelper<T, /* UseInlineStorage */ true> {
+  static void Delete(AnyInternal* any) {
+    reinterpret_cast<T*>(&any->union_.inline_alloc.bytes)->~T();
+  }
+};
+
+template <typename T>
+struct AnyInternal::DeleteHelper<T, /* UseInlineStorage */ false> {
+  static void Delete(AnyInternal* any) {
+    delete static_cast<T*>(any->union_.outline_alloc.value);
+  }
+};
+
+}  // namespace internal
+}  // namespace base
+
+#endif  // BASE_CONTAINERS_ANY_INTERNAL_H_
diff --git a/base/containers/any_internal_unittest.cc b/base/containers/any_internal_unittest.cc
new file mode 100644
index 0000000..dacd2ff8
--- /dev/null
+++ b/base/containers/any_internal_unittest.cc
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/any_internal.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+TEST(AnyInternalTest, InlineOrOutlineStorage) {
+  static_assert(AnyInternal::InlineStorageHelper<int>::kUseInlineStorage,
+                "int should be stored inline");
+  static_assert(AnyInternal::InlineStorageHelper<int*>::kUseInlineStorage,
+                "int* should be stored inline");
+  static_assert(
+      AnyInternal::InlineStorageHelper<std::unique_ptr<int>>::kUseInlineStorage,
+      "std::unique_ptr<int> should be stored inline");
+  static_assert(
+      !AnyInternal::InlineStorageHelper<std::string>::kUseInlineStorage,
+      "std::string should be stored out of line");
+}
+
+}  // namespace internal
+}  // namespace base
diff --git a/base/containers/checked_iterators.h b/base/containers/checked_iterators.h
index c243ce93..8b3f282 100644
--- a/base/containers/checked_iterators.h
+++ b/base/containers/checked_iterators.h
@@ -57,6 +57,11 @@
     return current_ < other.current_;
   }
 
+  bool operator<=(const CheckedRandomAccessIterator& other) const {
+    CheckComparable(other);
+    return current_ <= other.current_;
+  }
+
   CheckedRandomAccessIterator& operator++() {
     CHECK(current_ != end_);
     ++current_;
@@ -205,6 +210,11 @@
     return current_ < other.current_;
   }
 
+  bool operator<=(const CheckedRandomAccessConstIterator& other) const {
+    CheckComparable(other);
+    return current_ <= other.current_;
+  }
+
   CheckedRandomAccessConstIterator& operator++() {
     CHECK(current_ != end_);
     ++current_;
diff --git a/base/containers/unique_any.cc b/base/containers/unique_any.cc
new file mode 100644
index 0000000..2a16a209
--- /dev/null
+++ b/base/containers/unique_any.cc
@@ -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.
+
+#include "base/containers/unique_any.h"
+
+namespace base {
+
+unique_any::~unique_any() = default;
+
+}  // namespace base
diff --git a/base/containers/unique_any.h b/base/containers/unique_any.h
new file mode 100644
index 0000000..af5717e4
--- /dev/null
+++ b/base/containers/unique_any.h
@@ -0,0 +1,335 @@
+// 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_CONTAINERS_UNIQUE_ANY_H_
+#define BASE_CONTAINERS_UNIQUE_ANY_H_
+
+#include <utility>
+
+#include "base/containers/any_internal.h"
+
+// base::unique_any is similar to std::any except it:
+// * can hold move constructible types such as std::unique_ptr<>
+// * doesn't have a copy constructor or copy assignment operator
+// * doesn't require exceptions or RTTI.
+//
+// It can be thought of as a better void * E.g.
+// base::unique_any a = 123;
+// EXPECT_EQ(123, base::unique_any_cast<int>(a));
+//
+// a = std::string("123");
+// EXPECT_EQ("123", base::unique_any_cast<std::string>(a));
+//
+// a = make_unique_ptr("123");
+// EXPECT_EQ("123", *base::unique_any_cast<std::unique_ptr<int>>(a));
+//
+// base::unique_any b(std::move(a));
+// EXPECT_EQ("123", *base::unique_any_cast<std::unique_ptr<int>>(b));
+//
+// Note an incorrect base::unique_any_cast will lead to a CHECK.
+
+namespace base {
+
+class unique_any;
+
+namespace internal {
+
+template <typename T>
+struct is_unique_any {
+  static constexpr bool value = false;
+};
+
+template <>
+struct is_unique_any<unique_any> {
+  static constexpr bool value = true;
+};
+
+}  // namespace internal
+
+// Constructs a base::unique_any of type |T| with the given arguments.
+template <typename T, typename... Args>
+unique_any make_unique_any(Args&&... args);
+
+// Overload of |base::make_unique_any()| for constructing a base::unique_any
+// type from an initializer list. E.g.
+// base::unique_any a = base::make_any<std::vector<int>>({1, 2, 3, 4});
+template <typename T, typename U, typename... Args>
+unique_any make_unique_any(std::initializer_list<U> il, Args&&... args);
+
+// Statically casts the value of a const base::unique_any to the given type.
+// Unlike std::any_cast which throws an exception, this function will CHECK if
+// the stored value type |any| does not match the cast. E.g.
+//
+//     base::unique_any a = 123;
+//     int i = base::unique_any_cast<int>(a);  // i = 123
+//
+// NB unique_any_cast() can also be used to get a reference to the internal
+// storage iff a reference type is passed as its ValueType. E.g.
+//
+//     base::unique_any my_any = std::vector<int>();
+//     base::unique_any_cast<std::vector<int>&>(my_any).push_back(42);
+//
+template <typename ValueType>
+ValueType unique_any_cast(const unique_any& any);
+
+template <typename ValueType>
+ValueType unique_any_cast(unique_any& any);
+
+template <typename ValueType>
+ValueType unique_any_cast(unique_any&& any);
+
+// Overload of unique_any_cast() to statically cast the value of a const pointer
+// base::unique_any to the given pointer type, or nullptr if the stored value
+// type of |any| does not match the cast.
+template <typename ValueType>
+const ValueType* unique_any_cast(const unique_any* any) noexcept;
+
+template <typename ValueType>
+ValueType* unique_any_cast(unique_any* any) noexcept;
+
+class BASE_EXPORT unique_any {
+ private:
+  template <typename T, bool UseInlineStorage>
+  using ConstructHelper =
+      internal::AnyInternal::ConstructHelper<T, UseInlineStorage>;
+
+  template <typename T>
+  using InlineStorageHelper = internal::AnyInternal::InlineStorageHelper<T>;
+
+  template <typename T>
+  using TypeOpsHelper = internal::AnyInternal::TypeOpsHelper<T>;
+
+ public:
+  // Constructs an empty base::unique_any.
+  constexpr unique_any() noexcept {}
+
+  // Constructs a base::unique_any with the value contained by |other| moved
+  // into it.
+  constexpr unique_any(unique_any&& other) noexcept
+      : internal_(other.internal_.type_ops_) {
+    if (internal_.type_ops_)
+      internal_.type_ops_->move_fn_ptr(&other.internal_, &internal_);
+  }
+
+  template <typename T>
+  struct is_move_or_copy_constructible {
+    static constexpr bool value = std::is_copy_constructible<T>::value ||
+                                  std::is_move_constructible<T>::value;
+  };
+
+  // Constructs a base::unique_any containing |value| as long as |T| is move or
+  // copy constructible and it isn't base::unique_any or
+  // base::in_place_type_t<>.
+  // E.g. base::unique_any a(123);
+  template <typename T,
+            typename VT = std::decay_t<T>,
+            std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
+                             !internal::is_unique_any<VT>::value &&
+                             !is_in_place_type_t<VT>::value>* = nullptr>
+  unique_any(T&& value) noexcept : internal_(&TypeOpsHelper<VT>::type_ops) {
+    ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
+        &internal_, std::forward<T>(value));
+  }
+
+  // Constructs a base::unique_any containing an object of type T which is
+  // initialized by std::forward<Args>(args). E.g.
+  // base::unique_any a(base::in_place_type_t<std::unique_ptr<int>>(), 123);
+  template <
+      typename T,
+      typename... Args,
+      typename VT = std::decay_t<T>,
+      std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
+                       std::is_constructible<VT, Args...>::value>* = nullptr>
+  explicit unique_any(in_place_type_t<T> /*tag*/, Args&&... args) noexcept
+      : internal_(&TypeOpsHelper<VT>::type_ops) {
+    ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
+        &internal_, std::forward<Args>(args)...);
+  }
+
+  // Constructs a base::unique_any containing an object of type T which is
+  // initialized with a std::initializer_list<U> and std::forward for any
+  // remaining args. E.g. base::unique_any
+  // a(base::in_place_type_t<std::vector<int>>(), {1, 2, 3}); base::unique_any
+  // b(base::in_place_type_t<std::vector<int>>(), {1, 2, 3}, 4, 5);
+  template <typename T,
+            typename U,
+            typename... Args,
+            typename VT = std::decay_t<T>,
+            std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
+                             std::is_constructible<VT,
+                                                   std::initializer_list<U>&,
+                                                   Args...>::value>* = nullptr>
+  explicit unique_any(in_place_type_t<T> /*tag*/,
+                      std::initializer_list<U> ilist,
+                      Args&&... args) noexcept
+      : internal_(&internal::AnyInternal::TypeOpsHelper<VT>::type_ops) {
+    ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
+        &internal_, ilist, std::forward<Args>(args)...);
+  }
+
+  ~unique_any();
+
+  // Emplaces a value within a base::unique_any object by calling reset(),
+  // initializing the contained value as if direct-non-list-initializing an
+  // object of type |VT| with the arguments |std::forward<Args>(args)...|, and
+  // returning a reference to the new contained value.
+  // E.g.
+  // base::unique_any a;
+  // a.emplace<std::unique_ptr<int>>(123);
+  template <
+      typename T,
+      typename... Args,
+      typename VT = std::decay_t<T>,
+      std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
+                       std::is_constructible<VT, Args...>::value>* = nullptr>
+  VT& emplace(Args&&... args) noexcept {
+    reset();
+    ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
+        &internal_, std::forward<Args>(args)...);
+    internal_.type_ops_ = &TypeOpsHelper<VT>::type_ops;
+    return *internal_.GetStorage<VT>();
+  }
+
+  // Overload of |emplace()| to emplace a value within a |base::unique_any|
+  // object by calling |reset()|, initializing the contained value as if
+  // direct-non-list-initializing an object of type |VT| with the arguments
+  // |initializer_list, std::forward<Args>(args)...|, and returning a reference
+  // to the new contained value.
+  // E.g.
+  // base::unique_any a;
+  // a.emplace<std::vector<int>>({1, 2, 3});
+  template <typename T,
+            class U,
+            typename... Args,
+            typename VT = std::decay_t<T>,
+            std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
+                             std::is_constructible<VT,
+                                                   std::initializer_list<U>&,
+                                                   Args...>::value>* = nullptr>
+  VT& emplace(std::initializer_list<U> ilist, Args&&... args) noexcept {
+    reset();
+    ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
+        &internal_, ilist, std::forward<Args>(args)...);
+    internal_.type_ops_ = &TypeOpsHelper<VT>::type_ops;
+    return *internal_.GetStorage<VT>();
+  }
+
+  // Assigns |t| as long as |T| is move or copy constructible and it isn't
+  // base::unique_any.
+  template <typename T,
+            typename VT = std::decay_t<T>,
+            std::enable_if_t<is_move_or_copy_constructible<VT>::value &&
+                             !internal::is_unique_any<VT>::value>* = nullptr>
+  unique_any& operator=(T&& t) noexcept {
+    reset();
+    ConstructHelper<VT, InlineStorageHelper<VT>::kUseInlineStorage>::Construct(
+        &internal_, std::forward<T>(t));
+    internal_.type_ops_ = &TypeOpsHelper<VT>::type_ops;
+    return *this;
+  }
+
+  constexpr unique_any& operator=(unique_any&& other) noexcept {
+    internal_ = std::move(other.internal_);
+    return *this;
+  }
+
+  void swap(unique_any& other) noexcept {
+    using std::swap;
+    swap(internal_, other.internal_);
+  }
+
+  bool has_value() const noexcept { return internal_.has_value(); }
+
+  // Note unlike std::any we return TypeId which does not require RTTI.
+  TypeId type() const noexcept { return internal_.type(); }
+
+  void reset() noexcept { internal_.reset(); }
+
+ private:
+  template <typename ValueType>
+  friend ValueType unique_any_cast(const unique_any& any);
+
+  template <typename ValueType>
+  friend ValueType unique_any_cast(unique_any& any);
+
+  template <typename ValueType>
+  friend ValueType unique_any_cast(unique_any&& any);
+
+  template <typename ValueType>
+  friend const ValueType* unique_any_cast(const unique_any* any) noexcept;
+
+  template <typename ValueType>
+  friend ValueType* unique_any_cast(unique_any* any) noexcept;
+
+  internal::AnyInternal internal_;
+};
+
+// Swaps two base::Any values. Equivalent to |x.swap(y)| where |x| and |y| are
+// base::Any types.
+inline void swap(unique_any& x, unique_any& y) noexcept {
+  x.swap(y);
+}
+
+template <typename T, typename... Args>
+unique_any make_unique_any(Args&&... args) {
+  return unique_any(in_place_type_t<T>(), std::forward<Args>(args)...);
+}
+
+template <typename T, typename U, typename... Args>
+unique_any make_unique_any(std::initializer_list<U> il, Args&&... args) {
+  return unique_any(in_place_type_t<T>(), il, std::forward<Args>(args)...);
+}
+
+template <typename ValueType>
+ValueType unique_any_cast(const unique_any& any) {
+  using U = typename std::remove_cv<
+      typename std::remove_reference<ValueType>::type>::type;
+  static_assert(std::is_constructible<ValueType, const U&>::value,
+                "Invalid ValueType");
+  DCHECK(any.has_value());
+  CHECK_EQ(TypeId::From<U>(), any.type());
+  return *any.internal_.GetStorage<U>();
+}
+
+template <typename ValueType>
+ValueType unique_any_cast(unique_any& any) {
+  using U = typename std::remove_cv<
+      typename std::remove_reference<ValueType>::type>::type;
+  static_assert(std::is_constructible<ValueType, U&>::value,
+                "Invalid ValueType");
+  DCHECK(any.has_value());
+  CHECK_EQ(TypeId::From<U>(), any.type());
+  return *any.internal_.GetStorage<U>();
+}
+
+template <typename ValueType>
+ValueType unique_any_cast(unique_any&& any) {
+  using U = typename std::remove_cv<
+      typename std::remove_reference<ValueType>::type>::type;
+  static_assert(std::is_constructible<ValueType, U>::value,
+                "Invalid ValueType");
+  DCHECK(any.has_value());
+  CHECK_EQ(TypeId::From<U>(), any.type());
+  return std::move(*any.internal_.GetStorage<U>());
+}
+
+template <typename ValueType>
+const ValueType* unique_any_cast(const unique_any* any) noexcept {
+  using U = typename std::remove_cv<ValueType>::type;
+  if (TypeId::From<U>() != any->type())
+    return nullptr;
+  return any->internal_.GetStorage<U>();
+}
+
+template <typename ValueType>
+ValueType* unique_any_cast(unique_any* any) noexcept {
+  using U = typename std::remove_cv<ValueType>::type;
+  if (TypeId::From<U>() != any->type())
+    return nullptr;
+  return any->internal_.GetStorage<U>();
+}
+
+}  // namespace base
+
+#endif  // BASE_CONTAINERS_UNIQUE_ANY_H_
diff --git a/base/containers/unique_any_unittest.cc b/base/containers/unique_any_unittest.cc
new file mode 100644
index 0000000..7812b86
--- /dev/null
+++ b/base/containers/unique_any_unittest.cc
@@ -0,0 +1,634 @@
+// 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/containers/unique_any.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/test/copy_only_int.h"
+#include "base/test/gtest_util.h"
+#include "base/test/move_only_int.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// The first section of tests are imported from absl::any with modifications for
+// move only type support. Note tests dealing with exception have been omitted
+// because Chromium does not use exceptions.
+// See third_party/abseil-cpp/absl/types/any_test.cc
+namespace base {
+
+namespace {
+template <typename T>
+const T& AsConst(const T& t) {
+  return t;
+}
+
+struct MoveOnlyWithListConstructor {
+  MoveOnlyWithListConstructor() = default;
+  explicit MoveOnlyWithListConstructor(std::initializer_list<int> /*ilist*/,
+                                       int value)
+      : value(value) {}
+  MoveOnlyWithListConstructor(MoveOnlyWithListConstructor&&) = default;
+  MoveOnlyWithListConstructor& operator=(MoveOnlyWithListConstructor&&) =
+      default;
+
+  int value = 0;
+};
+
+struct IntMoveOnlyCopyOnlyInt {
+  IntMoveOnlyCopyOnlyInt(int value,
+                         MoveOnlyInt /*move_only*/,
+                         CopyOnlyInt /*copy_only*/)
+      : value(value) {}
+
+  int value;
+};
+
+struct ListMoveOnlyCopyOnlyInt {
+  ListMoveOnlyCopyOnlyInt(std::initializer_list<int> ilist,
+                          MoveOnlyInt /*move_only*/,
+                          CopyOnlyInt /*copy_only*/)
+      : values(ilist) {}
+
+  std::vector<int> values;
+};
+
+using FunctionType = void();
+void FunctionToEmplace() {}
+
+using ArrayType = int[2];
+using DecayedArray = std::decay_t<ArrayType>;
+
+struct Value {};
+
+}  // namespace
+
+TEST(UniqueAnyTest, Noexcept) {
+  static_assert(std::is_nothrow_default_constructible<unique_any>(), "");
+  static_assert(std::is_nothrow_move_constructible<unique_any>(), "");
+  static_assert(std::is_nothrow_move_assignable<unique_any>(), "");
+  static_assert(noexcept(std::declval<unique_any&>().has_value()), "");
+  static_assert(noexcept(std::declval<unique_any&>().type()), "");
+  static_assert(noexcept(unique_any_cast<int>(std::declval<unique_any*>())),
+                "");
+  static_assert(
+      noexcept(std::declval<unique_any&>().swap(std::declval<unique_any&>())),
+      "");
+
+  using std::swap;
+  static_assert(
+      noexcept(swap(std::declval<unique_any&>(), std::declval<unique_any&>())),
+      "");
+}
+
+TEST(UniqueAnyTest, HasValue) {
+  unique_any o;
+  EXPECT_FALSE(o.has_value());
+  o.emplace<int>();
+  EXPECT_TRUE(o.has_value());
+  o.reset();
+  EXPECT_FALSE(o.has_value());
+}
+
+TEST(UniqueAnyTest, TypeId) {
+  unique_any a;
+  EXPECT_EQ(a.type(), TypeId::From<void>());
+
+  a = 123;
+  EXPECT_EQ(a.type(), TypeId::From<int>());
+
+  a = 123.0f;
+  EXPECT_EQ(a.type(), TypeId::From<float>());
+
+  a = true;
+  EXPECT_EQ(a.type(), TypeId::From<bool>());
+
+  a = std::string("test");
+  EXPECT_EQ(a.type(), TypeId::From<std::string>());
+
+  a.reset();
+  EXPECT_EQ(a.type(), TypeId::From<void>());
+}
+
+TEST(UniqueAnyTest, EmptyPointerCast) {
+  // pointer-to-unqualified overload
+  {
+    unique_any o;
+    EXPECT_EQ(nullptr, unique_any_cast<int>(&o));
+    o.emplace<int>();
+    EXPECT_NE(nullptr, unique_any_cast<int>(&o));
+    o.reset();
+    EXPECT_EQ(nullptr, unique_any_cast<int>(&o));
+  }
+
+  // pointer-to-const overload
+  {
+    unique_any o;
+    EXPECT_EQ(nullptr, unique_any_cast<int>(&AsConst(o)));
+    o.emplace<int>();
+    EXPECT_NE(nullptr, unique_any_cast<int>(&AsConst(o)));
+    o.reset();
+    EXPECT_EQ(nullptr, unique_any_cast<int>(&AsConst(o)));
+  }
+}
+
+TEST(UniqueAnyTest, InPlaceConstruction) {
+  const CopyOnlyInt copy_only{};
+  unique_any o(in_place_type_t<IntMoveOnlyCopyOnlyInt>(), 5, MoveOnlyInt(),
+               copy_only);
+  IntMoveOnlyCopyOnlyInt& v = unique_any_cast<IntMoveOnlyCopyOnlyInt&>(o);
+  EXPECT_EQ(5, v.value);
+}
+
+TEST(UniqueAnyTest, InPlaceConstructionWithCV) {
+  const CopyOnlyInt copy_only{};
+  unique_any o(in_place_type_t<const volatile IntMoveOnlyCopyOnlyInt>(), 5,
+               MoveOnlyInt(), copy_only);
+  IntMoveOnlyCopyOnlyInt& v = unique_any_cast<IntMoveOnlyCopyOnlyInt&>(o);
+  EXPECT_EQ(5, v.value);
+}
+
+TEST(UniqueAnyTest, InPlaceConstructionWithFunction) {
+  unique_any o(in_place_type_t<FunctionType>(), FunctionToEmplace);
+  FunctionType*& construction_result = unique_any_cast<FunctionType*&>(o);
+  EXPECT_EQ(&FunctionToEmplace, construction_result);
+}
+
+TEST(UniqueAnyTest, InPlaceConstructionWithArray) {
+  ArrayType ar = {5, 42};
+  unique_any o(in_place_type_t<ArrayType>(), ar);
+  DecayedArray& construction_result = unique_any_cast<DecayedArray&>(o);
+  EXPECT_EQ(&ar[0], construction_result);
+}
+
+TEST(UniqueAnyTest, InPlaceConstructionIlist) {
+  const CopyOnlyInt copy_only{};
+  unique_any o(in_place_type_t<ListMoveOnlyCopyOnlyInt>(), {1, 2, 3, 4},
+               MoveOnlyInt(), copy_only);
+  ListMoveOnlyCopyOnlyInt& v = unique_any_cast<ListMoveOnlyCopyOnlyInt&>(o);
+  std::vector<int> expected_values = {1, 2, 3, 4};
+  EXPECT_EQ(expected_values, v.values);
+}
+
+TEST(UniqueAnyTest, InPlaceConstructionIlistWithCV) {
+  const CopyOnlyInt copy_only{};
+  unique_any o(in_place_type_t<const volatile ListMoveOnlyCopyOnlyInt>(),
+               {1, 2, 3, 4}, MoveOnlyInt(), copy_only);
+  ListMoveOnlyCopyOnlyInt& v = unique_any_cast<ListMoveOnlyCopyOnlyInt&>(o);
+  std::vector<int> expected_values = {1, 2, 3, 4};
+  EXPECT_EQ(expected_values, v.values);
+}
+
+TEST(UniqueAnyTest, InPlaceNoArgs) {
+  unique_any o(in_place_type_t<int>{});
+  EXPECT_EQ(0, unique_any_cast<int&>(o));
+}
+
+template <typename Enabler, typename T, typename... Args>
+struct CanEmplaceAnyImpl : std::false_type {};
+
+template <typename T, typename... Args>
+struct CanEmplaceAnyImpl<void_t<decltype(std::declval<unique_any&>().emplace<T>(
+                             std::declval<Args>()...))>,
+                         T,
+                         Args...> : std::true_type {};
+
+template <typename T, typename... Args>
+using CanEmplaceAny = CanEmplaceAnyImpl<void, T, Args...>;
+
+TEST(UniqueAnyTest, Emplace) {
+  const CopyOnlyInt copy_only{};
+  unique_any o;
+  EXPECT_TRUE((std::is_same<decltype(o.emplace<IntMoveOnlyCopyOnlyInt>(
+                                5, MoveOnlyInt(), copy_only)),
+                            IntMoveOnlyCopyOnlyInt&>::value));
+  IntMoveOnlyCopyOnlyInt& emplace_result =
+      o.emplace<IntMoveOnlyCopyOnlyInt>(5, MoveOnlyInt(), copy_only);
+  EXPECT_EQ(5, emplace_result.value);
+  IntMoveOnlyCopyOnlyInt& v = unique_any_cast<IntMoveOnlyCopyOnlyInt&>(o);
+  EXPECT_EQ(5, v.value);
+  EXPECT_EQ(&emplace_result, &v);
+
+  static_assert(!CanEmplaceAny<int, int, int>::value, "Too many parameters");
+  static_assert(CanEmplaceAny<MoveOnlyInt, MoveOnlyInt>::value,
+                "Can't emplace move only type");
+}
+
+TEST(UniqueAnyTest, EmplaceWithCV) {
+  const CopyOnlyInt copy_only{};
+  unique_any o;
+  EXPECT_TRUE(
+      (std::is_same<decltype(o.emplace<const volatile IntMoveOnlyCopyOnlyInt>(
+                        5, MoveOnlyInt(), copy_only)),
+                    IntMoveOnlyCopyOnlyInt&>::value));
+  IntMoveOnlyCopyOnlyInt& emplace_result =
+      o.emplace<const volatile IntMoveOnlyCopyOnlyInt>(5, MoveOnlyInt(),
+                                                       copy_only);
+  EXPECT_EQ(5, emplace_result.value);
+  IntMoveOnlyCopyOnlyInt& v = unique_any_cast<IntMoveOnlyCopyOnlyInt&>(o);
+  EXPECT_EQ(5, v.value);
+  EXPECT_EQ(&emplace_result, &v);
+}
+
+TEST(UniqueAnyTest, EmplaceWithFunction) {
+  unique_any o;
+  EXPECT_TRUE(
+      (std::is_same<decltype(o.emplace<FunctionType>(FunctionToEmplace)),
+                    FunctionType*&>::value));
+  FunctionType*& emplace_result = o.emplace<FunctionType>(FunctionToEmplace);
+  EXPECT_EQ(&FunctionToEmplace, emplace_result);
+}
+
+TEST(UniqueAnyTest, EmplaceWithArray) {
+  unique_any o;
+  ArrayType ar = {5, 42};
+  EXPECT_TRUE(
+      (std::is_same<decltype(o.emplace<ArrayType>(ar)), DecayedArray&>::value));
+  DecayedArray& emplace_result = o.emplace<ArrayType>(ar);
+  EXPECT_EQ(&ar[0], emplace_result);
+}
+
+TEST(UniqueAnyTest, EmplaceIlist) {
+  const CopyOnlyInt copy_only{};
+  unique_any o;
+  EXPECT_TRUE((std::is_same<decltype(o.emplace<ListMoveOnlyCopyOnlyInt>(
+                                {1, 2, 3, 4}, MoveOnlyInt(), copy_only)),
+                            ListMoveOnlyCopyOnlyInt&>::value));
+  ListMoveOnlyCopyOnlyInt& emplace_result = o.emplace<ListMoveOnlyCopyOnlyInt>(
+      {1, 2, 3, 4}, MoveOnlyInt(), copy_only);
+  ListMoveOnlyCopyOnlyInt& v = unique_any_cast<ListMoveOnlyCopyOnlyInt&>(o);
+  EXPECT_EQ(&v, &emplace_result);
+  std::vector<int> expected_values = {1, 2, 3, 4};
+  EXPECT_EQ(expected_values, v.values);
+
+  static_assert(!CanEmplaceAny<int, std::initializer_list<int>>::value,
+                "Too many parameters");
+  static_assert(CanEmplaceAny<MoveOnlyWithListConstructor,
+                              std::initializer_list<int>, int>::value,
+                "Can emplace move only type");
+}
+
+TEST(UniqueAnyTest, EmplaceIlistWithCV) {
+  const CopyOnlyInt copy_only{};
+  unique_any o;
+  EXPECT_TRUE(
+      (std::is_same<decltype(o.emplace<const volatile ListMoveOnlyCopyOnlyInt>(
+                        {1, 2, 3, 4}, MoveOnlyInt(), copy_only)),
+                    ListMoveOnlyCopyOnlyInt&>::value));
+  ListMoveOnlyCopyOnlyInt& emplace_result =
+      o.emplace<const volatile ListMoveOnlyCopyOnlyInt>(
+          {1, 2, 3, 4}, MoveOnlyInt(), copy_only);
+  ListMoveOnlyCopyOnlyInt& v = unique_any_cast<ListMoveOnlyCopyOnlyInt&>(o);
+  EXPECT_EQ(&v, &emplace_result);
+  std::vector<int> expected_values = {1, 2, 3, 4};
+  EXPECT_EQ(expected_values, v.values);
+}
+
+TEST(UniqueAnyTest, EmplaceNoArgs) {
+  unique_any o;
+  o.emplace<int>();
+  EXPECT_EQ(0, unique_any_cast<int>(o));
+}
+
+TEST(UniqueAnyTest, ConversionConstruction) {
+  {
+    unique_any o = 5;
+    EXPECT_EQ(5, unique_any_cast<int>(o));
+  }
+
+  {
+    const CopyOnlyInt copy_only(5);
+    unique_any o = copy_only;
+    EXPECT_EQ(5, unique_any_cast<CopyOnlyInt&>(o).data());
+  }
+
+  {
+    MoveOnlyInt i{123};
+    unique_any o(std::move(i));
+    EXPECT_EQ(123, unique_any_cast<MoveOnlyInt&>(o).data());
+  }
+}
+
+TEST(UniqueAnyTest, ConversionAssignment) {
+  {
+    unique_any o;
+    o = 5;
+    EXPECT_EQ(5, unique_any_cast<int>(o));
+  }
+
+  {
+    const CopyOnlyInt copy_only(5);
+    unique_any o;
+    o = copy_only;
+    EXPECT_EQ(5, unique_any_cast<CopyOnlyInt&>(o).data());
+  }
+
+  {
+    unique_any o;
+    MoveOnlyInt i{123};
+    o = std::move(i);
+    EXPECT_EQ(123, unique_any_cast<MoveOnlyInt&>(o).data());
+  }
+}
+
+// Suppress MSVC warnings.
+// 4521: multiple copy constructors specified
+// We wrote multiple of them to test that the correct overloads are selected.
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4521)
+#endif
+
+// Weird type for testing, only used to make sure we "properly" perfect-forward
+// when being placed into an unique_any (use the l-value constructor if given
+// an l-value rather than use the copy constructor).
+struct WeirdConstructor42 {
+  explicit WeirdConstructor42(int value) : value(value) {}
+
+  // Copy-constructor
+  WeirdConstructor42(const WeirdConstructor42& other) : value(other.value) {}
+
+  // L-value "weird" constructor (used when given an l-value)
+  WeirdConstructor42(
+      WeirdConstructor42& /*other*/)  // NOLINT(runtime/references)
+      : value(42) {}
+
+  int value;
+};
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+TEST(UniqueAnyTest, WeirdConversionConstruction) {
+  {
+    const WeirdConstructor42 source(5);
+    unique_any o = source;  // Actual copy
+    EXPECT_EQ(5, unique_any_cast<WeirdConstructor42&>(o).value);
+  }
+
+  {
+    WeirdConstructor42 source(5);
+    unique_any o = source;  // Weird "conversion"
+    EXPECT_EQ(42, unique_any_cast<WeirdConstructor42&>(o).value);
+  }
+}
+
+TEST(UniqueAnyTest, WeirdConversionAssignment) {
+  {
+    const WeirdConstructor42 source(5);
+    unique_any o;
+    o = source;  // Actual copy
+    EXPECT_EQ(5, unique_any_cast<WeirdConstructor42&>(o).value);
+  }
+
+  {
+    WeirdConstructor42 source(5);
+    unique_any o;
+    o = source;  // Weird "conversion"
+    EXPECT_EQ(42, unique_any_cast<WeirdConstructor42&>(o).value);
+  }
+}
+
+TEST(UniqueAnyTest, AnyCastValue) {
+  {
+    unique_any o;
+    o.emplace<int>(5);
+    EXPECT_EQ(5, unique_any_cast<int>(o));
+    EXPECT_EQ(5, unique_any_cast<int>(AsConst(o)));
+    static_assert(
+        std::is_same<decltype(unique_any_cast<Value>(o)), Value>::value, "");
+  }
+
+  {
+    unique_any o;
+    o.emplace<int>(5);
+    EXPECT_EQ(5, unique_any_cast<const int>(o));
+    EXPECT_EQ(5, unique_any_cast<const int>(AsConst(o)));
+    static_assert(std::is_same<decltype(unique_any_cast<const Value>(o)),
+                               const Value>::value,
+                  "");
+  }
+
+  {
+    unique_any a = std::make_unique<int>(1234);
+    std::unique_ptr<int> b =
+        unique_any_cast<std::unique_ptr<int>>(std::move(a));
+    EXPECT_EQ(1234, *b);
+  }
+}
+
+TEST(UniqueAnyTest, AnyCastReference) {
+  {
+    unique_any o;
+    o.emplace<int>(5);
+    EXPECT_EQ(5, unique_any_cast<int&>(o));
+    EXPECT_EQ(5, unique_any_cast<const int&>(AsConst(o)));
+    static_assert(
+        std::is_same<decltype(unique_any_cast<Value&>(o)), Value&>::value, "");
+  }
+
+  {
+    unique_any o;
+    o.emplace<int>(5);
+    EXPECT_EQ(5, unique_any_cast<const int>(o));
+    EXPECT_EQ(5, unique_any_cast<const int>(AsConst(o)));
+    static_assert(std::is_same<decltype(unique_any_cast<const Value&>(o)),
+                               const Value&>::value,
+                  "");
+  }
+
+  {
+    unique_any o;
+    o.emplace<int>(5);
+    EXPECT_EQ(5, unique_any_cast<int&&>(std::move(o)));
+    static_assert(std::is_same<decltype(unique_any_cast<Value&&>(std::move(o))),
+                               Value&&>::value,
+                  "");
+  }
+
+  {
+    unique_any o;
+    o.emplace<int>(5);
+    EXPECT_EQ(5, unique_any_cast<const int>(std::move(o)));
+    static_assert(
+        std::is_same<decltype(unique_any_cast<const Value&&>(std::move(o))),
+                     const Value&&>::value,
+        "");
+  }
+}
+
+TEST(UniqueAnyTest, AnyCastPointer) {
+  {
+    unique_any o;
+    EXPECT_EQ(nullptr, unique_any_cast<char>(&o));
+    EXPECT_EQ(nullptr, unique_any_cast<char>(&o));
+    o.emplace<char>('a');
+    EXPECT_EQ('a', *unique_any_cast<char>(&o));
+    static_assert(
+        std::is_same<decltype(unique_any_cast<Value>(&o)), Value*>::value, "");
+  }
+
+  {
+    unique_any o;
+    EXPECT_EQ(nullptr, unique_any_cast<const char>(&o));
+    o.emplace<int>(5);
+    EXPECT_EQ(nullptr, unique_any_cast<const char>(&o));
+    o.emplace<char>('a');
+    EXPECT_EQ('a', *unique_any_cast<const char>(&o));
+    static_assert(std::is_same<decltype(unique_any_cast<const Value>(&o)),
+                               const Value*>::value,
+                  "");
+  }
+}
+
+TEST(UniqueAnyTest, MakeAny) {
+  const CopyOnlyInt copy_only{};
+  auto o = base::make_unique_any<IntMoveOnlyCopyOnlyInt>(5, MoveOnlyInt(),
+                                                         copy_only);
+  static_assert(std::is_same<decltype(o), unique_any>::value, "");
+  EXPECT_EQ(5, unique_any_cast<IntMoveOnlyCopyOnlyInt&>(o).value);
+}
+
+TEST(UniqueAnyTest, MakeAnyIList) {
+  const CopyOnlyInt copy_only{};
+  auto o = base::make_unique_any<ListMoveOnlyCopyOnlyInt>(
+      {1, 2, 3}, MoveOnlyInt(), copy_only);
+  static_assert(std::is_same<decltype(o), unique_any>::value, "");
+  ListMoveOnlyCopyOnlyInt& v = unique_any_cast<ListMoveOnlyCopyOnlyInt&>(o);
+  std::vector<int> expected_values = {1, 2, 3};
+  EXPECT_EQ(expected_values, v.values);
+
+  base::unique_any a = base::make_unique_any<std::vector<int>>({1, 2, 3, 4});
+  EXPECT_EQ(4u, unique_any_cast<std::vector<int>>(a).size());
+}
+
+TEST(UniqueAnyTest, Reset) {
+  unique_any o;
+  o.emplace<int>();
+
+  o.reset();
+  EXPECT_FALSE(o.has_value());
+
+  o.emplace<char>();
+  EXPECT_TRUE(o.has_value());
+}
+
+TEST(UniqueAnyTest, ConversionConstructionCausesOneCopy) {
+  CopyOnlyInt::reset_num_copies();
+  CopyOnlyInt counter(5);
+  unique_any o(counter);
+  EXPECT_EQ(5, unique_any_cast<CopyOnlyInt&>(o).data());
+  EXPECT_EQ(1, CopyOnlyInt::num_copies());
+}
+
+// Start of chromium specific tests:
+namespace {
+
+class DestructDetector {
+ public:
+  explicit DestructDetector(bool* destructor_called)
+      : destructor_called_(destructor_called) {}
+
+  ~DestructDetector() { *destructor_called_ = true; }
+
+ private:
+  bool* destructor_called_;  // NOT OWNED
+};
+
+}  // namespace
+
+TEST(UniqueAnyTest, DestructorCalled) {
+  bool destructor_called = false;
+
+  {
+    unique_any a;
+    a.emplace<DestructDetector>(&destructor_called);
+    EXPECT_FALSE(destructor_called);
+  }
+
+  EXPECT_TRUE(destructor_called);
+}
+
+TEST(UniqueAnyTest, DestructorCalledOnAssignment) {
+  bool destructor_called = false;
+
+  unique_any a;
+  a.emplace<DestructDetector>(&destructor_called);
+
+  EXPECT_FALSE(destructor_called);
+  a = 123;
+  EXPECT_TRUE(destructor_called);
+}
+
+TEST(UniqueAnyTest, MoveAssignment) {
+  unique_any a(std::make_unique<int>(1234));
+  unique_any b;
+
+  b = std::move(a);
+
+  // The state of |a| is undefined here. The unique_ptr should have been moved
+  // from however.
+  EXPECT_TRUE(b.has_value());
+  EXPECT_EQ(nullptr, unique_any_cast<std::unique_ptr<int>&>(a));
+  EXPECT_EQ(1234, *unique_any_cast<std::unique_ptr<int>&>(b));
+}
+
+TEST(UniqueAnyTest, MoveConstructor) {
+  unique_any a(std::make_unique<int>(1234));
+  unique_any b(std::move(a));
+  // The state of |a| is undefined here. The unique_ptr should have been moved
+  // from however.
+  EXPECT_TRUE(b.has_value());
+  EXPECT_EQ(nullptr, unique_any_cast<std::unique_ptr<int>&>(a));
+  EXPECT_EQ(1234, *unique_any_cast<std::unique_ptr<int>&>(b));
+}
+
+TEST(UniqueAnyTest, MoveOnlyInt) {
+  unique_any a;
+  a = MoveOnlyInt(1234);
+
+  EXPECT_EQ(1234, unique_any_cast<MoveOnlyInt&>(a).data());
+
+  unique_any b;
+  b = std::move(a);
+  EXPECT_EQ(1234, unique_any_cast<MoveOnlyInt&>(b).data());
+}
+
+TEST(UniqueAnyTest, SwapEmptySmall) {
+  unique_any a;
+  unique_any b(123);
+
+  swap(a, b);
+
+  EXPECT_TRUE(a.has_value());
+  EXPECT_EQ(123, unique_any_cast<int>(a));
+  EXPECT_FALSE(b.has_value());
+
+  std::swap(a, b);
+
+  EXPECT_FALSE(a.has_value());
+  EXPECT_TRUE(b.has_value());
+  EXPECT_EQ(123, unique_any_cast<int>(b));
+}
+
+TEST(UniqueAnyTest, SwapEmptyLarge) {
+  unique_any a;
+  unique_any b(std::string("hello"));
+
+  swap(a, b);
+
+  EXPECT_TRUE(a.has_value());
+  EXPECT_EQ("hello", unique_any_cast<std::string>(a));
+  EXPECT_FALSE(b.has_value());
+
+  std::swap(a, b);
+
+  EXPECT_FALSE(a.has_value());
+  EXPECT_TRUE(b.has_value());
+  EXPECT_EQ("hello", unique_any_cast<std::string>(b));
+}
+
+}  // namespace base
diff --git a/base/containers/unique_any_unittest.nc b/base/containers/unique_any_unittest.nc
new file mode 100644
index 0000000..5fa9e1d
--- /dev/null
+++ b/base/containers/unique_any_unittest.nc
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is a "No Compile Test" suite.
+// http://dev.chromium.org/developers/testing/no-compile-tests
+
+#include "base/containers/unique_any.h"
+#include "base/test/move_only_int.h"
+
+namespace base {
+
+#if defined(NCTEST_UNIQUE_ANY_CANT_COPY_CONSTRUCT)  // [r"fatal error: call to implicitly-deleted copy constructor of 'base::unique_any'"]
+
+void WontCompile() {
+  unique_any a(1);
+  unique_any b(a);
+}
+
+#elif defined(NCTEST_UNIQUE_ANY_CANT_COPY_ASSIGN)  // [r"fatal error: object of type 'base::unique_any' cannot be assigned because its copy assignment operator is implicitly deleted"]
+
+void WontCompile() {
+  unique_any a;
+  unique_any b(1);
+  a = b;
+}
+
+#elif defined(NCTEST_UNIQUE_ANY_CAST_WITH_NON_REFERENCE_TYPE)  // [r"fatal error: static_assert failed due to requirement 'std::is_constructible.*\"Invalid ValueType\""]
+
+void WontCompile() {
+  unique_any a(MoveOnlyInt{});
+
+  unique_any_cast<MoveOnlyInt>(a);  // Fails because this would return a copy.
+}
+
+#endif
+
+}  // namespace base
diff --git a/base/message_loop/message_pump.h b/base/message_loop/message_pump.h
index 1ff0a75..012bafa 100644
--- a/base/message_loop/message_pump.h
+++ b/base/message_loop/message_pump.h
@@ -6,8 +6,10 @@
 #define BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_
 
 #include "base/base_export.h"
+#include "base/logging.h"
 #include "base/message_loop/timer_slack.h"
 #include "base/sequence_checker.h"
+#include "base/time/time.h"
 
 namespace base {
 
@@ -24,10 +26,48 @@
     // Called before work performed internal to the message pump is executed.
     virtual void BeforeDoInternalWork();
 
+    struct NextWorkInfo {
+      // Helper to extract a TimeDelta for pumps that need a
+      // timeout-till-next-task.
+      TimeDelta remaining_delay() const {
+        DCHECK(!delayed_run_time.is_null() && !delayed_run_time.is_max());
+        DCHECK_GE(TimeTicks::Now(), recent_now);
+        return delayed_run_time - recent_now;
+      }
+
+      // Helper to verify if the next task is ready right away.
+      bool is_immediate() const { return delayed_run_time.is_null(); }
+
+      // The next PendingTask's |delayed_run_time|. is_null() if there's extra
+      // work to run immediately. is_max() if there are no more immediate nor
+      // delayed tasks.
+      TimeTicks delayed_run_time;
+
+      // A recent view of TimeTicks::Now(). Only valid if |next_task_run_time|
+      // isn't null nor max. MessagePump impls should use remaining_delay()
+      // instead of resampling Now() if they wish to sleep for a TimeDelta.
+      TimeTicks recent_now;
+    };
+
+    // The latest model of MessagePumps will invoke this instead of
+    // DoWork()/DoDelayedWork(). Executes an immediate task or a ripe delayed
+    // task. Returns a struct which indicates |delayed_run_time|. DoSomeWork()
+    // will be invoked again shortly if is_immediate(); it will be invoked after
+    // |delayed_run_time| (or ScheduleWork()) if there isn't immediate work and
+    // |!delayed_run_time.is_max()|; and it will not be invoked again until
+    // ScheduleWork() otherwise. Redundant/spurious invocations outside of those
+    // guarantees are not impossible however. DoIdleWork() will not be called so
+    // long as this returns a null |delayed_run_time|. See design doc for
+    // details :
+    // https://docs.google.com/document/d/1no1JMli6F1r8gTF9KDIOvoWkUUZcXDktPf4A1IXYc3M/edit#
+    virtual NextWorkInfo DoSomeWork() = 0;
+
     // Called from within Run in response to ScheduleWork or when the message
     // pump would otherwise call DoDelayedWork.  Returns true to indicate that
     // work was done.  DoDelayedWork will still be called if DoWork returns
     // true, but DoIdleWork will not.
+    // Used in conjunction with DoDelayedWork() by old MessagePumps.
+    // TODO(gab): Migrate such pumps to DoSomeWork().
     virtual bool DoWork() = 0;
 
     // Called from within Run in response to ScheduleDelayedWork or when the
@@ -38,6 +78,8 @@
     // |next_delayed_work_time| is null (per Time::is_null), then the queue of
     // future delayed work (timer events) is currently empty, and no additional
     // calls to this function need to be scheduled.
+    // Used in conjunction with DoWork() by old MessagePumps.
+    // TODO(gab): Migrate such pumps to DoSomeWork().
     virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 0;
 
     // Called from within Run just before the message pump goes to sleep.
@@ -125,6 +167,8 @@
   // Schedule a DoDelayedWork callback to happen at the specified time,
   // cancelling any pending DoDelayedWork callback.  This method may only be
   // used on the thread that called Run.
+  // TODO(gab): This method is obsolete in the DoSomeWork() variant, remove it
+  // once the migration is complete.
   virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0;
 
   // Sets the timer slack to the specified value.
diff --git a/base/message_loop/message_pump_default.cc b/base/message_loop/message_pump_default.cc
index 30ed2bf..b396841 100644
--- a/base/message_loop/message_pump_default.cc
+++ b/base/message_loop/message_pump_default.cc
@@ -36,32 +36,25 @@
     mac::ScopedNSAutoreleasePool autorelease_pool;
 #endif
 
-    bool did_work = delegate->DoWork();
+    Delegate::NextWorkInfo next_work_info = delegate->DoSomeWork();
+    bool has_more_immediate_work = next_work_info.is_immediate();
     if (!keep_running_)
       break;
 
-    did_work |= delegate->DoDelayedWork(&delayed_work_time_);
-    if (!keep_running_)
-      break;
-
-    if (did_work)
+    if (has_more_immediate_work)
       continue;
 
-    did_work = delegate->DoIdleWork();
+    has_more_immediate_work = delegate->DoIdleWork();
     if (!keep_running_)
       break;
 
-    if (did_work)
+    if (has_more_immediate_work)
       continue;
 
-    if (delayed_work_time_.is_null()) {
+    if (next_work_info.delayed_run_time.is_max()) {
       event_.Wait();
     } else {
-      // No need to handle already expired |delayed_work_time_| in any special
-      // way. When |delayed_work_time_| is in the past TimeWaitUntil returns
-      // promptly and |delayed_work_time_| will re-initialized on a next
-      // DoDelayedWork call which has to be called in order to get here again.
-      event_.TimedWaitUntil(delayed_work_time_);
+      event_.TimedWait(next_work_info.remaining_delay());
     }
     // Since event_ is auto-reset, we don't need to do anything special here
     // other than service each delegate method.
@@ -80,10 +73,11 @@
 
 void MessagePumpDefault::ScheduleDelayedWork(
     const TimeTicks& delayed_work_time) {
-  // We know that we can't be blocked on Wait right now since this method can
-  // only be called on the same thread as Run, so we only need to update our
-  // record of how long to sleep when we do sleep.
-  delayed_work_time_ = delayed_work_time;
+  // Since this is always called from the same thread as Run(), there is nothing
+  // to do as the loop is already running. It will wait in Run() with the
+  // correct timeout when it's out of immediate tasks.
+  // TODO(gab): Consider removing ScheduleDelayedWork() when all pumps function
+  // this way (bit.ly/merge-message-pump-do-work).
 }
 
 #if defined(OS_MACOSX)
diff --git a/base/message_loop/message_pump_default.h b/base/message_loop/message_pump_default.h
index dd11adc..b67661c 100644
--- a/base/message_loop/message_pump_default.h
+++ b/base/message_loop/message_pump_default.h
@@ -35,9 +35,6 @@
   // Used to sleep until there is more work to do.
   WaitableEvent event_;
 
-  // The time at which we should call DoDelayedWork.
-  TimeTicks delayed_work_time_;
-
   DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault);
 };
 
diff --git a/base/message_loop/message_pump_unittest.cc b/base/message_loop/message_pump_unittest.cc
index 14250f0..d5f4e43 100644
--- a/base/message_loop/message_pump_unittest.cc
+++ b/base/message_loop/message_pump_unittest.cc
@@ -3,9 +3,11 @@
 // found in the LICENSE file.
 
 #include "base/message_loop/message_pump.h"
+
 #include "base/message_loop/message_loop.h"
 #include "base/test/bind_test_util.h"
 #include "base/threading/thread.h"
+#include "build/build_config.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -15,13 +17,52 @@
 using ::testing::Return;
 
 namespace base {
+
 namespace {
 
+bool PumpTypeUsesDoSomeWork(MessageLoopBase::Type type) {
+  switch (type) {
+    case MessageLoopBase::Type::TYPE_DEFAULT:
+#if defined(OS_IOS)
+      // iOS uses a MessagePumpCFRunLoop instead of MessagePumpDefault for
+      // TYPE_DEFAULT. TODO(gab): migrate MessagePumpCFRunLoop too.
+      return false;
+#else
+      return true;
+#endif
+    case MessageLoopBase::Type::TYPE_UI:
+#if defined(OS_IOS)
+      // iOS uses a MessagePumpDefault for UI in unit tests, ref.
+      // test_support_ios.mm::CreateMessagePumpForUIForTests().
+      return true;
+#else
+      // TODO(gab): Complete migration of all UI pumps to DoSomeWork() as part
+      // of crbug.com/885371.
+      return false;
+#endif
+    case MessageLoopBase::Type::TYPE_IO:
+      // TODO(gab): Complete migration of all IO pumps to DoSomeWork() as part
+      // of crbug.com/885371.
+      return false;
+
+    case MessageLoopBase::Type::TYPE_CUSTOM:
+#if defined(OS_ANDROID)
+    case MessageLoopBase::Type::TYPE_JAVA:
+#endif  // defined(OS_ANDROID)
+      // Not tested in this file.
+      NOTREACHED();
+      return false;
+  }
+  NOTREACHED();
+  return false;
+}
+
 class MockMessagePumpDelegate : public MessagePump::Delegate {
  public:
   MockMessagePumpDelegate() = default;
 
   // MessagePump::Delegate:
+  MOCK_METHOD0(DoSomeWork, MessagePump::Delegate::NextWorkInfo());
   MOCK_METHOD0(DoWork, bool());
   MOCK_METHOD1(DoDelayedWork, bool(TimeTicks*));
   MOCK_METHOD0(DoIdleWork, bool());
@@ -36,17 +77,28 @@
       : message_pump_(MessageLoop::CreateMessagePumpForType(GetParam())) {}
 
  protected:
+  const bool pump_uses_do_some_work_ = PumpTypeUsesDoSomeWork(GetParam());
+
   std::unique_ptr<MessagePump> message_pump_;
 };
 
+}  // namespace
+
 TEST_P(MessagePumpTest, QuitStopsWork) {
   testing::StrictMock<MockMessagePumpDelegate> delegate;
 
   // Not expecting any calls to DoDelayedWork or DoIdleWork after quitting.
-  EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
-    message_pump_->Quit();
-    return false;
-  }));
+  if (pump_uses_do_some_work_) {
+    EXPECT_CALL(delegate, DoSomeWork).WillOnce(Invoke([this] {
+      message_pump_->Quit();
+      return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
+    }));
+  } else {
+    EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
+      message_pump_->Quit();
+      return false;
+    }));
+  }
   EXPECT_CALL(delegate, DoDelayedWork(_)).Times(0);
   EXPECT_CALL(delegate, DoIdleWork()).Times(0);
 
@@ -63,30 +115,54 @@
   // nested loop exits, we schedule another DoWork which quits the outer
   // (original) run loop. The test verifies that there are no extra calls to
   // DoWork after the outer loop quits.
-  EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([&] {
-    message_pump_->ScheduleWork();
-    message_pump_->Run(&nested_delegate);
-    message_pump_->ScheduleWork();
-    return false;
-  }));
-  EXPECT_CALL(nested_delegate, DoWork).WillOnce(Invoke([&] {
-    // Quit the nested run loop.
-    message_pump_->Quit();
-    return false;
-  }));
-  EXPECT_CALL(delegate, DoDelayedWork(_)).WillOnce(Return(false));
+  if (pump_uses_do_some_work_) {
+    EXPECT_CALL(delegate, DoSomeWork).WillOnce(Invoke([&] {
+      message_pump_->ScheduleWork();
+      message_pump_->Run(&nested_delegate);
+      message_pump_->ScheduleWork();
+      return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
+    }));
+    EXPECT_CALL(nested_delegate, DoSomeWork).WillOnce(Invoke([&] {
+      // Quit the nested run loop.
+      message_pump_->Quit();
+      return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
+    }));
+  } else {
+    EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([&] {
+      message_pump_->ScheduleWork();
+      message_pump_->Run(&nested_delegate);
+      message_pump_->ScheduleWork();
+      return false;
+    }));
+    EXPECT_CALL(nested_delegate, DoWork).WillOnce(Invoke([&] {
+      // Quit the nested run loop.
+      message_pump_->Quit();
+      return false;
+    }));
+    EXPECT_CALL(delegate, DoDelayedWork(_)).WillOnce(Return(false));
+  }
+
   // The outer pump may or may not trigger idle work at this point.
   EXPECT_CALL(delegate, DoIdleWork()).Times(AnyNumber());
-  EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([&] {
-    // Quit the original run loop.
-    message_pump_->Quit();
-    return false;
-  }));
+
+  if (pump_uses_do_some_work_) {
+    EXPECT_CALL(delegate, DoSomeWork).WillOnce(Invoke([this] {
+      message_pump_->Quit();
+      return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
+    }));
+  } else {
+    EXPECT_CALL(delegate, DoWork).WillOnce(Invoke([this] {
+      message_pump_->Quit();
+      return false;
+    }));
+  }
 
   message_pump_->ScheduleWork();
   message_pump_->Run(&delegate);
 }
 
+namespace {
+
 class TimerSlackTestDelegate : public MessagePump::Delegate {
  public:
   TimerSlackTestDelegate(MessagePump* message_pump)
@@ -101,14 +177,34 @@
     action_.store(NONE);
   }
 
+  MessagePump::Delegate::NextWorkInfo DoSomeWork() override {
+    switch (action_.load()) {
+      case NONE:
+        break;
+      case SCHEDULE_DELAYED_WORK: {
+        // After being woken up by the other thread, we let the pump know that
+        // the next delayed task is in fact much sooner than the 1 hour delay it
+        // was aware of. If the pump refreshes its timer correctly, it will wake
+        // up shortly, finishing the test.
+        action_.store(QUIT);
+        TimeTicks now = TimeTicks::Now();
+        return {now + TimeDelta::FromMilliseconds(50), now};
+      }
+      case QUIT:
+        message_pump_->Quit();
+        break;
+    }
+    return MessagePump::Delegate::NextWorkInfo{TimeTicks::Max()};
+  }
+
   bool DoWork() override {
     switch (action_.load()) {
       case NONE:
         break;
       case SCHEDULE_DELAYED_WORK:
         // After being woken up by the other thread, we schedule work after a
-        // short delay. If is workaround was successful, the pump will wake up
-        // shortly, finishing the test.
+        // short delay. If the pump refreshes its timer correctly, it will wake
+        // up shortly, finishing the test.
         action_.store(QUIT);
         message_pump_->ScheduleDelayedWork(TimeTicks::Now() +
                                            TimeDelta::FromMilliseconds(50));
@@ -138,6 +234,8 @@
   std::atomic<Action> action_;
 };
 
+}  // namespace
+
 TEST_P(MessagePumpTest, TimerSlackWithLongDelays) {
   // This is a regression test for an issue where the iOS message pump fails to
   // run delayed work when timer slack is enabled. The steps needed to trigger
@@ -171,5 +269,4 @@
                                            MessageLoop::TYPE_UI,
                                            MessageLoop::TYPE_IO));
 
-}  // namespace
-}  // namespace base
\ No newline at end of file
+}  // namespace base
diff --git a/base/task/sequence_manager/task_queue_impl.cc b/base/task/sequence_manager/task_queue_impl.cc
index b183da3d..c33e1e8 100644
--- a/base/task/sequence_manager/task_queue_impl.cc
+++ b/base/task/sequence_manager/task_queue_impl.cc
@@ -11,6 +11,7 @@
 #include "base/task/sequence_manager/sequence_manager_impl.h"
 #include "base/task/sequence_manager/time_domain.h"
 #include "base/task/sequence_manager/work_queue.h"
+#include "base/threading/thread_restrictions.h"
 #include "base/time/time.h"
 #include "base/trace_event/blame_context.h"
 #include "build/build_config.h"
@@ -145,7 +146,10 @@
 
 void TaskQueueImpl::UnregisterTaskQueue() {
   // Detach task runners.
-  task_poster_->ShutdownAndWaitForZeroOperations();
+  {
+    ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
+    task_poster_->ShutdownAndWaitForZeroOperations();
+  }
 
   TaskDeque immediate_incoming_queue;
 
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
index 9ba1ab47d..f8e2aef3 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.cc
@@ -142,8 +142,10 @@
   if (main_thread_only().next_delayed_do_work == run_time)
     return;
 
-  run_time = CapAtOneDay(run_time, lazy_now);
+  // Cap at one day but remember the exact time for the above equality check on
+  // the next round.
   main_thread_only().next_delayed_do_work = run_time;
+  run_time = CapAtOneDay(run_time, lazy_now);
 
   // Do not call ScheduleDelayedWork if there is an immediate DoWork scheduled.
   // We can rely on calling ScheduleDelayedWork from the next DoWork call.
@@ -214,6 +216,39 @@
   return associated_thread_;
 }
 
+MessagePump::Delegate::NextWorkInfo
+ThreadControllerWithMessagePumpImpl::DoSomeWork() {
+  main_thread_only().immediate_do_work_posted = false;
+  bool ran_task = false;  // Unused.
+  LazyNow continuation_lazy_now(time_source_);
+  TimeDelta delay_till_next_task =
+      DoWorkImpl(&continuation_lazy_now, &ran_task);
+  // Schedule a continuation.
+  if (delay_till_next_task.is_zero()) {
+    // Need to run new work immediately, but due to the contract of DoSomeWork
+    // we only need to return a null TimeTicks to ensure that happens.
+    main_thread_only().immediate_do_work_posted = true;
+    return MessagePump::Delegate::NextWorkInfo();
+  }
+
+  // While the math below would saturate when |delay_till_next_task.is_max()|;
+  // special-casing here avoids unnecessarily sampling Now() when out of work.
+  if (delay_till_next_task.is_max()) {
+    main_thread_only().next_delayed_do_work = TimeTicks::Max();
+    return {TimeTicks::Max()};
+  }
+
+  // The MessagePump will schedule the delay on our behalf, so we need to update
+  // |main_thread_only().next_delayed_do_work|.
+  // TODO(gab, alexclarke): Replace DelayTillNextTask() with NextTaskTime() to
+  // avoid converting back-and-forth between TimeTicks and TimeDelta.
+  main_thread_only().next_delayed_do_work =
+      continuation_lazy_now.Now() + delay_till_next_task;
+  return {CapAtOneDay(main_thread_only().next_delayed_do_work,
+                      &continuation_lazy_now),
+          continuation_lazy_now.Now()};
+}
+
 bool ThreadControllerWithMessagePumpImpl::DoWork() {
   main_thread_only().immediate_do_work_posted = false;
   bool ran_task = false;
diff --git a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
index 7a6bd516..1d1f26b 100644
--- a/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
+++ b/base/task/sequence_manager/thread_controller_with_message_pump_impl.h
@@ -74,6 +74,7 @@
   explicit ThreadControllerWithMessagePumpImpl(const TickClock* time_source);
 
   // MessagePump::Delegate implementation.
+  MessagePump::Delegate::NextWorkInfo DoSomeWork() override;
   bool DoWork() override;
   bool DoDelayedWork(TimeTicks* next_run_time) override;
   bool DoIdleWork() override;
diff --git a/base/task/task_features.cc b/base/task/task_features.cc
index 99462a25..d0e7a5e 100644
--- a/base/task/task_features.cc
+++ b/base/task/task_features.cc
@@ -11,8 +11,11 @@
 const Feature kAllTasksUserBlocking{"AllTasksUserBlocking",
                                     FEATURE_DISABLED_BY_DEFAULT};
 
+// This experiment no longer has any impact, but remains enabled by default
+// because script streamer depends on it.
+// TODO(etiennep): Cleanup this experiment.
 const Feature kMergeBlockingNonBlockingPools = {
-    "MergeBlockingNonBlockingPools", base::FEATURE_DISABLED_BY_DEFAULT};
+    "MergeBlockingNonBlockingPools", base::FEATURE_ENABLED_BY_DEFAULT};
 
 const Feature kNoDetachBelowInitialCapacity = {
     "NoDetachBelowInitialCapacity", base::FEATURE_DISABLED_BY_DEFAULT};
diff --git a/base/task/task_scheduler/environment_config.h b/base/task/task_scheduler/environment_config.h
index 4ca8416..5280cd7a 100644
--- a/base/task/task_scheduler/environment_config.h
+++ b/base/task/task_scheduler/environment_config.h
@@ -14,19 +14,18 @@
 namespace base {
 namespace internal {
 
+// TODO(etiennep): This is now specific to
+// SchedulerSingleThreadTaskRunnerManager, move it there.
 enum EnvironmentType {
   FOREGROUND = 0,
   FOREGROUND_BLOCKING,
-  // Pools will only be created for the environment above on platforms that
-  // don't support SchedulerWorkers running with a background priority.
-  ENVIRONMENT_COUNT_WITHOUT_BACKGROUND_PRIORITY,
-  BACKGROUND = ENVIRONMENT_COUNT_WITHOUT_BACKGROUND_PRIORITY,
+  BACKGROUND,
   BACKGROUND_BLOCKING,
   ENVIRONMENT_COUNT  // Always last.
 };
 
 // Order must match the EnvironmentType enum.
-constexpr struct {
+struct EnvironmentParams {
   // The threads and histograms of this environment will be labeled with
   // the task scheduler name concatenated to this.
   const char* name_suffix;
@@ -34,7 +33,9 @@
   // Preferred priority for threads in this environment; the actual thread
   // priority depends on shutdown state and platform capabilities.
   ThreadPriority priority_hint;
-} kEnvironmentParams[] = {
+};
+
+constexpr EnvironmentParams kEnvironmentParams[] = {
     {"Foreground", base::ThreadPriority::NORMAL},
     {"ForegroundBlocking", base::ThreadPriority::NORMAL},
     {"Background", base::ThreadPriority::BACKGROUND},
diff --git a/base/task/task_scheduler/task_scheduler.cc b/base/task/task_scheduler/task_scheduler.cc
index d98a4f26..02c9297 100644
--- a/base/task/task_scheduler/task_scheduler.cc
+++ b/base/task/task_scheduler/task_scheduler.cc
@@ -25,16 +25,10 @@
 
 TaskScheduler::InitParams::InitParams(
     const SchedulerWorkerPoolParams& background_worker_pool_params_in,
-    const SchedulerWorkerPoolParams& background_blocking_worker_pool_params_in,
     const SchedulerWorkerPoolParams& foreground_worker_pool_params_in,
-    const SchedulerWorkerPoolParams& foreground_blocking_worker_pool_params_in,
     SharedWorkerPoolEnvironment shared_worker_pool_environment_in)
     : background_worker_pool_params(background_worker_pool_params_in),
-      background_blocking_worker_pool_params(
-          background_blocking_worker_pool_params_in),
       foreground_worker_pool_params(foreground_worker_pool_params_in),
-      foreground_blocking_worker_pool_params(
-          foreground_blocking_worker_pool_params_in),
       shared_worker_pool_environment(shared_worker_pool_environment_in) {}
 
 TaskScheduler::InitParams::~InitParams() = default;
@@ -64,17 +58,15 @@
   // * The main thread is assumed to be busy, cap foreground workers at
   //   |num_cores - 1|.
   const int num_cores = SysInfo::NumberOfProcessors();
-  constexpr int kBackgroundMaxThreads = 1;
-  constexpr int kBackgroundBlockingMaxThreads = 2;
-  const int kForegroundMaxThreads = std::max(1, num_cores - 1);
-  const int kForegroundBlockingMaxThreads = std::max(2, num_cores - 1);
+
+  // TODO(etiennep): Change this to 2.
+  constexpr int kBackgroundMaxThreads = 3;
+  const int kForegroundMaxThreads = std::max(3, num_cores - 1);
 
   constexpr TimeDelta kSuggestedReclaimTime = TimeDelta::FromSeconds(30);
 
   Start({{kBackgroundMaxThreads, kSuggestedReclaimTime},
-         {kBackgroundBlockingMaxThreads, kSuggestedReclaimTime},
-         {kForegroundMaxThreads, kSuggestedReclaimTime},
-         {kForegroundBlockingMaxThreads, kSuggestedReclaimTime}});
+         {kForegroundMaxThreads, kSuggestedReclaimTime}});
 }
 #endif  // !defined(OS_NACL)
 
diff --git a/base/task/task_scheduler/task_scheduler.h b/base/task/task_scheduler/task_scheduler.h
index 03cc1d9..63b776a 100644
--- a/base/task/task_scheduler/task_scheduler.h
+++ b/base/task/task_scheduler/task_scheduler.h
@@ -64,19 +64,13 @@
 
     InitParams(
         const SchedulerWorkerPoolParams& background_worker_pool_params_in,
-        const SchedulerWorkerPoolParams&
-            background_blocking_worker_pool_params_in,
         const SchedulerWorkerPoolParams& foreground_worker_pool_params_in,
-        const SchedulerWorkerPoolParams&
-            foreground_blocking_worker_pool_params_in,
         SharedWorkerPoolEnvironment shared_worker_pool_environment_in =
             SharedWorkerPoolEnvironment::DEFAULT);
     ~InitParams();
 
     SchedulerWorkerPoolParams background_worker_pool_params;
-    SchedulerWorkerPoolParams background_blocking_worker_pool_params;
     SchedulerWorkerPoolParams foreground_worker_pool_params;
-    SchedulerWorkerPoolParams foreground_blocking_worker_pool_params;
     SharedWorkerPoolEnvironment shared_worker_pool_environment;
   };
 
diff --git a/base/task/task_scheduler/task_scheduler_impl.cc b/base/task/task_scheduler/task_scheduler_impl.cc
index 7ceb5fa..264d791f 100644
--- a/base/task/task_scheduler/task_scheduler_impl.cc
+++ b/base/task/task_scheduler/task_scheduler_impl.cc
@@ -32,19 +32,11 @@
 
 namespace {
 
-// Returns worker pool EnvironmentType for given arguments |is_background| and
-// |is_blocking|.
-EnvironmentType GetEnvironmentIndex(bool is_background, bool is_blocking) {
-  if (is_background) {
-    if (is_blocking)
-      return BACKGROUND_BLOCKING;
-    return BACKGROUND;
-  }
+constexpr EnvironmentParams kForegroundPoolEnvironmentParams{
+    "Foreground", base::ThreadPriority::NORMAL};
 
-  if (is_blocking)
-    return FOREGROUND_BLOCKING;
-  return FOREGROUND;
-}
+constexpr EnvironmentParams kBackgroundPoolEnvironmentParams{
+    "Background", base::ThreadPriority::BACKGROUND};
 
 }  // namespace
 
@@ -66,42 +58,22 @@
       tracked_ref_factory_(this) {
   DCHECK(!histogram_label.empty());
 
-  static_assert(
-      std::extent<decltype(environment_to_worker_pool_)>() == ENVIRONMENT_COUNT,
-      "The size of |environment_to_worker_pool_| must match "
-      "ENVIRONMENT_COUNT.");
-  static_assert(
-      size(kEnvironmentParams) == ENVIRONMENT_COUNT,
-      "The size of |kEnvironmentParams| must match ENVIRONMENT_COUNT.");
+  foreground_pool_.emplace(
+      JoinString(
+          {histogram_label, kForegroundPoolEnvironmentParams.name_suffix}, "."),
+      kForegroundPoolEnvironmentParams.name_suffix,
+      kForegroundPoolEnvironmentParams.priority_hint,
+      task_tracker_->GetTrackedRef(), tracked_ref_factory_.GetTrackedRef());
 
-  int num_pools_to_create = CanUseBackgroundPriorityForSchedulerWorker()
-                                ? ENVIRONMENT_COUNT
-                                : ENVIRONMENT_COUNT_WITHOUT_BACKGROUND_PRIORITY;
-  for (int environment_type = 0; environment_type < num_pools_to_create;
-       ++environment_type) {
-    worker_pools_.emplace_back(std::make_unique<SchedulerWorkerPoolImpl>(
+  if (CanUseBackgroundPriorityForSchedulerWorker()) {
+    background_pool_.emplace(
         JoinString(
-            {histogram_label, kEnvironmentParams[environment_type].name_suffix},
+            {histogram_label, kBackgroundPoolEnvironmentParams.name_suffix},
             "."),
-        kEnvironmentParams[environment_type].name_suffix,
-        kEnvironmentParams[environment_type].priority_hint,
-        task_tracker_->GetTrackedRef(), tracked_ref_factory_.GetTrackedRef()));
+        kBackgroundPoolEnvironmentParams.name_suffix,
+        kBackgroundPoolEnvironmentParams.priority_hint,
+        task_tracker_->GetTrackedRef(), tracked_ref_factory_.GetTrackedRef());
   }
-
-  // Map environment indexes to pools. |kMergeBlockingNonBlockingPools| is
-  // assumed to be disabled.
-  environment_to_worker_pool_[FOREGROUND] =
-      worker_pools_[GetEnvironmentIndex(false, false)].get();
-  environment_to_worker_pool_[FOREGROUND_BLOCKING] =
-      worker_pools_[GetEnvironmentIndex(false, true)].get();
-  environment_to_worker_pool_[BACKGROUND] =
-      worker_pools_[GetEnvironmentIndex(
-                        CanUseBackgroundPriorityForSchedulerWorker(), false)]
-          .get();
-  environment_to_worker_pool_[BACKGROUND_BLOCKING] =
-      worker_pools_[GetEnvironmentIndex(
-                        CanUseBackgroundPriorityForSchedulerWorker(), true)]
-          .get();
 }
 
 TaskSchedulerImpl::~TaskSchedulerImpl() {
@@ -109,8 +81,9 @@
   DCHECK(join_for_testing_returned_.IsSet());
 #endif
 
-  // Clear |worker_pools_| to release held TrackedRefs, which block teardown.
-  worker_pools_.clear();
+  // Reset worker pools to release held TrackedRefs, which block teardown.
+  foreground_pool_.reset();
+  background_pool_.reset();
 }
 
 void TaskSchedulerImpl::Start(
@@ -123,26 +96,6 @@
   if (FeatureList::IsEnabled(kAllTasksUserBlocking))
     all_tasks_user_blocking_.Set();
 
-  const bool use_blocking_pools =
-      !base::FeatureList::IsEnabled(kMergeBlockingNonBlockingPools);
-
-  // Remap environment indexes to pools with |use_blocking_pools|.
-  // TODO(etiennep): This is only necessary because of the kMergeBlockingNonBlockingPools
-  // experiment. Remove this after the experiment.
-  environment_to_worker_pool_[FOREGROUND] =
-      worker_pools_[GetEnvironmentIndex(false, false)].get();
-  environment_to_worker_pool_[FOREGROUND_BLOCKING] =
-      worker_pools_[GetEnvironmentIndex(false, use_blocking_pools)].get();
-  environment_to_worker_pool_[BACKGROUND] =
-      worker_pools_[GetEnvironmentIndex(
-                        CanUseBackgroundPriorityForSchedulerWorker(), false)]
-          .get();
-  environment_to_worker_pool_[BACKGROUND_BLOCKING] =
-      worker_pools_[GetEnvironmentIndex(
-                        CanUseBackgroundPriorityForSchedulerWorker(),
-                        use_blocking_pools)]
-          .get();
-
   // Start the service thread. On platforms that support it (POSIX except NaCL
   // SFI), the service thread runs a MessageLoopForIO which is used to support
   // FileDescriptorWatcher in the scope in which tasks run.
@@ -187,32 +140,17 @@
   const int max_best_effort_tasks_in_foreground_pool = std::max(
       1, std::min(init_params.background_worker_pool_params.max_tasks(),
                   init_params.foreground_worker_pool_params.max_tasks() / 2));
-  worker_pools_[FOREGROUND]->Start(
-      init_params.foreground_worker_pool_params,
-      max_best_effort_tasks_in_foreground_pool, service_thread_task_runner,
-      scheduler_worker_observer, worker_environment);
-  const int max_best_effort_tasks_in_foreground_blocking_pool = std::max(
-      1, std::min(
-             init_params.background_blocking_worker_pool_params.max_tasks(),
-             init_params.foreground_blocking_worker_pool_params.max_tasks() /
-                 2));
-  worker_pools_[FOREGROUND_BLOCKING]->Start(
-      init_params.foreground_blocking_worker_pool_params,
-      max_best_effort_tasks_in_foreground_blocking_pool,
-      service_thread_task_runner, scheduler_worker_observer,
-      worker_environment);
+  foreground_pool_->Start(init_params.foreground_worker_pool_params,
+                          max_best_effort_tasks_in_foreground_pool,
+                          service_thread_task_runner, scheduler_worker_observer,
+                          worker_environment);
 
-  if (CanUseBackgroundPriorityForSchedulerWorker()) {
-    worker_pools_[BACKGROUND]->Start(
+  if (background_pool_.has_value()) {
+    background_pool_->Start(
         init_params.background_worker_pool_params,
         init_params.background_worker_pool_params.max_tasks(),
         service_thread_task_runner, scheduler_worker_observer,
         worker_environment);
-    worker_pools_[BACKGROUND_BLOCKING]->Start(
-        init_params.background_blocking_worker_pool_params,
-        init_params.background_blocking_worker_pool_params.max_tasks(),
-        service_thread_task_runner, scheduler_worker_observer,
-        worker_environment);
   }
 }
 
@@ -267,8 +205,9 @@
 
 std::vector<const HistogramBase*> TaskSchedulerImpl::GetHistograms() const {
   std::vector<const HistogramBase*> histograms;
-  for (const auto& worker_pool : worker_pools_)
-    worker_pool->GetHistograms(&histograms);
+  foreground_pool_->GetHistograms(&histograms);
+  if (background_pool_.has_value())
+    background_pool_->GetHistograms(&histograms);
 
   return histograms;
 }
@@ -304,8 +243,9 @@
   // https://crbug.com/771701.
   service_thread_->Stop();
   single_thread_task_runner_manager_.JoinForTesting();
-  for (const auto& worker_pool : worker_pools_)
-    worker_pool->JoinForTesting();
+  foreground_pool_->JoinForTesting();
+  if (background_pool_.has_value())
+    background_pool_->JoinForTesting();
 #if DCHECK_IS_ON()
   join_for_testing_returned_.Set();
 #endif
@@ -401,8 +341,12 @@
 }
 
 SchedulerWorkerPoolImpl* TaskSchedulerImpl::GetWorkerPoolForTraits(
-    const TaskTraits& traits) const {
-  return environment_to_worker_pool_[GetEnvironmentIndexForTraits(traits)];
+    const TaskTraits& traits) {
+  if (traits.priority() == TaskPriority::BEST_EFFORT &&
+      background_pool_.has_value()) {
+    return &background_pool_.value();
+  }
+  return &foreground_pool_.value();
 }
 
 TaskTraits TaskSchedulerImpl::SetUserBlockingPriorityIfNeeded(
@@ -413,9 +357,9 @@
 }
 
 void TaskSchedulerImpl::ReportHeartbeatMetrics() const {
-  for (const auto& worker_pool : worker_pools_)
-    worker_pool->ReportHeartbeatMetrics();
-  delayed_task_manager_.ReportHeartbeatMetrics();
+  foreground_pool_->ReportHeartbeatMetrics();
+  if (background_pool_.has_value())
+    background_pool_->ReportHeartbeatMetrics();
 }
 
 }  // namespace internal
diff --git a/base/task/task_scheduler/task_scheduler_impl.h b/base/task/task_scheduler/task_scheduler_impl.h
index 7b32eb5..46896fb 100644
--- a/base/task/task_scheduler/task_scheduler_impl.h
+++ b/base/task/task_scheduler/task_scheduler_impl.h
@@ -100,8 +100,11 @@
 
  private:
   // Returns the worker pool that runs Tasks with |traits|.
-  SchedulerWorkerPoolImpl* GetWorkerPoolForTraits(
-      const TaskTraits& traits) const;
+  SchedulerWorkerPoolImpl* GetWorkerPoolForTraits(const TaskTraits& traits);
+  const SchedulerWorkerPoolImpl* GetWorkerPoolForTraits(
+      const TaskTraits& traits) const {
+    return const_cast<TaskSchedulerImpl*>(this)->GetWorkerPoolForTraits(traits);
+  }
 
   // Returns |traits|, with priority set to TaskPriority::USER_BLOCKING if
   // |all_tasks_user_blocking_| is set.
@@ -133,12 +136,8 @@
   // TODO(fdoray): Remove after experiment. https://crbug.com/757022
   AtomicFlag all_tasks_user_blocking_;
 
-  // Owns all the pools managed by this TaskScheduler.
-  std::vector<std::unique_ptr<SchedulerWorkerPoolImpl>> worker_pools_;
-
-  // Maps an environment from EnvironmentType to a pool in |worker_pools_|.
-  SchedulerWorkerPoolImpl* environment_to_worker_pool_[static_cast<int>(
-      EnvironmentType::ENVIRONMENT_COUNT)];
+  Optional<SchedulerWorkerPoolImpl> foreground_pool_;
+  Optional<SchedulerWorkerPoolImpl> background_pool_;
 
 #if DCHECK_IS_ON()
   // Set once JoinForTesting() has returned.
diff --git a/base/task/task_scheduler/task_scheduler_impl_unittest.cc b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
index 9d78749a..0e7388bf 100644
--- a/base/task/task_scheduler/task_scheduler_impl_unittest.cc
+++ b/base/task/task_scheduler/task_scheduler_impl_unittest.cc
@@ -59,11 +59,6 @@
 
 namespace {
 
-enum class PoolConfiguration {
-  kDefault,
-  kMergeBlockingNonBlocking,
-};
-
 enum class SchedulerState {
   // TaskScheduler::Start() was not called yet, no thread was created.
   kBeforeSchedulerStart,
@@ -73,15 +68,11 @@
 
 struct TaskSchedulerImplTestParams {
   TaskSchedulerImplTestParams(const TaskTraits& traits,
-                              test::ExecutionMode execution_mode,
-                              PoolConfiguration pool_config)
-      : traits(traits),
-        execution_mode(execution_mode),
-        pool_config(pool_config) {}
+                              test::ExecutionMode execution_mode)
+      : traits(traits), execution_mode(execution_mode) {}
 
   TaskTraits traits;
   test::ExecutionMode execution_mode;
-  PoolConfiguration pool_config;
 };
 
 #if DCHECK_IS_ON()
@@ -130,15 +121,10 @@
                       ? "Background"
                       : "Foreground"));
   }
-  // TaskScheduler only handles |kMergeBlockingNonBlockingPools| once started
-  // (early task runners are not merged for this experiment).
-  // TODO(etiennep): Simplify this after the experiment.
-  // Merging pools does not affect SingleThread workers.
-  if (base::FeatureList::IsEnabled(kMergeBlockingNonBlockingPools) &&
-      state == SchedulerState::kAfterSchedulerStart &&
-      current_thread_name.find("SingleThread") == std::string::npos) {
+  if (current_thread_name.find("SingleThread") == std::string::npos) {
     EXPECT_EQ(std::string::npos, current_thread_name.find("Blocking"));
   } else {
+    // SingleThread workers discriminate blocking/non-blocking tasks.
     EXPECT_EQ(traits.may_block(),
               current_thread_name.find("Blocking") != std::string::npos);
   }
@@ -243,17 +229,9 @@
          priority_index <= static_cast<size_t>(TaskPriority::HIGHEST);
          ++priority_index) {
       const TaskPriority priority = static_cast<TaskPriority>(priority_index);
-      params.push_back(TaskSchedulerImplTestParams(
-          {priority}, execution_mode, PoolConfiguration::kDefault));
-      params.push_back(TaskSchedulerImplTestParams(
-          {MayBlock()}, execution_mode, PoolConfiguration::kDefault));
-
-      params.push_back(TaskSchedulerImplTestParams(
-          {priority}, execution_mode,
-          PoolConfiguration::kMergeBlockingNonBlocking));
-      params.push_back(TaskSchedulerImplTestParams(
-          {MayBlock()}, execution_mode,
-          PoolConfiguration::kMergeBlockingNonBlocking));
+      params.push_back(TaskSchedulerImplTestParams({priority}, execution_mode));
+      params.push_back(
+          TaskSchedulerImplTestParams({priority, MayBlock()}, execution_mode));
     }
   }
 
@@ -263,17 +241,10 @@
 class TaskSchedulerImplTest
     : public testing::TestWithParam<TaskSchedulerImplTestParams> {
  protected:
-  TaskSchedulerImplTest() : scheduler_("Test") {
-    feature_list_.emplace();
-    feature_list_->InitWithFeatures(GetFeaturesEnabledByConstructor(), {});
-  }
+  TaskSchedulerImplTest() : scheduler_("Test") {}
 
   void EnableAllTasksUserBlocking() {
-    feature_list_.reset();
-    feature_list_.emplace();
-    std::vector<Feature> enabled_features = GetFeaturesEnabledByConstructor();
-    enabled_features.push_back(kAllTasksUserBlocking);
-    feature_list_->InitWithFeatures(enabled_features, {});
+    feature_list_.InitWithFeatures({kAllTasksUserBlocking}, {});
   }
 
   void set_scheduler_worker_observer(
@@ -283,14 +254,10 @@
 
   void StartTaskScheduler(TimeDelta reclaim_time = TimeDelta::FromSeconds(30)) {
     constexpr int kMaxNumBackgroundThreads = 1;
-    constexpr int kMaxNumBackgroundBlockingThreads = 3;
     constexpr int kMaxNumForegroundThreads = 4;
-    constexpr int kMaxNumForegroundBlockingThreads = 12;
 
     scheduler_.Start({{kMaxNumBackgroundThreads, reclaim_time},
-                      {kMaxNumBackgroundBlockingThreads, reclaim_time},
-                      {kMaxNumForegroundThreads, reclaim_time},
-                      {kMaxNumForegroundBlockingThreads, reclaim_time}},
+                      {kMaxNumForegroundThreads, reclaim_time}},
                      scheduler_worker_observer_);
   }
 
@@ -306,14 +273,7 @@
   TaskSchedulerImpl scheduler_;
 
  private:
-  std::vector<Feature> GetFeaturesEnabledByConstructor() {
-    if (GetParam().pool_config == PoolConfiguration::kMergeBlockingNonBlocking)
-      return {kMergeBlockingNonBlockingPools};
-    else
-      return {};
-  }
-
-  Optional<base::test::ScopedFeatureList> feature_list_;
+  base::test::ScopedFeatureList feature_list_;
   SchedulerWorkerObserver* scheduler_worker_observer_ = nullptr;
   bool did_tear_down_ = false;
 
@@ -513,7 +473,7 @@
 // TaskTraits and ExecutionModes. Verifies that each Task runs on a thread with
 // the expected priority and I/O restrictions and respects the characteristics
 // of its ExecutionMode.
-TEST_P(TaskSchedulerImplTest, MultipleTaskSchedulerImplTestParams) {
+TEST_F(TaskSchedulerImplTest, MultipleTaskSchedulerImplTestParams) {
   StartTaskScheduler();
   std::vector<std::unique_ptr<ThreadPostingTasks>> threads_posting_tasks;
   for (const auto& test_params : GetTaskSchedulerImplTestParams()) {
@@ -529,7 +489,7 @@
   }
 }
 
-TEST_P(TaskSchedulerImplTest,
+TEST_F(TaskSchedulerImplTest,
        GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated) {
   StartTaskScheduler();
 
@@ -545,32 +505,19 @@
         {MayBlock(), TaskPriority::BEST_EFFORT});
   });
 
-  if (GetParam().pool_config == PoolConfiguration::kMergeBlockingNonBlocking) {
-    EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
-                     {TaskPriority::USER_VISIBLE}));
-    EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
-                     {MayBlock(), TaskPriority::USER_VISIBLE}));
-    EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
-                     {TaskPriority::USER_BLOCKING}));
-    EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
-                     {MayBlock(), TaskPriority::USER_BLOCKING}));
-  } else {
-    EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
-                     {TaskPriority::USER_VISIBLE}));
-    EXPECT_EQ(12,
-              scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
-                  {MayBlock(), TaskPriority::USER_VISIBLE}));
-    EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
-                     {TaskPriority::USER_BLOCKING}));
-    EXPECT_EQ(12,
-              scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
-                  {MayBlock(), TaskPriority::USER_BLOCKING}));
-  }
+  EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+                   {TaskPriority::USER_VISIBLE}));
+  EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+                   {MayBlock(), TaskPriority::USER_VISIBLE}));
+  EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+                   {TaskPriority::USER_BLOCKING}));
+  EXPECT_EQ(4, scheduler_.GetMaxConcurrentNonBlockedTasksWithTraitsDeprecated(
+                   {MayBlock(), TaskPriority::USER_BLOCKING}));
 }
 
 // Verify that the RunsTasksInCurrentSequence() method of a SequencedTaskRunner
 // returns false when called from a task that isn't part of the sequence.
-TEST_P(TaskSchedulerImplTest, SequencedRunsTasksInCurrentSequence) {
+TEST_F(TaskSchedulerImplTest, SequencedRunsTasksInCurrentSequence) {
   StartTaskScheduler();
   auto single_thread_task_runner =
       scheduler_.CreateSingleThreadTaskRunnerWithTraits(
@@ -594,7 +541,7 @@
 // Verify that the RunsTasksInCurrentSequence() method of a
 // SingleThreadTaskRunner returns false when called from a task that isn't part
 // of the sequence.
-TEST_P(TaskSchedulerImplTest, SingleThreadRunsTasksInCurrentSequence) {
+TEST_F(TaskSchedulerImplTest, SingleThreadRunsTasksInCurrentSequence) {
   StartTaskScheduler();
   auto sequenced_task_runner =
       scheduler_.CreateSequencedTaskRunnerWithTraits(TaskTraits());
@@ -617,7 +564,7 @@
 }
 
 #if defined(OS_WIN)
-TEST_P(TaskSchedulerImplTest, COMSTATaskRunnersRunWithCOMSTA) {
+TEST_F(TaskSchedulerImplTest, COMSTATaskRunnersRunWithCOMSTA) {
   StartTaskScheduler();
   auto com_sta_task_runner = scheduler_.CreateCOMSTATaskRunnerWithTraits(
       TaskTraits(), SingleThreadTaskRunnerThreadMode::SHARED);
@@ -634,7 +581,7 @@
 }
 #endif  // defined(OS_WIN)
 
-TEST_P(TaskSchedulerImplTest, DelayedTasksNotRunAfterShutdown) {
+TEST_F(TaskSchedulerImplTest, DelayedTasksNotRunAfterShutdown) {
   StartTaskScheduler();
   // As with delayed tasks in general, this is racy. If the task does happen to
   // run after Shutdown within the timeout, it will fail this test.
@@ -657,7 +604,7 @@
 
 #if defined(OS_POSIX)
 
-TEST_P(TaskSchedulerImplTest, FileDescriptorWatcherNoOpsAfterShutdown) {
+TEST_F(TaskSchedulerImplTest, FileDescriptorWatcherNoOpsAfterShutdown) {
   StartTaskScheduler();
 
   int pipes[2];
@@ -704,7 +651,7 @@
 
 // Verify that tasks posted on the same sequence access the same values on
 // SequenceLocalStorage, and tasks on different sequences see different values.
-TEST_P(TaskSchedulerImplTest, SequenceLocalStorage) {
+TEST_F(TaskSchedulerImplTest, SequenceLocalStorage) {
   StartTaskScheduler();
 
   SequenceLocalStorageSlot<int> slot;
@@ -735,7 +682,7 @@
   scheduler_.FlushForTesting();
 }
 
-TEST_P(TaskSchedulerImplTest, FlushAsyncNoTasks) {
+TEST_F(TaskSchedulerImplTest, FlushAsyncNoTasks) {
   StartTaskScheduler();
   bool called_back = false;
   scheduler_.FlushAsyncForTesting(
@@ -779,7 +726,7 @@
 // Integration test that verifies that workers have a frame on their stacks
 // which easily identifies the type of worker and shutdown behavior (useful to
 // diagnose issues from logs without memory dumps).
-TEST_P(TaskSchedulerImplTest, MAYBE_IdentifiableStacks) {
+TEST_F(TaskSchedulerImplTest, MAYBE_IdentifiableStacks) {
   StartTaskScheduler();
 
   // Shutdown behaviors and expected stack frames.
@@ -859,14 +806,14 @@
   scheduler_.FlushForTesting();
 }
 
-TEST_P(TaskSchedulerImplTest, SchedulerWorkerObserver) {
+TEST_F(TaskSchedulerImplTest, SchedulerWorkerObserver) {
   testing::StrictMock<test::MockSchedulerWorkerObserver> observer;
   set_scheduler_worker_observer(&observer);
 
   // A worker should be created for each pool. After that, 4 threads should be
   // created for each SingleThreadTaskRunnerThreadMode (8 on Windows).
   const int kExpectedNumPoolWorkers =
-      CanUseBackgroundPriorityForSchedulerWorker() ? 4 : 2;
+      CanUseBackgroundPriorityForSchedulerWorker() ? 2 : 1;
 #if defined(OS_WIN)
   const int kExpectedNumSingleThreadedWorkersPerMode = 8;
 #else
@@ -984,8 +931,6 @@
     constexpr TimeDelta kSuggestedReclaimTime = TimeDelta::FromSeconds(30);
 
     scheduler_.Start({{threads_per_pool, kSuggestedReclaimTime},
-                      {threads_per_pool, kSuggestedReclaimTime},
-                      {threads_per_pool, kSuggestedReclaimTime},
                       {threads_per_pool, kSuggestedReclaimTime}},
                      nullptr);
   }
@@ -1043,13 +988,9 @@
 
   pool_blocking_events.push_back(std::make_unique<PoolBlockingEvents>(
       TaskTraits({TaskPriority::USER_BLOCKING})));
-  pool_blocking_events.push_back(std::make_unique<PoolBlockingEvents>(
-      TaskTraits({TaskPriority::USER_BLOCKING, MayBlock()})));
   if (CanUseBackgroundPriorityForSchedulerWorker()) {
     pool_blocking_events.push_back(std::make_unique<PoolBlockingEvents>(
         TaskTraits({TaskPriority::BEST_EFFORT})));
-    pool_blocking_events.push_back(std::make_unique<PoolBlockingEvents>(
-        TaskTraits({TaskPriority::BEST_EFFORT, MayBlock()})));
   }
 
   // When all blocking tasks signal |scheduled|, there is a task blocked in
@@ -1096,10 +1037,6 @@
   for (auto& task_runner_and_events : task_runners_and_events_) {
     test::WaitWithoutBlockingObserver(&task_runner_and_events->task_ran);
   }
-
-  // Make sure to coalesce tasks from |pool_blocking_events| (they are not
-  // guaranteed to all have picked up the Signal() to unblock at this point).
-  scheduler_.FlushForTesting();
 }
 
 // Update the priority of a sequence when it is scheduled, i.e. not currently
diff --git a/base/task/task_scheduler/task_scheduler_perftest.cc b/base/task/task_scheduler/task_scheduler_perftest.cc
index b83cffd..5976687f 100644
--- a/base/task/task_scheduler/task_scheduler_perftest.cc
+++ b/base/task/task_scheduler/task_scheduler_perftest.cc
@@ -122,14 +122,10 @@
                           base::RepeatingClosure post_action) {
     constexpr TimeDelta kSuggestedReclaimTime = TimeDelta::FromSeconds(30);
     constexpr int kMaxNumBackgroundThreads = 1;
-    constexpr int kMaxNumBackgroundBlockingThreads = 1;
-    constexpr int kMaxNumForegroundBlockingThreads = 1;
 
     TaskScheduler::GetInstance()->Start(
         {{kMaxNumBackgroundThreads, kSuggestedReclaimTime},
-         {kMaxNumBackgroundBlockingThreads, kSuggestedReclaimTime},
-         {num_running_threads, kSuggestedReclaimTime},
-         {kMaxNumForegroundBlockingThreads, kSuggestedReclaimTime}},
+         {num_running_threads, kSuggestedReclaimTime}},
         nullptr);
 
     base::RepeatingClosure done = BarrierClosure(
diff --git a/base/test/BUILD.gn b/base/test/BUILD.gn
index 09ed1a1..1266b4b 100644
--- a/base/test/BUILD.gn
+++ b/base/test/BUILD.gn
@@ -48,6 +48,7 @@
     "android/url_utils.h",
     "bind_test_util.cc",
     "bind_test_util.h",
+    "copy_only_int.cc",
     "copy_only_int.h",
     "fuzzed_data_provider.h",
     "gtest_util.cc",
diff --git a/base/test/copy_only_int.cc b/base/test/copy_only_int.cc
new file mode 100644
index 0000000..d135a86
--- /dev/null
+++ b/base/test/copy_only_int.cc
@@ -0,0 +1,12 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/copy_only_int.h"
+
+namespace base {
+
+// static
+int CopyOnlyInt::num_copies_ = 0;
+
+}  // namespace base
diff --git a/base/test/copy_only_int.h b/base/test/copy_only_int.h
index 4e482c9..5cd969c 100644
--- a/base/test/copy_only_int.h
+++ b/base/test/copy_only_int.h
@@ -14,7 +14,7 @@
 class CopyOnlyInt {
  public:
   explicit CopyOnlyInt(int data = 1) : data_(data) {}
-  CopyOnlyInt(const CopyOnlyInt& other) = default;
+  CopyOnlyInt(const CopyOnlyInt& other) : data_(other.data_) { ++num_copies_; }
   ~CopyOnlyInt() { data_ = 0; }
 
   friend bool operator==(const CopyOnlyInt& lhs, const CopyOnlyInt& rhs) {
@@ -43,9 +43,15 @@
 
   int data() const { return data_; }
 
+  static void reset_num_copies() { num_copies_ = 0; }
+
+  static int num_copies() { return num_copies_; }
+
  private:
   volatile int data_;
 
+  static int num_copies_;
+
   CopyOnlyInt(CopyOnlyInt&&) = delete;
   CopyOnlyInt& operator=(CopyOnlyInt&) = delete;
 };
diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc
index 206064ba..a25bec59 100644
--- a/base/test/launcher/test_launcher.cc
+++ b/base/test/launcher/test_launcher.cc
@@ -139,18 +139,14 @@
 void CreateAndStartTaskScheduler(int num_parallel_jobs) {
   // These values are taken from TaskScheduler::StartWithDefaultParams(), which
   // is not used directly to allow a custom number of threads in the foreground
-  // blocking pool.
-  constexpr int kMaxBackgroundThreads = 1;
-  constexpr int kMaxBackgroundBlockingThreads = 2;
-  const int max_foreground_threads =
-      std::max(1, base::SysInfo::NumberOfProcessors());
+  // pool.
+  // TODO(etiennep): Change this to 2 in future CL.
+  constexpr int kMaxBackgroundThreads = 3;
   constexpr base::TimeDelta kSuggestedReclaimTime =
       base::TimeDelta::FromSeconds(30);
   base::TaskScheduler::Create("TestLauncher");
   base::TaskScheduler::GetInstance()->Start(
       {{kMaxBackgroundThreads, kSuggestedReclaimTime},
-       {kMaxBackgroundBlockingThreads, kSuggestedReclaimTime},
-       {max_foreground_threads, kSuggestedReclaimTime},
        {num_parallel_jobs, kSuggestedReclaimTime}});
 }
 
diff --git a/base/test/scoped_task_environment.cc b/base/test/scoped_task_environment.cc
index ff63c85..a47cca8 100644
--- a/base/test/scoped_task_environment.cc
+++ b/base/test/scoped_task_environment.cc
@@ -337,7 +337,7 @@
   CHECK(base::ThreadTaskRunnerHandle::IsSet())
       << "ThreadTaskRunnerHandle should've been set now.";
 
-  // Instantiate a TaskScheduler with 2 threads in each of its 4 pools. Threads
+  // Instantiate a TaskScheduler with 2 threads in each of its pools. Threads
   // stay alive even when they don't have work.
   // Each pool uses two threads to prevent deadlocks in unit tests that have a
   // sequence that uses WithBaseSyncPrimitives() to wait on the result of
@@ -353,8 +353,7 @@
       "ScopedTaskEnvironment", WrapUnique(task_tracker_)));
   task_scheduler_ = TaskScheduler::GetInstance();
   TaskScheduler::GetInstance()->Start({
-    worker_pool_params, worker_pool_params, worker_pool_params,
-        worker_pool_params
+    worker_pool_params, worker_pool_params
 #if defined(OS_WIN)
         ,
         // Enable the MTA in unit tests to match the browser process'
diff --git a/base/test/type_id_test_support_a.cc b/base/test/type_id_test_support_a.cc
index 2db005b..143c28e5 100644
--- a/base/test/type_id_test_support_a.cc
+++ b/base/test/type_id_test_support_a.cc
@@ -15,13 +15,12 @@
 namespace base {
 
 // static
-experimental::TypeId
-TypeIdTestSupportA::GetTypeIdForTypeInAnonymousNameSpace() {
-  return experimental::TypeId::Create<TypeInAnonymousNameSpace>();
+TypeId TypeIdTestSupportA::GetTypeIdForTypeInAnonymousNameSpace() {
+  return TypeId::From<TypeInAnonymousNameSpace>();
 }
 
-experimental::TypeId TypeIdTestSupportA::GetTypeIdForUniquePtrInt() {
-  return experimental::TypeId::Create<std::unique_ptr<int>>();
+TypeId TypeIdTestSupportA::GetTypeIdForUniquePtrInt() {
+  return TypeId::From<std::unique_ptr<int>>();
 }
 
 }  // namespace base
diff --git a/base/test/type_id_test_support_a.h b/base/test/type_id_test_support_a.h
index db7cdf1..22b80b5 100644
--- a/base/test/type_id_test_support_a.h
+++ b/base/test/type_id_test_support_a.h
@@ -12,8 +12,8 @@
 
 // This is here to help test base::TypeId.
 struct COMPONENT_EXPORT(BASE_TEST) TypeIdTestSupportA {
-  static experimental::TypeId GetTypeIdForTypeInAnonymousNameSpace();
-  static experimental::TypeId GetTypeIdForUniquePtrInt();
+  static TypeId GetTypeIdForTypeInAnonymousNameSpace();
+  static TypeId GetTypeIdForUniquePtrInt();
 };
 
 }  // namespace base
diff --git a/base/test/type_id_test_support_b.cc b/base/test/type_id_test_support_b.cc
index 5c6b1a5cf..1ac9af7e 100644
--- a/base/test/type_id_test_support_b.cc
+++ b/base/test/type_id_test_support_b.cc
@@ -15,13 +15,12 @@
 namespace base {
 
 // static
-experimental::TypeId
-TypeIdTestSupportB::GetTypeIdForTypeInAnonymousNameSpace() {
-  return experimental::TypeId::Create<TypeInAnonymousNameSpace>();
+TypeId TypeIdTestSupportB::GetTypeIdForTypeInAnonymousNameSpace() {
+  return TypeId::From<TypeInAnonymousNameSpace>();
 }
 
-experimental::TypeId TypeIdTestSupportB::GetTypeIdForUniquePtrInt() {
-  return experimental::TypeId::Create<std::unique_ptr<int>>();
+TypeId TypeIdTestSupportB::GetTypeIdForUniquePtrInt() {
+  return TypeId::From<std::unique_ptr<int>>();
 }
 
 }  // namespace base
diff --git a/base/test/type_id_test_support_b.h b/base/test/type_id_test_support_b.h
index 9c457a7..342076e 100644
--- a/base/test/type_id_test_support_b.h
+++ b/base/test/type_id_test_support_b.h
@@ -12,8 +12,8 @@
 
 // This is here to help test base::TypeId.
 struct COMPONENT_EXPORT(BASE_TEST) TypeIdTestSupportB {
-  static experimental::TypeId GetTypeIdForTypeInAnonymousNameSpace();
-  static experimental::TypeId GetTypeIdForUniquePtrInt();
+  static TypeId GetTypeIdForTypeInAnonymousNameSpace();
+  static TypeId GetTypeIdForUniquePtrInt();
 };
 
 }  // namespace base
diff --git a/base/threading/post_task_and_reply_impl.cc b/base/threading/post_task_and_reply_impl.cc
index 5aacdad..cc80274 100644
--- a/base/threading/post_task_and_reply_impl.cc
+++ b/base/threading/post_task_and_reply_impl.cc
@@ -21,55 +21,73 @@
  public:
   PostTaskAndReplyRelay(const Location& from_here,
                         OnceClosure task,
-                        OnceClosure reply)
+                        OnceClosure reply,
+                        scoped_refptr<SequencedTaskRunner> reply_task_runner)
       : from_here_(from_here),
         task_(std::move(task)),
-        reply_(std::move(reply)) {}
+        reply_(std::move(reply)),
+        reply_task_runner_(std::move(reply_task_runner)) {}
   PostTaskAndReplyRelay(PostTaskAndReplyRelay&&) = default;
 
+  // It is important that |reply_| always be deleted on the origin sequence
+  // (|reply_task_runner_|) since its destructor can be affine to it. More
+  // sutbly, it is also important that |task_| be destroyed on the origin
+  // sequence when it fails to run. This is because |task_| can own state which
+  // is affine to |reply_task_runner_| and was intended to be handed to
+  // |reply_|, e.g. https://crbug.com/829122. Since |task_| already needs to
+  // support deletion on the origin sequence (since the initial PostTask can
+  // always fail), it's safer to delete it there when PostTask succeeds but
+  // |task_| is later prevented from running.
+  //
+  // PostTaskAndReplyRelay's move semantics along with logic in this destructor
+  // enforce the above semantics in all the following cases :
+  //  1) Posting |task_| fails right away on the origin sequence:
+  //    a) |reply_task_runner_| is null (i.e. during late shutdown);
+  //    b) |reply_task_runner_| is set.
+  //  2) ~PostTaskAndReplyRelay() runs on the destination sequence:
+  //    a) RunTaskAndPostReply() is cancelled before running;
+  //    b) RunTaskAndPostReply() is skipped on shutdown;
+  //    c) Posting RunReply() fails.
+  //  3) ~PostTaskAndReplyRelay() runs on the origin sequence:
+  //    a) RunReply() is cancelled before running;
+  //    b) RunReply() is skipped on shutdown;
+  //    c) The DeleteSoon() posted by (2) runs.
+  //  4) ~PostTaskAndReplyRelay() should no-op:
+  //    a) This relay was moved to another relay instance;
+  //    b) RunReply() ran and completed this relay's mandate.
   ~PostTaskAndReplyRelay() {
-    if (reply_) {
-      // This can run:
-      // 1) On origin sequence, when:
-      //    1a) Posting |task_| fails.
-      //    1b) |reply_| is cancelled before running.
-      //    1c) The DeleteSoon() below is scheduled.
-      // 2) On destination sequence, when:
-      //    2a) |task_| is cancelled before running.
-      //    2b) Posting |reply_| fails.
-
-      if (!reply_task_runner_->RunsTasksInCurrentSequence()) {
-        // Case 2a) or 2b).
-        //
-        // Destroy callbacks asynchronously on |reply_task_runner| since their
-        // destructors can rightfully be affine to it. As always, DeleteSoon()
-        // might leak its argument if the target execution environment is
-        // shutdown (e.g. MessageLoop deleted, TaskScheduler shutdown).
-        //
-        // Note: while it's obvious why |reply_| can be affine to
-        // |reply_task_runner|, the reason that |task_| can also be affine to it
-        // is that it if neither tasks ran, |task_| may still hold an object
-        // which was intended to be moved to |reply_| when |task_| ran (such an
-        // object's destruction can be affine to |reply_task_runner_| -- e.g.
-        // https://crbug.com/829122).
-        auto relay_to_delete =
-            std::make_unique<PostTaskAndReplyRelay>(std::move(*this));
-        ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get());
-        reply_task_runner_->DeleteSoon(from_here_, std::move(relay_to_delete));
-      }
-
-      // Case 1a), 1b), 1c).
-      //
-      // Callbacks will be destroyed synchronously at the end of this scope.
-    } else {
-      // This can run when both callbacks have run or have been moved to another
-      // PostTaskAndReplyRelay instance. If |reply_| is null, |task_| must be
-      // null too.
-      DCHECK(!task_);
+    // Case 1a and 4a:
+    if (!reply_task_runner_) {
+      DCHECK_EQ(task_.is_null(), reply_.is_null());
+      return;
     }
+
+    // Case 4b:
+    if (!reply_) {
+      DCHECK(!task_);
+      return;
+    }
+
+    // Case 2:
+    if (!reply_task_runner_->RunsTasksInCurrentSequence()) {
+      DCHECK(reply_);
+
+      SequencedTaskRunner* reply_task_runner_raw = reply_task_runner_.get();
+      auto relay_to_delete =
+          std::make_unique<PostTaskAndReplyRelay>(std::move(*this));
+      // In case 2c, posting the DeleteSoon will also fail and |relay_to_delete|
+      // will be leaked. This only happens during shutdown and leaking is better
+      // than thread-unsafe execution.
+      ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get());
+      reply_task_runner_raw->DeleteSoon(from_here_, std::move(relay_to_delete));
+      return;
+    }
+
+    // Case 1b and 3: Any remaining state will be destroyed synchronously at the
+    // end of this scope.
   }
 
-  // No assignment operator because of const members.
+  // No assignment operator because of const member.
   PostTaskAndReplyRelay& operator=(PostTaskAndReplyRelay&&) = delete;
 
   // Static function is used because it is not possible to bind a method call to
@@ -78,12 +96,11 @@
     DCHECK(relay.task_);
     std::move(relay.task_).Run();
 
-    // Keep a reference to the reply TaskRunner for the PostTask() call before
+    // Keep a pointer to the reply TaskRunner for the PostTask() call before
     // |relay| is moved into a callback.
-    scoped_refptr<SequencedTaskRunner> reply_task_runner =
-        relay.reply_task_runner_;
+    SequencedTaskRunner* reply_task_runner_raw = relay.reply_task_runner_.get();
 
-    reply_task_runner->PostTask(
+    reply_task_runner_raw->PostTask(
         relay.from_here_,
         BindOnce(&PostTaskAndReplyRelay::RunReply, std::move(relay)));
   }
@@ -100,8 +117,8 @@
   const Location from_here_;
   OnceClosure task_;
   OnceClosure reply_;
-  const scoped_refptr<SequencedTaskRunner> reply_task_runner_ =
-      SequencedTaskRunnerHandle::Get();
+  // Not const to allow moving.
+  scoped_refptr<SequencedTaskRunner> reply_task_runner_;
 
   DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyRelay);
 };
@@ -116,10 +133,22 @@
   DCHECK(task) << from_here.ToString();
   DCHECK(reply) << from_here.ToString();
 
-  return PostTask(from_here,
-                  BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply,
-                           PostTaskAndReplyRelay(from_here, std::move(task),
-                                                 std::move(reply))));
+  const bool has_sequenced_context = SequencedTaskRunnerHandle::IsSet();
+
+  const bool post_task_success = PostTask(
+      from_here,
+      BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply,
+               PostTaskAndReplyRelay(
+                   from_here, std::move(task), std::move(reply),
+                   has_sequenced_context ? SequencedTaskRunnerHandle::Get()
+                                         : nullptr)));
+
+  // PostTaskAndReply() requires a SequencedTaskRunnerHandle to post the reply.
+  // Having no SequencedTaskRunnerHandle is allowed when posting the task fails,
+  // to simplify calls during shutdown (https://crbug.com/922938).
+  CHECK(has_sequenced_context || !post_task_success);
+
+  return post_task_success;
 }
 
 }  // namespace internal
diff --git a/base/threading/post_task_and_reply_impl_unittest.cc b/base/threading/post_task_and_reply_impl_unittest.cc
index 319327d..7f3ff77 100644
--- a/base/threading/post_task_and_reply_impl_unittest.cc
+++ b/base/threading/post_task_and_reply_impl_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/sequenced_task_runner_handle.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -70,6 +71,8 @@
           TestMockTimeTaskRunner::Type::kStandalone)
       : TestMockTimeTaskRunner(type) {}
 
+  void StopAcceptingTasks() { accepts_tasks_ = false; }
+
   void RunUntilIdleWithRunsTasksInCurrentSequence() {
     AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true);
     RunUntilIdle();
@@ -85,9 +88,20 @@
     return runs_tasks_in_current_sequence_;
   }
 
+  bool PostDelayedTask(const Location& from_here,
+                       OnceClosure task,
+                       TimeDelta delay) override {
+    if (!accepts_tasks_)
+      return false;
+
+    return TestMockTimeTaskRunner::PostDelayedTask(from_here, std::move(task),
+                                                   delay);
+  }
+
  private:
   ~MockRunsTasksInCurrentSequenceTaskRunner() override = default;
 
+  bool accepts_tasks_ = true;
   bool runs_tasks_in_current_sequence_ = false;
 
   DISALLOW_COPY_AND_ASSIGN(MockRunsTasksInCurrentSequenceTaskRunner);
@@ -97,16 +111,19 @@
  protected:
   PostTaskAndReplyImplTest() = default;
 
-  void PostTaskAndReplyToMockObject() {
+  bool PostTaskAndReplyToMockObject() {
+    return PostTaskAndReplyTaskRunner(post_runner_.get())
+        .PostTaskAndReply(
+            FROM_HERE,
+            BindOnce(&MockObject::Task, Unretained(&mock_object_),
+                     MakeRefCounted<ObjectToDelete>(&delete_task_flag_)),
+            BindOnce(&MockObject::Reply, Unretained(&mock_object_),
+                     MakeRefCounted<ObjectToDelete>(&delete_reply_flag_)));
+  }
+
+  void ExpectPostTaskAndReplyToMockObjectSucceeds() {
     // Expect the post to succeed.
-    EXPECT_TRUE(
-        PostTaskAndReplyTaskRunner(post_runner_.get())
-            .PostTaskAndReply(
-                FROM_HERE,
-                BindOnce(&MockObject::Task, Unretained(&mock_object_),
-                         MakeRefCounted<ObjectToDelete>(&delete_task_flag_)),
-                BindOnce(&MockObject::Reply, Unretained(&mock_object_),
-                         MakeRefCounted<ObjectToDelete>(&delete_reply_flag_))));
+    EXPECT_TRUE(PostTaskAndReplyToMockObject());
 
     // Expect the first task to be posted to |post_runner_|.
     EXPECT_TRUE(post_runner_->HasPendingTask());
@@ -131,7 +148,7 @@
 }  // namespace
 
 TEST_F(PostTaskAndReplyImplTest, PostTaskAndReply) {
-  PostTaskAndReplyToMockObject();
+  ExpectPostTaskAndReplyToMockObjectSucceeds();
 
   EXPECT_CALL(mock_object_, Task(_));
   post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
@@ -157,7 +174,7 @@
 }
 
 TEST_F(PostTaskAndReplyImplTest, TaskDoesNotRun) {
-  PostTaskAndReplyToMockObject();
+  ExpectPostTaskAndReplyToMockObjectSucceeds();
 
   // Clear the |post_runner_|. Both callbacks should be scheduled for deletion
   // on the |reply_runner_|.
@@ -174,7 +191,7 @@
 }
 
 TEST_F(PostTaskAndReplyImplTest, ReplyDoesNotRun) {
-  PostTaskAndReplyToMockObject();
+  ExpectPostTaskAndReplyToMockObjectSucceeds();
 
   EXPECT_CALL(mock_object_, Task(_));
   post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence();
@@ -194,5 +211,20 @@
   EXPECT_TRUE(delete_reply_flag_);
 }
 
+// This is a regression test for crbug.com/922938.
+TEST_F(PostTaskAndReplyImplTest,
+       PostTaskToStoppedTaskRunnerWithoutSequencedContext) {
+  reply_runner_.reset();
+  EXPECT_FALSE(SequencedTaskRunnerHandle::IsSet());
+  post_runner_->StopAcceptingTasks();
+
+  // Expect the post to return false, but not to crash.
+  EXPECT_FALSE(PostTaskAndReplyToMockObject());
+
+  // Expect all tasks to be deleted.
+  EXPECT_TRUE(delete_task_flag_);
+  EXPECT_TRUE(delete_reply_flag_);
+}
+
 }  // namespace internal
 }  // namespace base
diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h
index d3693135..c520c19a 100644
--- a/base/threading/thread_restrictions.h
+++ b/base/threading/thread_restrictions.h
@@ -245,6 +245,12 @@
 
 namespace base {
 
+namespace sequence_manager {
+namespace internal {
+class TaskQueueImpl;
+}
+}  // namespace sequence_manager
+
 namespace android {
 class JavaHandlerThread;
 }
@@ -420,6 +426,7 @@
       AwFormDatabaseService;  // http://crbug.com/904431
   friend class android_webview::CookieManager;
   friend class audio::OutputDevice;
+  friend class base::sequence_manager::internal::TaskQueueImpl;
   friend class base::FileDescriptorWatcher;
   friend class base::MessageLoopImpl;
   friend class base::ScopedAllowThreadRecallForStackSamplingProfiler;
diff --git a/base/trace_event/OWNERS b/base/trace_event/OWNERS
index 24a0bd2..aa3ab84 100644
--- a/base/trace_event/OWNERS
+++ b/base/trace_event/OWNERS
@@ -1,6 +1,8 @@
 chiniforooshan@chromium.org
+eseckler@chromium.org
 oysteine@chromium.org
 primiano@chromium.org
+skyostil@chromium.org
 per-file trace_event_android.cc=wangxianzhu@chromium.org
 
 # For memory-infra related changes
diff --git a/base/type_id.cc b/base/type_id.cc
index 76b53721..5269c36 100644
--- a/base/type_id.cc
+++ b/base/type_id.cc
@@ -7,13 +7,24 @@
 #include "base/strings/string_number_conversions.h"
 
 namespace base {
-namespace experimental {
+
+TypeId::TypeId(const char* function_name, internal::TypeUniqueId unique_type_id)
+    :
+#if DCHECK_IS_ON()
+      function_name_(function_name),
+#endif
+      unique_type_id_(unique_type_id) {
+}
+
+TypeId::TypeId() : TypeId("", internal::UniqueIdFromType<internal::NoType>()) {}
 
 std::string TypeId::ToString() const {
 #if DCHECK_IS_ON()
   return function_name_;
+#elif defined(COMPILER_MSVC)
+  return HexEncode(&unique_type_id_, sizeof(unique_type_id_));
 #else
-  return NumberToString(reinterpret_cast<uintptr_t>(type_id_));
+  return NumberToString(reinterpret_cast<uintptr_t>(unique_type_id_));
 #endif
 }
 
@@ -21,5 +32,4 @@
   return out << type_id.ToString();
 }
 
-}  // namespace experimental
 }  // namespace base
diff --git a/base/type_id.h b/base/type_id.h
index 0d3b463de..eb2d50f 100644
--- a/base/type_id.h
+++ b/base/type_id.h
@@ -14,56 +14,86 @@
 #include "base/strings/string_piece.h"
 #include "build/build_config.h"
 
-namespace base {
-// Not ready for public consumption yet.
-namespace experimental {
+#if defined(COMPILER_MSVC)
+#include <typeindex>
+#include <typeinfo>
+#endif
 
+namespace base {
+
+namespace internal {
+
+// The __attribute__((visibility("default"))) trick does not work for shared
+// libraries in MSVC. MSVC does support a reduced functionality typeid operator
+// with -fno-rtti, just enough for our needs.
+// Biggest drawback is that using typeid prevents us from having constexpr
+// methods (as the typeid operator is not constexpr)
+#if defined(COMPILER_MSVC)
+using TypeUniqueId = std::type_index;
+
+template <typename Type>
+inline TypeUniqueId UniqueIdFromType() {
+  return std::type_index(typeid(Type));
+}
+
+#else
 // A substitute for RTTI that uses the linker to uniquely reserve an address in
 // the binary for each type.
-class TypeId {
+// We need to make sure dummy_var has default visibility since we need to make
+// sure that there is only one definition across all libraries (shared or
+// static).
+template <typename Type>
+struct __attribute__((visibility("default"))) TypeTag {
+  static constexpr char dummy_var = 0;
+};
+
+// static
+template <typename Type>
+constexpr char TypeTag<Type>::dummy_var;
+
+using TypeUniqueId = const void*;
+
+template <typename Type>
+constexpr inline TypeUniqueId UniqueIdFromType() {
+  return &TypeTag<Type>::dummy_var;
+}
+#endif
+
+struct NoType {};
+
+}  // namespace internal
+
+class BASE_EXPORT TypeId {
  public:
-  bool constexpr operator==(const TypeId& other) const {
-    return type_id_ == other.type_id_;
+  template <typename T>
+  static TypeId From() {
+    return TypeId(PRETTY_FUNCTION, internal::UniqueIdFromType<T>());
   }
 
-  bool constexpr operator!=(const TypeId& other) const {
-    return !(*this == other);
+  TypeId();
+
+  TypeId(const TypeId& other) = default;
+  TypeId& operator=(const TypeId& other) = default;
+
+  bool operator==(TypeId other) const {
+    return unique_type_id_ == other.unique_type_id_;
   }
 
- public:
-  template <typename Type>
-  static constexpr TypeId Create() {
-    return TypeId(&TypeTag<Type>::dummy_var, PRETTY_FUNCTION);
-  }
+  bool operator!=(TypeId other) const { return !(*this == other); }
 
   std::string ToString() const;
 
  private:
-  template <typename Type>
-  struct TypeTag {
-    constexpr static char dummy_var = 0;
-  };
-
-  constexpr TypeId(const void* type_id, const char* function_name)
-      :
-#if DCHECK_IS_ON()
-        function_name_(function_name),
-#endif
-        type_id_(type_id) {
-  }
+  TypeId(const char* function_name, internal::TypeUniqueId unique_type_id);
 
 #if DCHECK_IS_ON()
-  const char* const function_name_;
+  const char* function_name_;
 #endif
-  const void* const type_id_;
+  internal::TypeUniqueId unique_type_id_;
 };
 
-template <typename Type>
-constexpr char TypeId::TypeTag<Type>::dummy_var;
-
 BASE_EXPORT std::ostream& operator<<(std::ostream& out, const TypeId& type_id);
 
-}  // namespace experimental
 }  // namespace base
 
 #endif  // BASE_TYPE_ID_H_
diff --git a/base/type_id_unittest.cc b/base/type_id_unittest.cc
index 00a5fede..31699f8e 100644
--- a/base/type_id_unittest.cc
+++ b/base/type_id_unittest.cc
@@ -4,12 +4,13 @@
 
 #include "base/type_id.h"
 
+#include <memory>
+
 #include "base/test/type_id_test_support_a.h"
 #include "base/test/type_id_test_support_b.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace base {
-namespace experimental {
 namespace {
 
 struct T {};
@@ -19,55 +20,39 @@
 }  // namespace
 
 TEST(TypeId, Basic) {
-  static_assert(TypeId::Create<int>() == TypeId::Create<int>(), "");
-  static_assert(TypeId::Create<int>() != TypeId::Create<void>(), "");
-  static_assert(TypeId::Create<int>() != TypeId::Create<float>(), "");
-  static_assert(TypeId::Create<int>() != TypeId::Create<std::unique_ptr<T>>(),
-                "");
-  static_assert(TypeId::Create<int>() != TypeId::Create<std::unique_ptr<U>>(),
-                "");
+  EXPECT_EQ(TypeId::From<int>(), TypeId::From<int>());
+  EXPECT_NE(TypeId::From<int>(), TypeId::From<void>());
+  EXPECT_NE(TypeId::From<int>(), TypeId::From<float>());
+  EXPECT_NE(TypeId::From<int>(), TypeId::From<std::unique_ptr<T>>());
+  EXPECT_NE(TypeId::From<int>(), TypeId::From<std::unique_ptr<U>>());
 
-  static_assert(TypeId::Create<void>() != TypeId::Create<int>(), "");
-  static_assert(TypeId::Create<void>() == TypeId::Create<void>(), "");
-  static_assert(TypeId::Create<void>() != TypeId::Create<float>(), "");
-  static_assert(TypeId::Create<void>() != TypeId::Create<std::unique_ptr<T>>(),
-                "");
-  static_assert(TypeId::Create<void>() != TypeId::Create<std::unique_ptr<U>>(),
-                "");
+  EXPECT_NE(TypeId::From<void>(), TypeId::From<int>());
+  EXPECT_EQ(TypeId::From<void>(), TypeId::From<void>());
+  EXPECT_NE(TypeId::From<void>(), TypeId::From<float>());
+  EXPECT_NE(TypeId::From<void>(), TypeId::From<std::unique_ptr<T>>());
+  EXPECT_NE(TypeId::From<void>(), TypeId::From<std::unique_ptr<U>>());
 
-  static_assert(TypeId::Create<float>() != TypeId::Create<int>(), "");
-  static_assert(TypeId::Create<float>() != TypeId::Create<void>(), "");
-  static_assert(TypeId::Create<float>() == TypeId::Create<float>(), "");
-  static_assert(TypeId::Create<float>() != TypeId::Create<std::unique_ptr<T>>(),
-                "");
-  static_assert(TypeId::Create<float>() != TypeId::Create<std::unique_ptr<U>>(),
-                "");
+  EXPECT_NE(TypeId::From<float>(), TypeId::From<int>());
+  EXPECT_NE(TypeId::From<float>(), TypeId::From<void>());
+  EXPECT_EQ(TypeId::From<float>(), TypeId::From<float>());
+  EXPECT_NE(TypeId::From<float>(), TypeId::From<std::unique_ptr<T>>());
+  EXPECT_NE(TypeId::From<float>(), TypeId::From<std::unique_ptr<U>>());
 
-  static_assert(TypeId::Create<std::unique_ptr<T>>() != TypeId::Create<int>(),
-                "");
-  static_assert(TypeId::Create<std::unique_ptr<T>>() != TypeId::Create<void>(),
-                "");
-  static_assert(TypeId::Create<std::unique_ptr<T>>() != TypeId::Create<float>(),
-                "");
-  static_assert(TypeId::Create<std::unique_ptr<T>>() ==
-                    TypeId::Create<std::unique_ptr<T>>(),
-                "");
-  static_assert(TypeId::Create<std::unique_ptr<T>>() !=
-                    TypeId::Create<std::unique_ptr<U>>(),
-                "");
+  EXPECT_NE(TypeId::From<std::unique_ptr<T>>(), TypeId::From<int>());
+  EXPECT_NE(TypeId::From<std::unique_ptr<T>>(), TypeId::From<void>());
+  EXPECT_NE(TypeId::From<std::unique_ptr<T>>(), TypeId::From<float>());
+  EXPECT_EQ(TypeId::From<std::unique_ptr<T>>(),
+            TypeId::From<std::unique_ptr<T>>());
+  EXPECT_NE(TypeId::From<std::unique_ptr<T>>(),
+            TypeId::From<std::unique_ptr<U>>());
 
-  static_assert(TypeId::Create<std::unique_ptr<U>>() != TypeId::Create<int>(),
-                "");
-  static_assert(TypeId::Create<std::unique_ptr<U>>() != TypeId::Create<void>(),
-                "");
-  static_assert(TypeId::Create<std::unique_ptr<U>>() != TypeId::Create<float>(),
-                "");
-  static_assert(TypeId::Create<std::unique_ptr<U>>() !=
-                    TypeId::Create<std::unique_ptr<T>>(),
-                "");
-  static_assert(TypeId::Create<std::unique_ptr<U>>() ==
-                    TypeId::Create<std::unique_ptr<U>>(),
-                "");
+  EXPECT_NE(TypeId::From<std::unique_ptr<U>>(), TypeId::From<int>());
+  EXPECT_NE(TypeId::From<std::unique_ptr<U>>(), TypeId::From<void>());
+  EXPECT_NE(TypeId::From<std::unique_ptr<U>>(), TypeId::From<float>());
+  EXPECT_NE(TypeId::From<std::unique_ptr<U>>(),
+            TypeId::From<std::unique_ptr<T>>());
+  EXPECT_EQ(TypeId::From<std::unique_ptr<U>>(),
+            TypeId::From<std::unique_ptr<U>>());
 }
 
 TEST(TypeId, TypesInAnonymousNameSpacesDontCollide) {
@@ -85,12 +70,11 @@
             TypeIdTestSupportB::GetTypeIdForUniquePtrInt());
 }
 
-TEST(TypeId, DISABLED_IdenticalTypesFromComponentAndStaticLibrary) {
+TEST(TypeId, IdenticalTypesFromComponentAndStaticLibrary) {
   // Code generated for the test itself is statically linked. Make sure it works
   // with components
-  constexpr TypeId static_linked_type = TypeId::Create<std::unique_ptr<int>>();
+  TypeId static_linked_type = TypeId::From<std::unique_ptr<int>>();
   EXPECT_EQ(static_linked_type, TypeIdTestSupportA::GetTypeIdForUniquePtrInt());
 }
 
-}  // namespace experimental
 }  // namespace base
diff --git a/build/android/docs/coverage.md b/build/android/docs/coverage.md
index 3c6c7c3..14dbef6 100644
--- a/build/android/docs/coverage.md
+++ b/build/android/docs/coverage.md
@@ -15,10 +15,10 @@
 
 1. Use the following GN build arguments:
 
-```
-    target_os = "android"
-    emma_coverage = true
-    emma_filter = "org.chromium.chrome.browser.ntp.*,-*Test*,-*Fake*,-*Mock*"
+```gn
+target_os = "android"
+emma_coverage = true
+emma_filter = "org.chromium.chrome.browser.ntp.*,-*Test*,-*Fake*,-*Mock*"
 ```
 
 The filter syntax is as documented for the [EMMA coverage
@@ -43,14 +43,14 @@
 6. Now we have both .em and .ec files. We can create a html report using
    `generate_emma_html.py`, for example:
 
-```
+   ```shell
    build/android/generate_emma_html.py \
        --coverage-dir /tmp/coverage/ \
        --metadata-dir out/Debug/ \
        --output example.html
-```
+   ```
    Then an example.html containing coverage info will be created:
 
-```
+   ```
    EMMA: writing [html] report to [<your_current_directory>/example.html] ...
-```
+   ```
diff --git a/build/android/docs/life_of_a_resource.md b/build/android/docs/life_of_a_resource.md
index f3a6419..bd1ffcd 100644
--- a/build/android/docs/life_of_a_resource.md
+++ b/build/android/docs/life_of_a_resource.md
@@ -197,7 +197,7 @@
 
 This is how a sample R.java file looks like:
 
-```
+```java
 package org.chromium.ui;
 
 public final class R {
diff --git a/build/android/gyp/merge_manifest.py b/build/android/gyp/merge_manifest.py
index 23432aa7..7745385 100755
--- a/build/android/gyp/merge_manifest.py
+++ b/build/android/gyp/merge_manifest.py
@@ -9,26 +9,33 @@
 import argparse
 import contextlib
 import os
+import shlex
 import sys
 import tempfile
 import xml.dom.minidom as minidom
+import xml.etree.ElementTree as ElementTree
 
 from util import build_utils
 from util import diff_utils
 
 # Tools library directory - relative to Android SDK root
-SDK_TOOLS_LIB_DIR = os.path.join('tools', 'lib')
+_SDK_TOOLS_LIB_DIR = os.path.join('tools', 'lib')
 
-MANIFEST_MERGER_MAIN_CLASS = 'com.android.manifmerger.Merger'
-MANIFEST_MERGER_JARS = [
-  'common{suffix}.jar',
-  'manifest-merger{suffix}.jar',
-  'sdk-common{suffix}.jar',
-  'sdklib{suffix}.jar',
+_MANIFEST_MERGER_MAIN_CLASS = 'com.android.manifmerger.Merger'
+_MANIFEST_MERGER_JARS = [
+    'common{suffix}.jar',
+    'manifest-merger{suffix}.jar',
+    'sdk-common{suffix}.jar',
+    'sdklib{suffix}.jar',
 ]
 
-TOOLS_NAMESPACE_PREFIX = 'tools'
-TOOLS_NAMESPACE = 'http://schemas.android.com/tools'
+_TOOLS_NAMESPACE_PREFIX = 'tools'
+_TOOLS_NAMESPACE = 'http://schemas.android.com/tools'
+_ANDROID_NAMESPACE = 'http://schemas.android.com/apk/res/android'
+
+# Without registering namespaces ElementTree converts them to "ns0" and "ns1"
+ElementTree.register_namespace('tools', _TOOLS_NAMESPACE)
+ElementTree.register_namespace('android', _ANDROID_NAMESPACE)
 
 
 @contextlib.contextmanager
@@ -44,7 +51,7 @@
   manifest = manifests[0]
   package = manifest.getAttribute('package')
 
-  manifest.setAttribute('xmlns:%s' % TOOLS_NAMESPACE_PREFIX, TOOLS_NAMESPACE)
+  manifest.setAttribute('xmlns:%s' % _TOOLS_NAMESPACE_PREFIX, _TOOLS_NAMESPACE)
 
   tmp_prefix = os.path.basename(manifest_path)
   with tempfile.NamedTemporaryFile(prefix=tmp_prefix) as patched_manifest:
@@ -55,14 +62,46 @@
 
 def _BuildManifestMergerClasspath(build_vars):
   return ':'.join([
-    os.path.join(
-      build_vars['android_sdk_root'],
-      SDK_TOOLS_LIB_DIR,
-      jar.format(suffix=build_vars['android_sdk_tools_version_suffix']))
-    for jar in MANIFEST_MERGER_JARS
+      os.path.join(
+          build_vars['android_sdk_root'], _SDK_TOOLS_LIB_DIR,
+          jar.format(suffix=build_vars['android_sdk_tools_version_suffix']))
+      for jar in _MANIFEST_MERGER_JARS
   ])
 
 
+def _SortAndStripElementTree(tree, reverse_toplevel=False):
+  for node in tree:
+    if node.text and node.text.isspace():
+      node.text = None
+    _SortAndStripElementTree(node)
+  tree[:] = sorted(tree, key=ElementTree.tostring, reverse=reverse_toplevel)
+
+
+def _NormalizeManifest(path):
+  with open(path) as f:
+    # This also strips comments and sorts node attributes alphabetically.
+    root = ElementTree.fromstring(f.read())
+
+  # Sort nodes alphabetically, recursively.
+  _SortAndStripElementTree(root, reverse_toplevel=True)
+
+  # Fix up whitespace/indentation.
+  dom = minidom.parseString(ElementTree.tostring(root))
+  lines = []
+  for l in dom.toprettyxml(indent='  ').splitlines():
+    if l.strip():
+      if len(l) > 100:
+        indent = ' ' * l.find('<')
+        attributes = shlex.split(l, posix=False)
+        lines.append('{}{}'.format(indent, attributes[0]))
+        for attribute in attributes[1:]:
+          lines.append('{}    {}'.format(indent, attribute))
+      else:
+        lines.append(l)
+
+  return '\n'.join(lines)
+
+
 def main(argv):
   argv = build_utils.ExpandFileArgs(argv)
   parser = argparse.ArgumentParser(description=__doc__)
@@ -75,6 +114,7 @@
                       required=True)
   parser.add_argument(
       '--expected-manifest', help='Expected contents for the merged manifest.')
+  parser.add_argument('--normalized-output', help='Normalized merged manifest.')
   parser.add_argument(
       '--verify-expected-manifest',
       action='store_true',
@@ -87,13 +127,14 @@
   classpath = _BuildManifestMergerClasspath(
       build_utils.ReadBuildVars(args.build_vars))
 
-  with build_utils.AtomicOutput(args.output) as f:
+  with build_utils.AtomicOutput(args.output) as output:
     cmd = [
-      'java',
-      '-cp',
-      classpath,
-      MANIFEST_MERGER_MAIN_CLASS,
-      '--out', f.name,
+        'java',
+        '-cp',
+        classpath,
+        _MANIFEST_MERGER_MAIN_CLASS,
+        '--out',
+        output.name,
     ]
 
     extras = build_utils.ParseGnList(args.extras)
@@ -107,10 +148,13 @@
         # https://issuetracker.google.com/issues/63514300:
         # The merger doesn't set a nonzero exit code for failures.
         fail_func=lambda returncode, stderr: returncode != 0 or
-          build_utils.IsTimeStale(f.name, [root_manifest] + extras))
+          build_utils.IsTimeStale(output.name, [root_manifest] + extras))
 
   if args.expected_manifest:
-    diff = diff_utils.DiffFileContents(args.expected_manifest, args.output)
+    with build_utils.AtomicOutput(args.normalized_output) as normalized_output:
+      normalized_output.write(_NormalizeManifest(args.output))
+    diff = diff_utils.DiffFileContents(args.expected_manifest,
+                                       args.normalized_output)
     if diff:
       print """
 {}
@@ -121,7 +165,7 @@
 
 See https://chromium.googlesource.com/chromium/src/+/HEAD/chrome/android/java/README.md
 for more info.
-""".format(diff, os.path.abspath(args.output),
+""".format(diff, os.path.abspath(args.normalized_output),
            os.path.abspath(args.expected_manifest))
       if args.verify_expected_manifest:
         sys.exit(1)
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index 59fc2c2..7bd38f6 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -1751,9 +1751,13 @@
 
       if (defined(invoker.expected_manifest)) {
         inputs += [ invoker.expected_manifest ]
+        _normalized_output = "${invoker.output_manifest}.normalized"
+        outputs += [ _normalized_output ]
         args += [
           "--expected-manifest",
           rebase_path(invoker.expected_manifest, root_build_dir),
+          "--normalized-output",
+          rebase_path(_normalized_output, root_build_dir),
         ]
         if (check_android_configuration) {
           args += [ "--verify-expected-manifest" ]
diff --git a/build/config/compiler/compiler.gni b/build/config/compiler/compiler.gni
index 817efb4..5b60633c 100644
--- a/build/config/compiler/compiler.gni
+++ b/build/config/compiler/compiler.gni
@@ -171,9 +171,11 @@
 declare_args() {
   # Set to true to use lld, the LLVM linker.
   # https://crbug.com/911658 for using lld on 32-bit linux.
+  # https://crbug.com/917504 for arm chromeos
   use_lld = is_clang &&
             (is_win || is_fuchsia || is_android ||
-             (is_linux && target_os != "chromeos" && current_cpu != "x86"))
+             (is_linux && target_os != "chromeos" && current_cpu != "x86") ||
+             (target_os == "chromeos" && current_cpu != "arm"))
 }
 
 declare_args() {
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index 5884a063..c0a6fdd 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-590f4e2abc471dca242f054e08dac2e07c5e255b
\ No newline at end of file
+6490ad40d973625dc27bf59b3ade9323a5edb814
\ No newline at end of file
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index 1d2b4f0..79e7668 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-8a2c5b509acacad27fcb2f3f288c5736d5a4e057
\ No newline at end of file
+4aaf262c95ceab6d163bf7e6411b31f91135fc85
\ No newline at end of file
diff --git a/build/fuchsia/update_sdk.py b/build/fuchsia/update_sdk.py
index 90a5622..f7d6115 100755
--- a/build/fuchsia/update_sdk.py
+++ b/build/fuchsia/update_sdk.py
@@ -7,6 +7,7 @@
 entry so that it only runs when .gclient's target_os includes 'fuchsia'."""
 
 import os
+import re
 import shutil
 import subprocess
 import sys
@@ -24,17 +25,49 @@
 SDK_SUBDIRS = ["arch", "pkg", "qemu", "sysroot", "target",
                "toolchain_libs", "tools"]
 
+EXTRA_SDK_HASH_PREFIX = ''
+
+def GetSdkGeneration(hash):
+  if not hash:
+    return None
+
+  cmd = [os.path.join(find_depot_tools.DEPOT_TOOLS_PATH, 'gsutil.py'), 'ls',
+         '-L', GetBucketForPlatform() + hash]
+  sdk_details = subprocess.check_output(cmd)
+  m = re.search('Generation:\s*(\d*)', sdk_details)
+  if not m:
+    return None
+  return int(m.group(1))
+
+
 def GetSdkHashForPlatform():
   filename = '{platform}.sdk.sha1'.format(platform =  GetHostOsFromPlatform())
-  hash_file = os.path.join(os.path.dirname(__file__), filename)
 
+  # Get the hash of the SDK in chromium.
+  sdk_hash = None
+  hash_file = os.path.join(os.path.dirname(__file__), filename)
   with open(hash_file, 'r') as f:
     sdk_hash = f.read().strip()
 
-  if not sdk_hash:
-    print >>sys.stderr, 'No SHA1 found in %s' % hash_file
+  # Get the hash of the SDK with the extra prefix.
+  extra_sdk_hash = None
+  if EXTRA_SDK_HASH_PREFIX:
+    extra_hash_file = os.path.join(os.path.dirname(__file__),
+                                   EXTRA_SDK_HASH_PREFIX + filename)
+    with open(extra_hash_file, 'r') as f:
+      extra_sdk_hash = f.read().strip()
+
+  # If both files are empty, return an error.
+  if not sdk_hash and not extra_sdk_hash:
+    print >>sys.stderr, 'No SHA1 found in {} or {}'.format(
+        hash_file, extra_hash_file)
     return 1
 
+  # Return the newer SDK based on the generation number.
+  sdk_generation = GetSdkGeneration(sdk_hash)
+  extra_sdk_generation = GetSdkGeneration(extra_sdk_hash)
+  if extra_sdk_generation > sdk_generation:
+    return extra_sdk_hash
   return sdk_hash
 
 def GetBucketForPlatform():
diff --git a/build/util/lastchange.py b/build/util/lastchange.py
index fd55ce3..fb17d04 100755
--- a/build/util/lastchange.py
+++ b/build/util/lastchange.py
@@ -7,20 +7,23 @@
 lastchange.py -- Chromium revision fetching utility.
 """
 
-import re
-import logging
 import argparse
+import collections
+import logging
 import os
 import subprocess
 import sys
 
-class VersionInfo(object):
-  def __init__(self, revision_id, full_revision_string, timestamp):
-    self.revision_id = revision_id
-    self.revision = full_revision_string
-    self.timestamp = timestamp
+VersionInfo = collections.namedtuple("VersionInfo",
+                                     ("revision_id", "revision", "timestamp"))
 
+class GitError(Exception):
+  pass
 
+# This function exists for compatibility with logic outside this
+# repository that uses this file as a library.
+# TODO(eliribble) remove this function after it has been ported into
+# the repositories that depend on it
 def RunGitCommand(directory, command):
   """
   Launches git subcommand.
@@ -49,50 +52,97 @@
     return None
 
 
-def FetchGitRevision(directory, filter):
+def _RunGitCommand(directory, command):
   """
-  Fetch the Git hash (and Cr-Commit-Position if any) for a given directory.
-
-  Errors are swallowed.
+  Launches git subcommand.
 
   Returns:
-    A VersionInfo object or None on error.
+    The stripped stdout of the git command.
+  Raises:
+    GitError on failure, including a nonzero return code.
   """
-  hsh = ''
-  git_args = ['log', '-1', '--format=%H %ct']
-  if filter is not None:
-    git_args.append('--grep=' + filter)
-  proc = RunGitCommand(directory, git_args)
-  if proc:
-    output = proc.communicate()[0].strip()
-    if proc.returncode == 0 and output:
-      hsh, ct = output.split()
-    else:
-      logging.error('Git error: rc=%d, output=%r' %
-                    (proc.returncode, output))
-  if not hsh:
-    return None
-  pos = ''
-  proc = RunGitCommand(directory, ['cat-file', 'commit', hsh])
-  if proc:
-    output = proc.communicate()[0]
-    if proc.returncode == 0 and output:
-      for line in reversed(output.splitlines()):
-        if line.startswith('Cr-Commit-Position:'):
-          pos = line.rsplit()[-1].strip()
-          break
-  return VersionInfo(hsh, '%s-%s' % (hsh, pos), int(ct))
+  command = ['git'] + command
+  # Force shell usage under cygwin. This is a workaround for
+  # mysterious loss of cwd while invoking cygwin's git.
+  # We can't just pass shell=True to Popen, as under win32 this will
+  # cause CMD to be used, while we explicitly want a cygwin shell.
+  if sys.platform == 'cygwin':
+    command = ['sh', '-c', ' '.join(command)]
+  try:
+    logging.info("Executing '%s' in %s", ' '.join(command), directory)
+    proc = subprocess.Popen(command,
+                            stdout=subprocess.PIPE,
+                            stderr=subprocess.PIPE,
+                            cwd=directory,
+                            shell=(sys.platform=='win32'))
+    stdout, stderr = proc.communicate()
+    stdout = stdout.strip()
+    logging.debug("returncode: %d", proc.returncode)
+    logging.debug("stdout: %s", stdout)
+    logging.debug("stderr: %s", stderr)
+    if proc.returncode != 0 or not stdout:
+      raise GitError((
+          "Git command 'git {}' in {} failed: "
+          "rc={}, stdout='{}' stderr='{}'").format(
+          " ".join(command), directory, proc.returncode, stdout, stderr))
+    return stdout
+  except OSError as e:
+    raise GitError("Git command 'git {}' in {} failed: {}".format(
+        " ".join(command), directory, e))
 
+def GetMergeBase(directory, ref):
+  """
+  Return the merge-base of HEAD and ref.
 
-def FetchVersionInfo(directory=None, filter=None):
+  Args:
+    directory: The directory containing the .git directory.
+    ref: The ref to use to find the merge base.
+  Returns:
+    The git commit SHA of the merge-base as a string.
+  """
+  logging.debug("Calculating merge base between HEAD and %s in %s",
+                ref, directory)
+  command = ['merge-base', 'HEAD', ref]
+  return _RunGitCommand(directory, command)
+
+def FetchGitRevision(directory, commit_filter, start_commit="HEAD"):
   """
   Returns the last change (as a VersionInfo object)
   from some appropriate revision control system.
+
+  Args:
+    directory: The directory containing the .git directory.
+    commit_filter: A filter to supply to grep to filter commits
+    start_commit: A commit identifier. The result of this function
+      will be limited to only consider commits before the provided
+      commit.
+  Returns:
+    A VersionInfo object. On error all values will be 0.
   """
-  version_info = FetchGitRevision(directory, filter)
-  if not version_info:
-    version_info = VersionInfo('0', '0', 0)
-  return version_info
+  hash_ = ''
+
+  git_args = ['log', '-1', '--format=%H %ct']
+  if commit_filter is not None:
+    git_args.append('--grep=' + commit_filter)
+
+  git_args.append(start_commit)
+
+  output = _RunGitCommand(directory, git_args)
+  hash_, commit_timestamp = output.split()
+  if not hash_:
+    return VersionInfo('0', '0', 0)
+
+  revision = hash_
+  output = _RunGitCommand(directory, ['cat-file', 'commit', hash_])
+  for line in reversed(output.splitlines()):
+    if line.startswith('Cr-Commit-Position:'):
+      pos = line.rsplit()[-1].strip()
+      logging.debug("Found Cr-Commit-Position '%s'", pos)
+      revision = "{}-{}".format(hash_, pos)
+      break
+  return VersionInfo(hash_, revision, int(commit_timestamp))
+
+
 
 
 def GetHeaderGuard(path):
@@ -133,6 +183,16 @@
   return header_contents
 
 
+def GetGitTopDirectory(source_dir):
+  """Get the top git directory - the directory that contains the .git directory.
+
+  Args:
+    source_dir: The directory to search.
+  Returns:
+    The output of "git rev-parse --show-toplevel" as a string
+  """
+  return _RunGitCommand(source_dir, ['rev-parse', '--show-toplevel'])
+
 def WriteIfChanged(file_name, contents):
   """
   Writes the specified contents to the specified file_name
@@ -157,20 +217,23 @@
 
   parser = argparse.ArgumentParser(usage="lastchange.py [options]")
   parser.add_argument("-m", "--version-macro",
-                    help="Name of C #define when using --header. Defaults to " +
-                    "LAST_CHANGE.",
-                    default="LAST_CHANGE")
+                    help=("Name of C #define when using --header. Defaults to "
+                          "LAST_CHANGE."))
   parser.add_argument("-o", "--output", metavar="FILE",
-                    help="Write last change to FILE. " +
-                    "Can be combined with --header to write both files.")
+                    help=("Write last change to FILE. "
+                          "Can be combined with --header to write both files."))
   parser.add_argument("--header", metavar="FILE",
                     help=("Write last change to FILE as a C/C++ header. "
                           "Can be combined with --output to write both files."))
+  parser.add_argument("--merge-base-ref",
+                    default=None,
+                    help=("Only consider changes since the merge "
+                          "base between HEAD and the provided ref"))
   parser.add_argument("--revision-id-only", action='store_true',
                     help=("Output the revision as a VCS revision ID only (in "
                           "Git, a 40-character commit hash, excluding the "
                           "Cr-Commit-Position)."))
-  parser.add_argument("--print-only", action='store_true',
+  parser.add_argument("--print-only", action="store_true",
                     help=("Just print the revision string. Overrides any "
                           "file-output-related options."))
   parser.add_argument("-s", "--source-dir", metavar="DIR",
@@ -180,13 +243,14 @@
                           "matches the supplied filter regex. Defaults to "
                           "'^Change-Id:' to suppress local commits."),
                     default='^Change-Id:')
+
   args, extras = parser.parse_known_args(argv[1:])
 
   logging.basicConfig(level=logging.WARNING)
 
   out_file = args.output
   header = args.header
-  filter=args.filter
+  commit_filter = args.filter
 
   while len(extras) and out_file is None:
     if out_file is None:
@@ -196,18 +260,37 @@
     parser.print_help()
     sys.exit(2)
 
-  if args.source_dir:
-    src_dir = args.source_dir
-  else:
-    src_dir = os.path.dirname(os.path.abspath(__file__))
+  source_dir = args.source_dir or os.path.dirname(os.path.abspath(__file__))
+  try:
+    git_top_dir = GetGitTopDirectory(source_dir)
+  except GitError as e:
+    logging.error("Failed to get git top directory from '%s': %s",
+                  source_dir, e)
+    return 2
 
-  version_info = FetchVersionInfo(directory=src_dir, filter=filter)
+  if args.merge_base_ref:
+    try:
+      merge_base_sha = GetMergeBase(git_top_dir, args.merge_base_ref)
+    except GitError as e:
+      logging.error("You requested a --merge-base-ref value of '%s' but no "
+                    "merge base could be found between it and HEAD. Git "
+                    "reports: %s", args.merge_base_ref, e)
+      return 3
+  else:
+    merge_base_sha = 'HEAD'
+
+  try:
+    version_info = FetchGitRevision(git_top_dir, commit_filter, merge_base_sha)
+  except GitError as e:
+    logging.error("Failed to get version info: %s")
+    return 1
+
   revision_string = version_info.revision
   if args.revision_id_only:
     revision_string = version_info.revision_id
 
   if args.print_only:
-    print revision_string
+    print(revision_string)
   else:
     contents = "LASTCHANGE=%s\n" % revision_string
     if not out_file and not args.header:
diff --git a/cc/animation/animation_host_unittest.cc b/cc/animation/animation_host_unittest.cc
index f4fd4805..c780b92 100644
--- a/cc/animation/animation_host_unittest.cc
+++ b/cc/animation/animation_host_unittest.cc
@@ -37,7 +37,7 @@
     client_impl_.RegisterElement(element_id_, ElementListType::ACTIVE);
 
     worklet_animation_ = WorkletAnimation::Create(
-        worklet_animation_id_, "test_name", nullptr, nullptr);
+        worklet_animation_id_, "test_name", 1, nullptr, nullptr);
     int cc_id = worklet_animation_->id();
     worklet_animation_->AttachElement(element_id_);
     host_->AddAnimationTimeline(timeline_);
@@ -334,7 +334,7 @@
 
   // Create a worklet animation that is bound to the scroll timeline.
   scoped_refptr<WorkletAnimation> worklet_animation(
-      new WorkletAnimation(animation_id2, worklet_animation_id, "test_name",
+      new WorkletAnimation(animation_id2, worklet_animation_id, "test_name", 1,
                            std::move(scroll_timeline), nullptr, true));
   worklet_animation->AttachElement(element_id);
   timeline_->AttachAnimation(worklet_animation);
diff --git a/cc/animation/animation_unittest.cc b/cc/animation/animation_unittest.cc
index cb09ab0..ab18ff0 100644
--- a/cc/animation/animation_unittest.cc
+++ b/cc/animation/animation_unittest.cc
@@ -642,9 +642,9 @@
 
   const scoped_refptr<ElementAnimations> element_animations =
       host_->GetElementAnimationsForElementId(element_id_);
-  EXPECT_TRUE(element_animations->keyframe_effects_list().HasObserver(
+  EXPECT_TRUE(element_animations->HasKeyframeEffectForTesting(
       animation_->GetKeyframeEffectById(keyframe_effect_id1)));
-  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
+  EXPECT_FALSE(element_animations->HasKeyframeEffectForTesting(
       animation_->GetKeyframeEffectById(keyframe_effect_id2)));
 
   animation_->AttachElementForKeyframeEffect(element_id_, keyframe_effect_id2);
@@ -654,16 +654,16 @@
   EXPECT_TRUE(animation_->GetKeyframeEffectById(keyframe_effect_id2)
                   ->needs_push_properties());
 
-  EXPECT_TRUE(element_animations->keyframe_effects_list().HasObserver(
+  EXPECT_TRUE(element_animations->HasKeyframeEffectForTesting(
       animation_->GetKeyframeEffectById(keyframe_effect_id2)));
 
   host_->PushPropertiesTo(host_impl_);
 
   const scoped_refptr<ElementAnimations> element_animations_impl =
       host_impl_->GetElementAnimationsForElementId(element_id_);
-  EXPECT_TRUE(element_animations_impl->keyframe_effects_list().HasObserver(
+  EXPECT_TRUE(element_animations_impl->HasKeyframeEffectForTesting(
       animation_impl_->GetKeyframeEffectById(keyframe_effect_id1)));
-  EXPECT_TRUE(element_animations_impl->keyframe_effects_list().HasObserver(
+  EXPECT_TRUE(element_animations_impl->HasKeyframeEffectForTesting(
       animation_impl_->GetKeyframeEffectById(keyframe_effect_id2)));
 
   EXPECT_TRUE(animation_impl_->element_animations(keyframe_effect_id1));
@@ -676,17 +676,17 @@
   animation_->DetachElement();
   EXPECT_FALSE(animation_->element_animations(keyframe_effect_id1));
   EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id1));
-  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
+  EXPECT_FALSE(element_animations->HasKeyframeEffectForTesting(
       animation_->GetKeyframeEffectById(keyframe_effect_id1)));
 
   EXPECT_FALSE(animation_->element_animations(keyframe_effect_id2));
   EXPECT_FALSE(animation_->element_id_of_keyframe_effect(keyframe_effect_id2));
-  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
+  EXPECT_FALSE(element_animations->HasKeyframeEffectForTesting(
       animation_->GetKeyframeEffectById(keyframe_effect_id2)));
 
-  EXPECT_TRUE(element_animations_impl->keyframe_effects_list().HasObserver(
+  EXPECT_TRUE(element_animations_impl->HasKeyframeEffectForTesting(
       animation_impl_->GetKeyframeEffectById(keyframe_effect_id1)));
-  EXPECT_TRUE(element_animations_impl->keyframe_effects_list().HasObserver(
+  EXPECT_TRUE(element_animations_impl->HasKeyframeEffectForTesting(
       animation_impl_->GetKeyframeEffectById(keyframe_effect_id2)));
 
   host_->PushPropertiesTo(host_impl_);
@@ -694,12 +694,12 @@
   EXPECT_FALSE(animation_impl_->element_animations(keyframe_effect_id1));
   EXPECT_FALSE(
       animation_impl_->element_id_of_keyframe_effect(keyframe_effect_id1));
-  EXPECT_FALSE(element_animations_impl->keyframe_effects_list().HasObserver(
+  EXPECT_FALSE(element_animations_impl->HasKeyframeEffectForTesting(
       animation_impl_->GetKeyframeEffectById(keyframe_effect_id1)));
   EXPECT_FALSE(animation_impl_->element_animations(keyframe_effect_id2));
   EXPECT_FALSE(
       animation_impl_->element_id_of_keyframe_effect(keyframe_effect_id2));
-  EXPECT_FALSE(element_animations_impl->keyframe_effects_list().HasObserver(
+  EXPECT_FALSE(element_animations_impl->HasKeyframeEffectForTesting(
       animation_impl_->GetKeyframeEffectById(keyframe_effect_id2)));
 
   timeline_->DetachAnimation(animation_);
@@ -776,18 +776,18 @@
                   ->needs_push_properties());
 
   element_animations = host_->GetElementAnimationsForElementId(element1);
-  EXPECT_TRUE(element_animations->keyframe_effects_list().HasObserver(
+  EXPECT_TRUE(element_animations->HasKeyframeEffectForTesting(
       animation_->GetKeyframeEffectById(keyframe_effect_id1)));
-  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
+  EXPECT_FALSE(element_animations->HasKeyframeEffectForTesting(
       animation_->GetKeyframeEffectById(keyframe_effect_id2)));
 
   element_animations = host_->GetElementAnimationsForElementId(element2);
-  EXPECT_TRUE(element_animations->keyframe_effects_list().HasObserver(
+  EXPECT_TRUE(element_animations->HasKeyframeEffectForTesting(
       animation_->GetKeyframeEffectById(keyframe_effect_id2)));
 
   animation_->DetachElement();
   EXPECT_TRUE(animation_->animation_timeline());
-  EXPECT_FALSE(element_animations->keyframe_effects_list().HasObserver(
+  EXPECT_FALSE(element_animations->HasKeyframeEffectForTesting(
       animation_->GetKeyframeEffectById(keyframe_effect_id2)));
   EXPECT_FALSE(animation_->GetKeyframeEffectById(keyframe_effect_id1)
                    ->element_animations());
diff --git a/cc/animation/element_animations.cc b/cc/animation/element_animations.cc
index 8ef6cc5..64fdf5f 100644
--- a/cc/animation/element_animations.cc
+++ b/cc/animation/element_animations.cc
@@ -519,6 +519,24 @@
   return PropertyToElementIdMap(std::move(entries));
 }
 
+unsigned int ElementAnimations::CountKeyframesForTesting() const {
+  unsigned int count = 0;
+  for (auto it = keyframe_effects_list_.begin();
+       it != keyframe_effects_list_.end(); it++)
+    count++;
+  return count;
+}
+
+KeyframeEffect* ElementAnimations::FirstKeyframeEffectForTesting() const {
+  DCHECK(keyframe_effects_list_.might_have_observers());
+  return &*keyframe_effects_list_.begin();
+}
+
+bool ElementAnimations::HasKeyframeEffectForTesting(
+    const KeyframeEffect* keyframe) const {
+  return keyframe_effects_list_.HasObserver(keyframe);
+}
+
 bool ElementAnimations::KeyframeModelAffectsActiveElements(
     KeyframeModel* keyframe_model) const {
   // When we force a keyframe_model update due to a notification, we do not have
diff --git a/cc/animation/element_animations.h b/cc/animation/element_animations.h
index 3f35069..8d960dc 100644
--- a/cc/animation/element_animations.h
+++ b/cc/animation/element_animations.h
@@ -19,10 +19,6 @@
 #include "ui/gfx/geometry/scroll_offset.h"
 #include "ui/gfx/transform.h"
 
-namespace gfx {
-class BoxF;
-}
-
 namespace cc {
 
 class AnimationHost;
@@ -64,11 +60,6 @@
   void RemoveKeyframeEffect(KeyframeEffect* keyframe_effect);
   bool IsEmpty() const;
 
-  typedef base::ObserverList<KeyframeEffect>::Unchecked KeyframeEffectsList;
-  const KeyframeEffectsList& keyframe_effects_list() const {
-    return keyframe_effects_list_;
-  }
-
   // Ensures that the list of active animations on the main thread and the impl
   // thread are kept in sync. This function does not take ownership of the impl
   // thread ElementAnimations.
@@ -117,9 +108,6 @@
     has_element_in_pending_list_ = has_element_in_pending_list;
   }
 
-  bool TransformAnimationBoundsForBox(const gfx::BoxF& box,
-                                      gfx::BoxF* bounds) const;
-
   bool HasOnlyTranslationTransforms(ElementListType list_type) const;
 
   bool AnimationsPreserveAxisAlignment() const;
@@ -137,8 +125,6 @@
   bool ScrollOffsetAnimationWasInterrupted() const;
 
   void SetNeedsPushProperties();
-  bool needs_push_properties() const { return needs_push_properties_; }
-
   void UpdateClientAnimationState();
 
   void NotifyClientFloatAnimated(float opacity,
@@ -172,6 +158,10 @@
   // this is required to let BGPT ship (see http://crbug.com/912574).
   PropertyToElementIdMap GetPropertyToElementIdMap() const;
 
+  unsigned int CountKeyframesForTesting() const;
+  KeyframeEffect* FirstKeyframeEffectForTesting() const;
+  bool HasKeyframeEffectForTesting(const KeyframeEffect* keyframe) const;
+
  private:
   friend class base::RefCounted<ElementAnimations>;
 
@@ -200,7 +190,7 @@
   bool KeyframeModelAffectsActiveElements(KeyframeModel* keyframe_model) const;
   bool KeyframeModelAffectsPendingElements(KeyframeModel* keyframe_model) const;
 
-  KeyframeEffectsList keyframe_effects_list_;
+  base::ObserverList<KeyframeEffect>::Unchecked keyframe_effects_list_;
   AnimationHost* animation_host_;
   ElementId element_id_;
 
diff --git a/cc/animation/element_animations_unittest.cc b/cc/animation/element_animations_unittest.cc
index beb43dbe..1d8175a 100644
--- a/cc/animation/element_animations_unittest.cc
+++ b/cc/animation/element_animations_unittest.cc
@@ -200,12 +200,7 @@
       animation_impl_->keyframe_effect()->element_animations();
   EXPECT_TRUE(element_animations_impl);
 
-  const ElementAnimations::KeyframeEffectsList& keyframe_effects =
-      element_animations_impl_->keyframe_effects_list();
-  int list_size_before = 0;
-  for (auto it = keyframe_effects.begin(); it != keyframe_effects.end(); ++it)
-    ++list_size_before;
-  EXPECT_EQ(3, list_size_before);
+  EXPECT_EQ(3u, element_animations_impl_->CountKeyframesForTesting());
 
   animation2->DetachElement();
   EXPECT_FALSE(animation2->keyframe_effect()->element_animations());
@@ -218,10 +213,7 @@
   EXPECT_EQ(element_animations_impl,
             animation_impl_->keyframe_effect()->element_animations());
 
-  int list_size_after = 0;
-  for (auto it = keyframe_effects.begin(); it != keyframe_effects.end(); ++it)
-    ++list_size_after;
-  EXPECT_EQ(2, list_size_after);
+  EXPECT_EQ(2u, element_animations_impl_->CountKeyframesForTesting());
 }
 
 TEST_F(ElementAnimationsTest, SyncNewAnimation) {
diff --git a/cc/animation/worklet_animation.cc b/cc/animation/worklet_animation.cc
index b6f3590..ffbf24c0 100644
--- a/cc/animation/worklet_animation.cc
+++ b/cc/animation/worklet_animation.cc
@@ -4,6 +4,7 @@
 
 #include "cc/animation/worklet_animation.h"
 
+#include <utility>
 #include "cc/animation/animation_id_provider.h"
 #include "cc/animation/keyframe_effect.h"
 #include "cc/animation/scroll_timeline.h"
@@ -15,12 +16,14 @@
     int cc_animation_id,
     WorkletAnimationId worklet_animation_id,
     const std::string& name,
+    double playback_rate,
     std::unique_ptr<ScrollTimeline> scroll_timeline,
     std::unique_ptr<AnimationOptions> options,
     bool is_controlling_instance)
     : WorkletAnimation(cc_animation_id,
                        worklet_animation_id,
                        name,
+                       playback_rate,
                        std::move(scroll_timeline),
                        std::move(options),
                        is_controlling_instance,
@@ -30,6 +33,7 @@
     int cc_animation_id,
     WorkletAnimationId worklet_animation_id,
     const std::string& name,
+    double playback_rate,
     std::unique_ptr<ScrollTimeline> scroll_timeline,
     std::unique_ptr<AnimationOptions> options,
     bool is_controlling_instance,
@@ -38,6 +42,7 @@
       worklet_animation_id_(worklet_animation_id),
       name_(name),
       scroll_timeline_(std::move(scroll_timeline)),
+      playback_rate_(playback_rate),
       options_(std::move(options)),
       local_time_(base::nullopt),
       start_time_(base::nullopt),
@@ -50,11 +55,12 @@
 scoped_refptr<WorkletAnimation> WorkletAnimation::Create(
     WorkletAnimationId worklet_animation_id,
     const std::string& name,
+    double playback_rate,
     std::unique_ptr<ScrollTimeline> scroll_timeline,
     std::unique_ptr<AnimationOptions> options) {
   return WrapRefCounted(new WorkletAnimation(
       AnimationIdProvider::NextAnimationId(), worklet_animation_id, name,
-      std::move(scroll_timeline), std::move(options), false));
+      playback_rate, std::move(scroll_timeline), std::move(options), false));
 }
 
 scoped_refptr<Animation> WorkletAnimation::CreateImplInstance() const {
@@ -62,9 +68,9 @@
   if (scroll_timeline_)
     impl_timeline = scroll_timeline_->CreateImplInstance();
 
-  return WrapRefCounted(new WorkletAnimation(id(), worklet_animation_id_,
-                                             name(), std::move(impl_timeline),
-                                             CloneOptions(), true));
+  return WrapRefCounted(
+      new WorkletAnimation(id(), worklet_animation_id_, name(), playback_rate_,
+                           std::move(impl_timeline), CloneOptions(), true));
 }
 
 void WorkletAnimation::PushPropertiesTo(Animation* animation_impl) {
@@ -74,6 +80,7 @@
     scroll_timeline_->PushPropertiesTo(
         worklet_animation_impl->scroll_timeline_.get());
   }
+  worklet_animation_impl->SetPlaybackRate(playback_rate_);
 }
 
 void WorkletAnimation::Tick(base::TimeTicks monotonic_time) {
@@ -140,7 +147,34 @@
   local_time_ = state.local_times[0];
 }
 
-// TODO(crbug.com/780151): Multiply the result by the play back rate.
+void WorkletAnimation::SetPlaybackRate(double playback_rate) {
+  if (playback_rate == playback_rate_)
+    return;
+
+  // Setting playback rate is rejected in the blink side if any of the
+  // conditions below is false.
+  DCHECK(playback_rate_ && !scroll_timeline_);
+
+  if (start_time_ && last_current_time_) {
+    // Update startTime in order to maintain previous currentTime and,
+    // as a result, prevent the animation from jumping.
+    base::TimeDelta current_time =
+        base::TimeDelta::FromMillisecondsD(last_current_time_.value());
+    start_time_ = start_time_.value() + current_time / playback_rate_ -
+                  current_time / playback_rate;
+  }
+  playback_rate_ = playback_rate;
+}
+
+void WorkletAnimation::UpdatePlaybackRate(double playback_rate) {
+  if (playback_rate == playback_rate_)
+    return;
+  playback_rate_ = playback_rate;
+  SetNeedsPushProperties();
+}
+
+// TODO(gerchiko): Implement support playback_rate for scroll-linked
+// animations. http://crbug.com/852475.
 double WorkletAnimation::CurrentTime(base::TimeTicks monotonic_time,
                                      const ScrollTree& scroll_tree,
                                      bool is_active_tree) {
@@ -148,7 +182,8 @@
   // by the start time. See: https://github.com/w3c/csswg-drafts/issues/2075
   if (scroll_timeline_)
     return scroll_timeline_->CurrentTime(scroll_tree, is_active_tree);
-  return (monotonic_time - start_time_.value()).InMillisecondsF();
+  return (monotonic_time - start_time_.value()).InMillisecondsF() *
+         playback_rate_;
 }
 
 bool WorkletAnimation::NeedsUpdate(base::TimeTicks monotonic_time,
diff --git a/cc/animation/worklet_animation.h b/cc/animation/worklet_animation.h
index fc8e67e..d558e1b 100644
--- a/cc/animation/worklet_animation.h
+++ b/cc/animation/worklet_animation.h
@@ -25,6 +25,13 @@
 // A WorkletAnimation is an animation that allows its animation
 // timing to be controlled by an animator instance that is running in a
 // AnimationWorkletGlobalScope.
+
+// Two instances of this class are created for Blink WorkletAnimation:
+// 1. UI thread instance that keeps all the meta data.
+// 2. Impl thread instance that ticks the animations on the Impl thread.
+// When Blink WorkletAnimation is updated, it calls the UI thread instance to
+// modify its properties. The updated properties are pushed by the UI thread
+// instance to the Impl thread instance during commit.
 class CC_ANIMATION_EXPORT WorkletAnimation final
     : public SingleKeyframeEffectAnimation {
  public:
@@ -32,12 +39,14 @@
   WorkletAnimation(int cc_animation_id,
                    WorkletAnimationId worklet_animation_id,
                    const std::string& name,
+                   double playback_rate,
                    std::unique_ptr<ScrollTimeline> scroll_timeline,
                    std::unique_ptr<AnimationOptions> options,
                    bool is_controlling_instance);
   static scoped_refptr<WorkletAnimation> Create(
       WorkletAnimationId worklet_animation_id,
       const std::string& name,
+      double playback_rate,
       std::unique_ptr<ScrollTimeline> scroll_timeline,
       std::unique_ptr<AnimationOptions> options);
   scoped_refptr<Animation> CreateImplInstance() const override;
@@ -70,6 +79,12 @@
   // require updating the ElementId for the ScrollTimeline scroll source.
   void PromoteScrollTimelinePendingToActive() override;
 
+  // Called by Blink WorkletAnimation when its playback rate is updated.
+  void UpdatePlaybackRate(double playback_rate);
+  void SetPlaybackRateForTesting(double playback_rate) {
+    SetPlaybackRate(playback_rate);
+  }
+
   void RemoveKeyframeModel(int keyframe_model_id) override;
 
  private:
@@ -78,6 +93,7 @@
   WorkletAnimation(int cc_animation_id,
                    WorkletAnimationId worklet_animation_id,
                    const std::string& name,
+                   double playback_rate,
                    std::unique_ptr<ScrollTimeline> scroll_timeline,
                    std::unique_ptr<AnimationOptions> options,
                    bool is_controlling_instance,
@@ -101,6 +117,10 @@
     return options_ ? options_->Clone() : nullptr;
   }
 
+  // Updates the playback rate of the Impl thread instance.
+  // Called by the UI thread WorletAnimation instance during commit.
+  void SetPlaybackRate(double playback_rate);
+
   WorkletAnimationId worklet_animation_id_;
   std::string name_;
 
@@ -112,6 +132,15 @@
   // some other future implementation.
   std::unique_ptr<ScrollTimeline> scroll_timeline_;
 
+  // Controls speed of the animation.
+  // https://drafts.csswg.org/web-animations-2/#animation-effect-playback-rate
+
+  // For UI thread instance contains the meta value to be pushed to the Impl
+  // thread instance.
+  // For the Impl thread instance contains the actual playback rate of the
+  // animation.
+  double playback_rate_;
+
   std::unique_ptr<AnimationOptions> options_;
 
   // Local time is used as an input to the keyframe effect of this animation.
diff --git a/cc/animation/worklet_animation_unittest.cc b/cc/animation/worklet_animation_unittest.cc
index 68600f49..6662d62 100644
--- a/cc/animation/worklet_animation_unittest.cc
+++ b/cc/animation/worklet_animation_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "cc/animation/worklet_animation.h"
 
+#include <utility>
 #include "base/memory/ptr_util.h"
 #include "cc/animation/scroll_timeline.h"
 #include "cc/test/animation_test_common.h"
@@ -35,7 +36,7 @@
     client_.RegisterElement(element_id_, ElementListType::ACTIVE);
 
     worklet_animation_ = WrapRefCounted(
-        new WorkletAnimation(1, worklet_animation_id_, "test_name", nullptr,
+        new WorkletAnimation(1, worklet_animation_id_, "test_name", 1, nullptr,
                              nullptr, true /* controlling instance*/));
     worklet_animation_->AttachElement(element_id_);
     host_->AddAnimationTimeline(timeline_);
@@ -64,7 +65,7 @@
 
   scoped_refptr<WorkletAnimation> worklet_animation =
       WrapRefCounted(new WorkletAnimation(
-          1, worklet_animation_id_, "test_name", nullptr, nullptr,
+          1, worklet_animation_id_, "test_name", 1, nullptr, nullptr,
           false /* not impl instance*/, std::move(effect)));
 
   EXPECT_CALL(*mock_effect, Tick(_)).Times(0);
@@ -109,8 +110,9 @@
 TEST_F(WorkletAnimationTest, CurrentTimeCorrectlyUsesScrollTimeline) {
   auto scroll_timeline = std::make_unique<MockScrollTimeline>();
   EXPECT_CALL(*scroll_timeline, CurrentTime(_, _)).WillRepeatedly(Return(1234));
-  scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
-      worklet_animation_id_, "test_name", std::move(scroll_timeline), nullptr);
+  scoped_refptr<WorkletAnimation> worklet_animation =
+      WorkletAnimation::Create(worklet_animation_id_, "test_name", 1,
+                               std::move(scroll_timeline), nullptr);
 
   ScrollTree scroll_tree;
   std::unique_ptr<MutatorInputState> state =
@@ -125,7 +127,7 @@
 TEST_F(WorkletAnimationTest,
        CurrentTimeFromRegularTimelineIsOffsetByStartTime) {
   scoped_refptr<WorkletAnimation> worklet_animation = WorkletAnimation::Create(
-      worklet_animation_id_, "test_name", nullptr, nullptr);
+      worklet_animation_id_, "test_name", 1, nullptr, nullptr);
 
   base::TimeTicks first_ticks =
       base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111);
@@ -156,6 +158,56 @@
   EXPECT_EQ(246.8, input->updated_animations[0].current_time);
 }
 
+// Verifies correctness of current time when playback rate is set on
+// initializing the animation and while the animation is playing.
+TEST_F(WorkletAnimationTest, DocumentTimelineSetPlaybackRate) {
+  const double playback_rate_double = 2;
+  const double playback_rate_half = 0.5;
+  scoped_refptr<WorkletAnimation> worklet_animation =
+      WorkletAnimation::Create(worklet_animation_id_, "test_name",
+                               playback_rate_double, nullptr, nullptr);
+
+  base::TimeTicks first_ticks =
+      base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111);
+  base::TimeTicks second_ticks =
+      base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111 + 123.4);
+  base::TimeTicks third_ticks =
+      base::TimeTicks() +
+      base::TimeDelta::FromMillisecondsD(111 + 123.4 + 200.0);
+
+  ScrollTree scroll_tree;
+  std::unique_ptr<MutatorInputState> state =
+      std::make_unique<MutatorInputState>();
+  // Start the animation.
+  worklet_animation->UpdateInputState(state.get(), first_ticks, scroll_tree,
+                                      true);
+  state.reset(new MutatorInputState);
+
+  // Play until second_ticks.
+  worklet_animation->UpdateInputState(state.get(), second_ticks, scroll_tree,
+                                      true);
+  std::unique_ptr<AnimationWorkletInput> input =
+      state->TakeWorkletState(worklet_animation_id_.worklet_id);
+
+  // Verify that the current time is updated twice faster than the timeline
+  // time.
+  EXPECT_EQ(123.4 * playback_rate_double,
+            input->updated_animations[0].current_time);
+
+  // Update the playback rate.
+  worklet_animation->SetPlaybackRateForTesting(playback_rate_half);
+  state.reset(new MutatorInputState());
+
+  // Play until third_ticks.
+  worklet_animation->UpdateInputState(state.get(), third_ticks, scroll_tree,
+                                      true);
+  input = state->TakeWorkletState(worklet_animation_id_.worklet_id);
+
+  // Verify that the current time is updated half as fast as the timeline time.
+  EXPECT_EQ(123.4 * playback_rate_double + 200.0 * playback_rate_half,
+            input->updated_animations[0].current_time);
+}
+
 // This test verifies that worklet animation state is properly updated.
 TEST_F(WorkletAnimationTest, UpdateInputStateProducesCorrectState) {
   AttachWorkletAnimation();
diff --git a/cc/paint/paint_canvas.h b/cc/paint/paint_canvas.h
index eb6f05de..2577a4f 100644
--- a/cc/paint/paint_canvas.h
+++ b/cc/paint/paint_canvas.h
@@ -12,6 +12,7 @@
 #include "cc/paint/paint_export.h"
 #include "cc/paint/paint_image.h"
 #include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkMetaData.h"
 #include "third_party/skia/include/core/SkTextBlob.h"
 
 namespace cc {
@@ -41,7 +42,7 @@
   PaintCanvas() {}
   virtual ~PaintCanvas() {}
 
-  virtual SkMetaData& getMetaData() = 0;
+  SkMetaData& getMetaData() { return metadata_; }
 
   // TODO(enne): this only appears to mostly be used to determine if this is
   // recording or not, so could be simplified or removed.
@@ -185,6 +186,8 @@
   virtual void recordCustomData(uint32_t id) {}
 
  private:
+  SkMetaData metadata_;
+
   DISALLOW_COPY_AND_ASSIGN(PaintCanvas);
 };
 
diff --git a/cc/paint/record_paint_canvas.cc b/cc/paint/record_paint_canvas.cc
index 080cd03..83656541 100644
--- a/cc/paint/record_paint_canvas.cc
+++ b/cc/paint/record_paint_canvas.cc
@@ -12,7 +12,6 @@
 #include "cc/paint/paint_recorder.h"
 #include "cc/paint/skottie_wrapper.h"
 #include "third_party/skia/include/core/SkAnnotation.h"
-#include "third_party/skia/include/core/SkMetaData.h"
 #include "third_party/skia/include/utils/SkNWayCanvas.h"
 
 namespace cc {
@@ -25,12 +24,6 @@
 
 RecordPaintCanvas::~RecordPaintCanvas() = default;
 
-SkMetaData& RecordPaintCanvas::getMetaData() {
-  // This could just be SkMetaData owned by RecordPaintCanvas, but since
-  // SkCanvas already has one, we might as well use it directly.
-  return GetCanvas()->getMetaData();
-}
-
 SkImageInfo RecordPaintCanvas::imageInfo() const {
   return GetCanvas()->imageInfo();
 }
diff --git a/cc/paint/record_paint_canvas.h b/cc/paint/record_paint_canvas.h
index 5b1c795..b0a75cc7 100644
--- a/cc/paint/record_paint_canvas.h
+++ b/cc/paint/record_paint_canvas.h
@@ -27,7 +27,6 @@
   RecordPaintCanvas(DisplayItemList* list, const SkRect& bounds);
   ~RecordPaintCanvas() override;
 
-  SkMetaData& getMetaData() override;
   SkImageInfo imageInfo() const override;
 
   void flush() override;
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc
index 62060b8..b5c5743 100644
--- a/cc/paint/skia_paint_canvas.cc
+++ b/cc/paint/skia_paint_canvas.cc
@@ -10,7 +10,6 @@
 #include "cc/paint/scoped_raster_flags.h"
 #include "third_party/skia/include/core/SkAnnotation.h"
 #include "third_party/skia/include/core/SkColorSpaceXformCanvas.h"
-#include "third_party/skia/include/core/SkMetaData.h"
 #include "third_party/skia/include/core/SkRegion.h"
 #include "third_party/skia/include/gpu/GrContext.h"
 #include "third_party/skia/include/utils/SkNWayCanvas.h"
@@ -58,10 +57,6 @@
   }
 }
 
-SkMetaData& SkiaPaintCanvas::getMetaData() {
-  return canvas_->getMetaData();
-}
-
 SkImageInfo SkiaPaintCanvas::imageInfo() const {
   return canvas_->imageInfo();
 }
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h
index 38abfe8..b3f665a 100644
--- a/cc/paint/skia_paint_canvas.h
+++ b/cc/paint/skia_paint_canvas.h
@@ -48,7 +48,6 @@
 
   void reset_image_provider() { image_provider_ = nullptr; }
 
-  SkMetaData& getMetaData() override;
   SkImageInfo imageInfo() const override;
 
   void flush() override;
diff --git a/cc/paint/solid_color_analyzer.cc b/cc/paint/solid_color_analyzer.cc
index df0dd78..9d3ed90 100644
--- a/cc/paint/solid_color_analyzer.cc
+++ b/cc/paint/solid_color_analyzer.cc
@@ -7,6 +7,7 @@
 #include <cmath>
 
 #include "base/trace_event/trace_event.h"
+#include "build/build_config.h"
 #include "cc/paint/paint_op_buffer.h"
 #include "third_party/skia/include/core/SkTypes.h"
 #include "third_party/skia/include/utils/SkNoDrawCanvas.h"
@@ -75,10 +76,19 @@
   // Paint is solid color if the following holds:
   // - Style is fill, and there are no special effects.
   // - Blend mode is either kSrc or kSrcOver.
-  return IsSolidColorBlendMode(blendmode) && !flags.HasShader() &&
-         !flags.getLooper() && !flags.getMaskFilter() &&
-         !flags.getColorFilter() && !flags.getImageFilter() &&
-         flags.getStyle() == PaintFlags::kFill_Style;
+  bool is_solid_color =
+      IsSolidColorBlendMode(blendmode) && !flags.HasShader() &&
+      !flags.getLooper() && !flags.getMaskFilter() && !flags.getColorFilter() &&
+      !flags.getImageFilter() && flags.getStyle() == PaintFlags::kFill_Style;
+
+#if defined(OS_MACOSX)
+  // Additionally, on Mac, we require that the color is opaque due to
+  // https://crbug.com/922899.
+  // TODO(andrescj): remove this condition once that bug is fixed.
+  is_solid_color = (is_solid_color && SkColorGetA(flags.getColor()) == 255);
+#endif  // OS_MACOSX
+
+  return is_solid_color;
 }
 
 // Returns true if the specified |drawn_shape| will cover the entire canvas
@@ -153,7 +163,17 @@
   else if (alpha != 0 || blendmode != SkBlendMode::kSrc)
     *is_transparent = false;
 
-  if (does_cover_canvas && IsSolidColorBlendMode(blendmode)) {
+  bool solid_color_candidate =
+      does_cover_canvas && IsSolidColorBlendMode(blendmode);
+
+#if defined(OS_MACOSX)
+  // Additionally, on Mac, we require that the color is opaque due to
+  // https://crbug.com/922899.
+  // TODO(andrescj): remove this condition once that bug is fixed.
+  solid_color_candidate = (solid_color_candidate && alpha == 255);
+#endif  // OS_MACOSX
+
+  if (solid_color_candidate) {
     CalculateSolidColor(color /* src_color */, blendmode,
                         out_color /* dst_color */, is_solid_color);
   } else {
diff --git a/cc/paint/solid_color_analyzer_unittest.cc b/cc/paint/solid_color_analyzer_unittest.cc
index e932c5f..3c95df2b 100644
--- a/cc/paint/solid_color_analyzer_unittest.cc
+++ b/cc/paint/solid_color_analyzer_unittest.cc
@@ -7,6 +7,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/optional.h"
 #include "base/stl_util.h"
+#include "build/build_config.h"
 #include "cc/paint/display_item_list.h"
 #include "cc/paint/paint_filter.h"
 #include "cc/paint/record_paint_canvas.h"
@@ -96,7 +97,13 @@
   Initialize();
   SkColor color = SkColorSetARGB(128, 11, 22, 33);
   canvas()->clear(color);
+#if defined(OS_MACOSX)
+  // TODO(andrescj): remove the special treatment of OS_MACOSX once
+  // https://crbug.com/922899 is fixed.
+  EXPECT_FALSE(IsSolidColor());
+#else
   EXPECT_EQ(color, GetColor());
+#endif  // OS_MACOSX
 }
 
 TEST_F(SolidColorAnalyzerTest, DrawColor) {
@@ -288,7 +295,13 @@
   flags.setColor(color);
   SkRect rect = SkRect::MakeWH(100, 100);
   canvas()->drawRect(rect, flags);
+#if defined(OS_MACOSX)
+  // TODO(andrescj): remove the special treatment of OS_MACOSX once
+  // https://crbug.com/922899 is fixed.
+  EXPECT_FALSE(IsSolidColor());
+#else
   EXPECT_EQ(color, GetColor());
+#endif  // OS_MACOSX
 }
 
 TEST_F(SolidColorAnalyzerTest, DrawRectTranslucentOverNonSolid) {
@@ -330,8 +343,14 @@
   flags.setColor(color);
   rect = SkRect::MakeWH(100, 100);
   canvas()->drawRect(rect, flags);
+#if defined(OS_MACOSX)
+  // TODO(andrescj): remove the special treatment of OS_MACOSX once
+  // https://crbug.com/922899 is fixed.
+  EXPECT_FALSE(IsSolidColor());
+#else
   EXPECT_EQ(SkColorSetARGB(159, 15, 25, 35),
             GetColor(2 /* max_ops_to_analyze */));
+#endif  // OS_MACOSX
 }
 
 TEST_F(SolidColorAnalyzerTest, SaveLayer) {
diff --git a/cc/test/animation_test_common.cc b/cc/test/animation_test_common.cc
index 65173e3..4516f8e2 100644
--- a/cc/test/animation_test_common.cc
+++ b/cc/test/animation_test_common.cc
@@ -324,9 +324,8 @@
   scoped_refptr<ElementAnimations> element_animations =
       timeline->animation_host()->GetElementAnimationsForElementId(element_id);
   DCHECK(element_animations);
-  DCHECK(element_animations->keyframe_effects_list().might_have_observers());
   KeyframeEffect* keyframe_effect =
-      &*element_animations->keyframe_effects_list().begin();
+      element_animations->FirstKeyframeEffectForTesting();
   DCHECK(keyframe_effect);
   keyframe_effect->AddKeyframeModel(std::move(keyframe_model));
 }
@@ -338,9 +337,8 @@
   scoped_refptr<ElementAnimations> element_animations =
       timeline->animation_host()->GetElementAnimationsForElementId(element_id);
   DCHECK(element_animations);
-  DCHECK(element_animations->keyframe_effects_list().might_have_observers());
   KeyframeEffect* keyframe_effect =
-      &*element_animations->keyframe_effects_list().begin();
+      element_animations->FirstKeyframeEffectForTesting();
   DCHECK(keyframe_effect);
   keyframe_effect->RemoveKeyframeModel(keyframe_model_id);
 }
@@ -352,9 +350,8 @@
   scoped_refptr<ElementAnimations> element_animations =
       timeline->animation_host()->GetElementAnimationsForElementId(element_id);
   DCHECK(element_animations);
-  DCHECK(element_animations->keyframe_effects_list().might_have_observers());
   KeyframeEffect* keyframe_effect =
-      &*element_animations->keyframe_effects_list().begin();
+      element_animations->FirstKeyframeEffectForTesting();
   DCHECK(keyframe_effect);
   return keyframe_effect->GetKeyframeModelById(keyframe_model_id);
 }
diff --git a/cc/test/animation_timelines_test_common.cc b/cc/test/animation_timelines_test_common.cc
index a015f50..0a09931 100644
--- a/cc/test/animation_timelines_test_common.cc
+++ b/cc/test/animation_timelines_test_common.cc
@@ -448,7 +448,7 @@
   const scoped_refptr<ElementAnimations> element_animations =
       host_->GetElementAnimationsForElementId(element_id);
   return element_animations
-             ? &*element_animations->keyframe_effects_list().begin()
+             ? element_animations->FirstKeyframeEffectForTesting()
              : nullptr;
 }
 
@@ -457,7 +457,7 @@
   const scoped_refptr<ElementAnimations> element_animations =
       host_impl_->GetElementAnimationsForElementId(element_id);
   return element_animations
-             ? &*element_animations->keyframe_effects_list().begin()
+             ? element_animations->FirstKeyframeEffectForTesting()
              : nullptr;
 }
 
diff --git a/cc/test/fake_proxy.cc b/cc/test/fake_proxy.cc
index 68c9da8d..4690e78 100644
--- a/cc/test/fake_proxy.cc
+++ b/cc/test/fake_proxy.cc
@@ -38,4 +38,8 @@
   return false;
 }
 
+uint32_t FakeProxy::GenerateChildSurfaceSequenceNumberSync() {
+  return 0u;
+}
+
 }  // namespace cc
diff --git a/cc/test/fake_proxy.h b/cc/test/fake_proxy.h
index 491a0bf..2ebebfba2 100644
--- a/cc/test/fake_proxy.h
+++ b/cc/test/fake_proxy.h
@@ -50,6 +50,7 @@
   void ClearHistory() override {}
   void SetRenderFrameObserver(
       std::unique_ptr<RenderFrameMetadataObserver> observer) override {}
+  uint32_t GenerateChildSurfaceSequenceNumberSync() override;
 
  private:
   LayerTreeHost* layer_tree_host_;
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 5aca1c6..60124b2e9 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -70,6 +70,19 @@
 }
 
 namespace cc {
+namespace {
+
+bool AreEmbedTokensEqual(const viz::LocalSurfaceId& lsi1,
+                         const viz::LocalSurfaceId& lsi2) {
+  return lsi1.embed_token() == lsi2.embed_token();
+}
+
+bool AreParentSequencesEqual(const viz::LocalSurfaceId& lsi1,
+                             const viz::LocalSurfaceId& lsi2) {
+  return lsi1.parent_sequence_number() == lsi2.parent_sequence_number();
+}
+
+}  // namespace
 
 LayerTreeHost::InitParams::InitParams() = default;
 LayerTreeHost::InitParams::~InitParams() = default;
@@ -761,11 +774,12 @@
         outer_viewport_scroll_layer(), overscroll_elasticity_element_id(),
         elastic_overscroll_, page_scale_factor_, device_scale_factor_,
         gfx::Rect(device_viewport_size_), identity_transform, &property_trees_);
-    TRACE_EVENT_INSTANT1("cc", "LayerTreeHost::UpdateLayers_BuiltPropertyTrees",
+    TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
+                         "LayerTreeHost::UpdateLayers_BuiltPropertyTrees",
                          TRACE_EVENT_SCOPE_THREAD, "property_trees",
                          property_trees_.AsTracedValue());
   } else {
-    TRACE_EVENT_INSTANT1("cc",
+    TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
                          "LayerTreeHost::UpdateLayers_ReceivedPropertyTrees",
                          TRACE_EVENT_SCOPE_THREAD, "property_trees",
                          property_trees_.AsTracedValue());
@@ -1314,6 +1328,10 @@
   content_source_id_ = id;
 }
 
+void LayerTreeHost::ClearCachesOnNextCommit() {
+  clear_caches_on_next_commit_ = true;
+}
+
 void LayerTreeHost::SetLocalSurfaceIdAllocationFromParent(
     const viz::LocalSurfaceIdAllocation&
         local_surface_id_allocation_from_parent) {
@@ -1346,18 +1364,29 @@
   // parent agreed upon these without needing to further advance its sequence
   // number. When this occurs the child is already up-to-date and a commit here
   // is simply redundant.
-  if (current_local_surface_id_from_parent.parent_sequence_number() ==
-          local_surface_id_from_parent.parent_sequence_number() &&
-      current_local_surface_id_from_parent.embed_token() ==
-          local_surface_id_from_parent.embed_token()) {
+  //
+  // If |generated_child_surface_sequence_number_| is set, it means a child
+  // sequence number was generated and needs to be compared against.
+  if (AreEmbedTokensEqual(current_local_surface_id_from_parent,
+                          local_surface_id_from_parent) &&
+      AreParentSequencesEqual(current_local_surface_id_from_parent,
+                              local_surface_id_from_parent) &&
+      (!generated_child_surface_sequence_number_ ||
+       local_surface_id_from_parent.child_sequence_number() <
+           *generated_child_surface_sequence_number_)) {
     return;
   }
+  generated_child_surface_sequence_number_.reset();
+
   UpdateDeferMainFrameUpdateInternal();
   SetNeedsCommit();
 }
 
-void LayerTreeHost::ClearCachesOnNextCommit() {
-  clear_caches_on_next_commit_ = true;
+uint32_t LayerTreeHost::GenerateChildSurfaceSequenceNumberSync() {
+  DCHECK(proxy_);
+  generated_child_surface_sequence_number_ =
+      proxy_->GenerateChildSurfaceSequenceNumberSync();
+  return *generated_child_surface_sequence_number_;
 }
 
 void LayerTreeHost::RequestNewLocalSurfaceId() {
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index fdfbdad..d463d77 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -22,6 +22,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
+#include "base/optional.h"
 #include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "cc/benchmarks/micro_benchmark.h"
@@ -449,6 +450,11 @@
     return local_surface_id_allocation_from_parent_;
   }
 
+  // Generates a new child surface sequence number (from a LocalSurfaceId). This
+  // results in disabling drawing until the LocalSurfaceIdAllocation is received
+  // via the active tree. This only works in single threaded mode.
+  uint32_t GenerateChildSurfaceSequenceNumberSync();
+
   // Requests the allocation of a new LocalSurfaceId on the compositor thread.
   void RequestNewLocalSurfaceId();
 
@@ -800,6 +806,10 @@
   bool new_local_surface_id_request_ = false;
   uint32_t defer_main_frame_update_count_ = 0;
 
+  // Last value returned from GenerateChildSurfaceSequenceNumberSync(). This is
+  // reset once a LocalSurfaceId is submitted with a higher id.
+  base::Optional<uint32_t> generated_child_surface_sequence_number_;
+
   SkColor background_color_ = SK_ColorWHITE;
 
   LayerSelection selection_;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 586bbac..a5287ed 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -542,6 +542,9 @@
     return false;
   }
 
+  if (waiting_for_local_surface_id_)
+    return false;
+
   if (resourceless_software_draw_)
     return true;
 
@@ -1357,7 +1360,7 @@
 
   // If we return DRAW_SUCCESS, then we expect DrawLayers() to be called before
   // this function is called again.
-  return draw_result;
+  return DRAW_SUCCESS;
 }
 
 void LayerTreeHostImpl::RemoveRenderPasses(FrameData* frame) {
@@ -1854,9 +1857,8 @@
       active_tree_->set_needs_update_draw_properties();
     }
 
-    if (resourceless_software_draw) {
+    if (resourceless_software_draw)
       client_->OnCanDrawStateChanged(CanDraw());
-    }
 
     client_->OnDrawForLayerTreeFrameSink(resourceless_software_draw_,
                                          skip_draw);
@@ -5698,7 +5700,41 @@
     ukm_manager_->SetSourceURL(url);
 }
 
+void LayerTreeHostImpl::OnLayerTreeLocalSurfaceIdAllocationChanged() {
+  if (!waiting_for_local_surface_id_)
+    return;
+
+  const viz::LocalSurfaceId& current_id =
+      child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
+          .local_surface_id();
+  const viz::LocalSurfaceId& new_id =
+      active_tree()
+          ->local_surface_id_allocation_from_parent()
+          .local_surface_id();
+  if ((current_id.embed_token() != new_id.embed_token()) ||
+      (new_id.parent_sequence_number() > current_id.parent_sequence_number()) ||
+      ((new_id.parent_sequence_number() ==
+        current_id.parent_sequence_number()) &&
+       (new_id.child_sequence_number() >=
+        current_id.child_sequence_number()))) {
+    waiting_for_local_surface_id_ = false;
+    client_->OnCanDrawStateChanged(CanDraw());
+  }
+}
+
+uint32_t LayerTreeHostImpl::GenerateChildSurfaceSequenceNumberSync() {
+  waiting_for_local_surface_id_ = true;
+  child_local_surface_id_allocator_.GenerateIdOrIncrementChild();
+  client_->OnCanDrawStateChanged(CanDraw());
+  return child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation()
+      .local_surface_id()
+      .child_sequence_number();
+}
+
 void LayerTreeHostImpl::AllocateLocalSurfaceId() {
+  if (!settings_.automatically_allocate_surface_ids)
+    return;
+
   child_local_surface_id_allocator_.GenerateId();
   client_->DidGenerateLocalSurfaceIdAllocationOnImplThread(
       child_local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation());
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 04b750687..9c0ef87 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -713,6 +713,12 @@
 
   void SetActiveURL(const GURL& url);
 
+  // Called when LayerTreeImpl's LocalSurfaceIdAllocation changes.
+  void OnLayerTreeLocalSurfaceIdAllocationChanged();
+
+  // See SyncSurfaceIdAllocator for details.
+  uint32_t GenerateChildSurfaceSequenceNumberSync();
+
  protected:
   LayerTreeHostImpl(
       const LayerTreeSettings& settings,
@@ -1083,6 +1089,10 @@
   base::Optional<RenderFrameMetadata> last_draw_render_frame_metadata_;
   viz::ChildLocalSurfaceIdAllocator child_local_surface_id_allocator_;
 
+  // Set to true if waiting to receive a LocalSurfaceIdAllocation that matches
+  // that of |child_local_surface_id_allocator_|.
+  bool waiting_for_local_surface_id_ = false;
+
   std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_;
 
   // Stores information needed once we get a response for a particular
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 2dc253b..50e845a 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -76,6 +76,7 @@
 #include "components/viz/common/quads/texture_draw_quad.h"
 #include "components/viz/common/quads/tile_draw_quad.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
+#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
 #include "components/viz/service/display/gl_renderer.h"
 #include "components/viz/test/begin_frame_args_test.h"
 #include "components/viz/test/fake_output_surface.h"
@@ -194,11 +195,14 @@
     requested_animation_delay_ = delay;
   }
   void DidActivateSyncTree() override {
-    // Make sure the active tree always has a valid LocalSurfaceId.
-    host_impl_->active_tree()->SetLocalSurfaceIdAllocationFromParent(
-        viz::LocalSurfaceIdAllocation(
-            viz::LocalSurfaceId(1, base::UnguessableToken::Deserialize(2u, 3u)),
-            base::TimeTicks::Now()));
+    if (set_local_surface_id_on_activate_) {
+      // Make sure the active tree always has a valid LocalSurfaceId.
+      host_impl_->active_tree()->SetLocalSurfaceIdAllocationFromParent(
+          viz::LocalSurfaceIdAllocation(
+              viz::LocalSurfaceId(1,
+                                  base::UnguessableToken::Deserialize(2u, 3u)),
+              base::TimeTicks::Now()));
+    }
   }
   void WillPrepareTiles() override {}
   void DidPrepareTiles() override {}
@@ -226,7 +230,10 @@
       std::vector<LayerTreeHost::PresentationTimeCallback> callbacks,
       const gfx::PresentationFeedback& feedback) override {}
   void DidGenerateLocalSurfaceIdAllocationOnImplThread(
-      const viz::LocalSurfaceIdAllocation& allocation) override {}
+      const viz::LocalSurfaceIdAllocation& allocation) override {
+    last_generated_local_surface_id_ = allocation;
+    did_generate_local_surface_id_ = true;
+  }
 
   void set_reduce_memory_result(bool reduce_memory_result) {
     reduce_memory_result_ = reduce_memory_result;
@@ -793,6 +800,9 @@
   viz::RenderPassList last_on_draw_render_passes_;
   scoped_refptr<AnimationTimeline> timeline_;
   std::unique_ptr<base::Thread> image_worker_;
+  viz::LocalSurfaceIdAllocation last_generated_local_surface_id_;
+  bool did_generate_local_surface_id_ = false;
+  bool set_local_surface_id_on_activate_ = true;
 };
 
 class CommitToPendingTreeLayerTreeHostImplTest : public LayerTreeHostImplTest {
@@ -898,6 +908,52 @@
   EXPECT_TRUE(host_impl_->CanDraw());
   EXPECT_TRUE(on_can_draw_state_changed_called_);
   on_can_draw_state_changed_called_ = false;
+
+  viz::ParentLocalSurfaceIdAllocator parent_allocator;
+  parent_allocator.GenerateId();
+  const uint32_t child_sequence_number1 =
+      host_impl_->GenerateChildSurfaceSequenceNumberSync();
+  EXPECT_NE(child_sequence_number1, viz::kInitialChildSequenceNumber);
+  EXPECT_FALSE(host_impl_->CanDraw());
+  EXPECT_TRUE(on_can_draw_state_changed_called_);
+  on_can_draw_state_changed_called_ = false;
+
+  host_impl_->active_tree()->SetLocalSurfaceIdAllocationFromParent(
+      parent_allocator.GetCurrentLocalSurfaceIdAllocation());
+  EXPECT_TRUE(host_impl_->CanDraw());
+  EXPECT_TRUE(on_can_draw_state_changed_called_);
+  on_can_draw_state_changed_called_ = false;
+
+  // Without this SetLocalSurfaceIdAllocationFromParent() is called from
+  // DidActivateSyncTree().
+  set_local_surface_id_on_activate_ = false;
+  // Necessary to set LocalSurfaceIdAllocation in
+  // |LayerTreeHostImpl::child_local_surface_id_allocator_|.
+  host_impl_->ActivateSyncTree();
+
+  const uint32_t child_sequence_number2 =
+      host_impl_->GenerateChildSurfaceSequenceNumberSync();
+  EXPECT_NE(child_sequence_number2, child_sequence_number1);
+  EXPECT_FALSE(host_impl_->CanDraw());
+  EXPECT_TRUE(on_can_draw_state_changed_called_);
+  on_can_draw_state_changed_called_ = false;
+
+  host_impl_->active_tree()->SetLocalSurfaceIdAllocationFromParent(
+      parent_allocator.GetCurrentLocalSurfaceIdAllocation());
+  EXPECT_FALSE(host_impl_->CanDraw());
+  EXPECT_FALSE(on_can_draw_state_changed_called_);
+
+  auto id_from_parent =
+      parent_allocator.GetCurrentLocalSurfaceIdAllocation().local_surface_id();
+  host_impl_->active_tree()->SetLocalSurfaceIdAllocationFromParent(
+      viz::LocalSurfaceIdAllocation(
+          viz::LocalSurfaceId(id_from_parent.parent_sequence_number(),
+                              child_sequence_number2,
+                              id_from_parent.embed_token()),
+          base::TimeTicks::Now()));
+  EXPECT_TRUE(host_impl_->CanDraw());
+  EXPECT_TRUE(on_can_draw_state_changed_called_);
+  on_can_draw_state_changed_called_ = false;
 }
 
 TEST_F(LayerTreeHostImplTest, ResourcelessDrawWithEmptyViewport) {
@@ -5092,20 +5148,21 @@
 };
 
 struct PrepareToDrawSuccessTestCase {
+  explicit PrepareToDrawSuccessTestCase(DrawResult result)
+      : expected_result(result) {}
+
   struct State {
     bool has_missing_tile = false;
     bool has_incomplete_tile = false;
     bool is_animating = false;
     bool has_copy_request = false;
   };
+
   bool high_res_required = false;
   State layer_before;
   State layer_between;
   State layer_after;
   DrawResult expected_result;
-
-  explicit PrepareToDrawSuccessTestCase(DrawResult result)
-      : expected_result(result) {}
 };
 
 static void CreateLayerFromState(
diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc
index dd854fb..ad61545 100644
--- a/cc/trees/layer_tree_host_unittest_animation.cc
+++ b/cc/trees/layer_tree_host_unittest_animation.cc
@@ -925,9 +925,8 @@
         GetImplAnimationHost(&host_impl)
             ->GetElementAnimationsForElementId(layer->element_id());
     DCHECK(element_animations);
-    DCHECK(element_animations->keyframe_effects_list().might_have_observers());
     KeyframeEffect* keyframe_effect =
-        &*element_animations->keyframe_effects_list().begin();
+        &*element_animations->FirstKeyframeEffectForTesting();
     DCHECK(keyframe_effect);
     return *keyframe_effect;
   }
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index e6ebda2..4cb521f 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -1064,6 +1064,8 @@
         local_surface_id_allocation_from_parent) {
   local_surface_id_allocation_from_parent_ =
       local_surface_id_allocation_from_parent;
+  if (IsActiveTree())
+    host_impl_->OnLayerTreeLocalSurfaceIdAllocationChanged();
 }
 
 void LayerTreeImpl::RequestNewLocalSurfaceId() {
diff --git a/cc/trees/layer_tree_settings.h b/cc/trees/layer_tree_settings.h
index 9de6c5f5..63ae9b2 100644
--- a/cc/trees/layer_tree_settings.h
+++ b/cc/trees/layer_tree_settings.h
@@ -167,6 +167,11 @@
   // go away and CC should send Blink fractional values:
   // https://crbug.com/414283.
   bool commit_fractional_scroll_deltas = false;
+
+  // If true, LayerTreeHostImpl automatically allocates LocalSurfaceIds as
+  // necessary. If false, it is clients generate LocalSurfaceIds as necessary.
+  // TODO(sky): remove this once https://crbug.com/921129 is fixed.
+  bool automatically_allocate_surface_ids = true;
 };
 
 }  // namespace cc
diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h
index ab893e54..f1f309a2 100644
--- a/cc/trees/proxy.h
+++ b/cc/trees/proxy.h
@@ -5,6 +5,8 @@
 #ifndef CC_TREES_PROXY_H_
 #define CC_TREES_PROXY_H_
 
+#include <stdint.h>
+
 #include <memory>
 #include <string>
 
@@ -95,6 +97,9 @@
 
   virtual void RequestBeginMainFrameNotExpected(bool new_state) = 0;
 
+  // See description in LayerTreeHost
+  virtual uint32_t GenerateChildSurfaceSequenceNumberSync() = 0;
+
   // Testing hooks
   virtual bool MainFrameWillHappenForTesting() = 0;
 
diff --git a/cc/trees/proxy_main.cc b/cc/trees/proxy_main.cc
index 39a64696..305a1dd 100644
--- a/cc/trees/proxy_main.cc
+++ b/cc/trees/proxy_main.cc
@@ -564,6 +564,12 @@
   return true;
 }
 
+uint32_t ProxyMain::GenerateChildSurfaceSequenceNumberSync() {
+  // This function only makes sense for single-threaded mode.
+  NOTREACHED();
+  return 0u;
+}
+
 bool ProxyMain::MainFrameWillHappenForTesting() {
   DCHECK(IsMainThread());
   bool main_frame_will_happen = false;
diff --git a/cc/trees/proxy_main.h b/cc/trees/proxy_main.h
index 408a6948..8e642bbd 100644
--- a/cc/trees/proxy_main.h
+++ b/cc/trees/proxy_main.h
@@ -97,6 +97,7 @@
   void SetMutator(std::unique_ptr<LayerTreeMutator> mutator) override;
   void SetPaintWorkletLayerPainter(
       std::unique_ptr<PaintWorkletLayerPainter> painter) override;
+  uint32_t GenerateChildSurfaceSequenceNumberSync() override;
   bool MainFrameWillHappenForTesting() override;
   void ReleaseLayerTreeFrameSink() override;
   void UpdateBrowserControlsState(BrowserControlsState constraints,
diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc
index d0bd1cb..db9ea23 100644
--- a/cc/trees/single_thread_proxy.cc
+++ b/cc/trees/single_thread_proxy.cc
@@ -526,9 +526,15 @@
 
 void SingleThreadProxy::DidGenerateLocalSurfaceIdAllocationOnImplThread(
     const viz::LocalSurfaceIdAllocation& allocation) {
+  DebugScopedSetMainThread main(task_runner_provider_);
   layer_tree_host_->DidGenerateLocalSurfaceIdAllocation(allocation);
 }
 
+uint32_t SingleThreadProxy::GenerateChildSurfaceSequenceNumberSync() {
+  DebugScopedSetImplThread impl(task_runner_provider_);
+  return host_impl_->GenerateChildSurfaceSequenceNumberSync();
+}
+
 void SingleThreadProxy::RequestBeginMainFrameNotExpected(bool new_state) {
   if (scheduler_on_impl_thread_) {
     scheduler_on_impl_thread_->SetMainThreadWantsBeginMainFrameNotExpected(
diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h
index 0a9a7b8..075ddb1 100644
--- a/cc/trees/single_thread_proxy.h
+++ b/cc/trees/single_thread_proxy.h
@@ -34,7 +34,7 @@
   static std::unique_ptr<Proxy> Create(
       LayerTreeHost* layer_tree_host,
       LayerTreeHostSingleThreadClient* client,
-      TaskRunnerProvider* task_runner_provider_);
+      TaskRunnerProvider* task_runner_provider);
   ~SingleThreadProxy() override;
 
   // Proxy implementation
@@ -124,6 +124,7 @@
                                    bool skip_draw) override;
   void NeedsImplSideInvalidation(bool needs_first_draw_on_activation) override;
   void RequestBeginMainFrameNotExpected(bool new_state) override;
+  uint32_t GenerateChildSurfaceSequenceNumberSync() override;
   void NotifyImageDecodeRequestFinished() override;
   void DidPresentCompositorFrameOnImplThread(
       uint32_t frame_token,
diff --git a/chrome/VERSION b/chrome/VERSION
index e143903..9a4a0f2 100644
--- a/chrome/VERSION
+++ b/chrome/VERSION
@@ -1,4 +1,4 @@
 MAJOR=74
 MINOR=0
-BUILD=3691
+BUILD=3695
 PATCH=0
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 5699f7e3..11b20b4 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -1604,32 +1604,6 @@
   }
 }
 
-monochrome_public_apk_or_module_tmpl("monochrome_public_base_bundle_module") {
-  module_name = "MonochromePublicBase"
-  target_type = "android_app_bundle_module"
-  is_base_module = true
-}
-
-if (android_64bit_target_cpu && enable_64_bit_browser) {
-  monochrome_public_apk_or_module_tmpl(
-      "monochrome_64_public_base_bundle_module") {
-    module_name = "Monochrome64PublicBase"
-    target_type = "android_app_bundle_module"
-    is_base_module = true
-    is_64_bit_browser = true
-    include_32_bit_webview = false
-  }
-
-  monochrome_public_apk_or_module_tmpl(
-      "monochrome_64_32_public_base_bundle_module") {
-    module_name = "Monochrome6432PublicBase"
-    target_type = "android_app_bundle_module"
-    is_base_module = true
-    is_64_bit_browser = true
-    include_32_bit_webview = true
-  }
-}
-
 if (public_android_sdk) {
   trichrome_library_apk_tmpl("trichrome_library_apk") {
     apk_name = "TrichromeLibrary"
@@ -1850,16 +1824,8 @@
 }
 
 # Feature modules that go into Chrome Android application bundles.
-if (modularize_ar) {
-  ar_module_tmpl("ar_public_bundle_module") {
-    manifest_package = manifest_package
-    module_name = "ArPublic"
-    base_module_target = ":monochrome_public_base_bundle_module"
-    version_code = monochrome_version_code
-    version_name = chrome_version_name
-  }
-}
-
+# Note that Monochrome feature modules are instantiated in the Monochrome
+# bundle target template.
 if (modularize_vr) {
   vr_module_tmpl("vr_public_bundle_module") {
     manifest_package = manifest_package
@@ -1869,15 +1835,6 @@
     version_name = chrome_version_name
     uncompress_shared_libraries = chromium_linker_supported
   }
-
-  vr_module_tmpl("vr_monochrome_public_bundle_module") {
-    manifest_package = manifest_package
-    module_name = "VrMonochromePublic"
-    base_module_target = ":monochrome_public_base_bundle_module"
-    version_code = monochrome_version_code
-    version_name = chrome_version_name
-    uncompress_shared_libraries = true
-  }
 }
 
 # Chrome Android application bundles.
@@ -1914,13 +1871,45 @@
 }
 
 template("monochrome_public_bundle_tmpl") {
-  android_app_bundle(target_name) {
+  _bundle_name = "MonochromePublic${invoker.bundle_suffix}"
+  _base_module_target_name = "${invoker.target_name}__base_bundle_module"
+
+  monochrome_public_apk_or_module_tmpl(_base_module_target_name) {
     forward_variables_from(invoker,
                            [
-                             "bundle_name",
-                             "base_module_target",
+                             "is_64_bit_browser",
+                             "include_32_bit_webview",
                            ])
+    module_name = "${_bundle_name}Base"
+    target_type = "android_app_bundle_module"
+    is_base_module = true
+  }
 
+  if (modularize_ar) {
+    ar_module_tmpl("${target_name}__ar_bundle_module") {
+      manifest_package = manifest_package
+      module_name = "Ar" + _bundle_name
+      base_module_target = ":$_base_module_target_name"
+      version_code = monochrome_version_code
+      version_name = chrome_version_name
+      forward_variables_from(invoker, [ "is_64_bit_browser" ])
+    }
+  }
+
+  if (modularize_vr) {
+    vr_module_tmpl("${target_name}__vr_bundle_module") {
+      manifest_package = manifest_package
+      module_name = "Vr" + _bundle_name
+      base_module_target = ":$_base_module_target_name"
+      version_code = monochrome_version_code
+      version_name = chrome_version_name
+      uncompress_shared_libraries = true
+    }
+  }
+
+  android_app_bundle(target_name) {
+    bundle_name = _bundle_name
+    base_module_target = ":$_base_module_target_name"
     command_line_flags_file = "chrome-command-line"
     if (!is_java_debug) {
       proguard_enabled = true
@@ -1928,13 +1917,12 @@
     }
     enable_language_splits = enable_chrome_language_splits
 
-    # TODO(cjgrant): We need to create base-module-specific module targets.
     extra_modules = []
     if (modularize_ar) {
       extra_modules = [
         {
           name = "ar"
-          module_target = ":ar_public_bundle_module"
+          module_target = ":${target_name}__ar_bundle_module"
         },
       ]
     }
@@ -1942,7 +1930,7 @@
       extra_modules += [
         {
           name = "vr"
-          module_target = ":vr_monochrome_public_bundle_module"
+          module_target = ":${target_name}__vr_bundle_module"
         },
       ]
     }
@@ -1950,18 +1938,19 @@
 }
 
 monochrome_public_bundle_tmpl("monochrome_public_bundle") {
-  bundle_name = "MonochromePublic"
-  base_module_target = ":monochrome_public_base_bundle_module"
+  bundle_suffix = ""
 }
 
 if (android_64bit_target_cpu && enable_64_bit_browser) {
   monochrome_public_bundle_tmpl("monochrome_64_public_bundle") {
-    bundle_name = "MonochromePublic64"
-    base_module_target = ":monochrome_64_public_base_bundle_module"
+    bundle_suffix = "64"
+    is_64_bit_browser = true
+    include_32_bit_webview = false
   }
 
   monochrome_public_bundle_tmpl("monochrome_64_32_public_bundle") {
-    bundle_name = "MonochromePublic6432"
-    base_module_target = ":monochrome_64_32_public_base_bundle_module"
+    bundle_suffix = "6432"
+    is_64_bit_browser = true
+    include_32_bit_webview = true
   }
 }
diff --git a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
index 56ab7b7a..d6a57fc5 100644
--- a/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
+++ b/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/FeedNewTabPage.java
@@ -132,8 +132,7 @@
         public BasicCardConfiguration(Resources resources, UiConfig uiConfig) {
             mResources = resources;
             mUiConfig = uiConfig;
-            mCornerRadius = mResources.getDimensionPixelSize(
-                    R.dimen.content_suggestions_card_modern_corner_radius);
+            mCornerRadius = mResources.getDimensionPixelSize(R.dimen.default_card_corner_radius);
             mCardMargin = mResources.getDimensionPixelSize(
                     R.dimen.content_suggestions_card_modern_margin);
             mCardWideMargin =
diff --git a/chrome/android/java/monochrome_public_apk.AndroidManifest.expected b/chrome/android/java/monochrome_public_apk.AndroidManifest.expected
index f6ce084..6a5481d 100644
--- a/chrome/android/java/monochrome_public_apk.AndroidManifest.expected
+++ b/chrome/android/java/monochrome_public_apk.AndroidManifest.expected
@@ -1,2349 +1,1938 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2015 The Chromium Authors. All rights reserved.
-     Use of this source code is governed by a BSD-style license that can be
-     found in the LICENSE file.
--->
-<!--
-Note: This is a jinja2 template, processed at build time into the final manifest.
-
-Blocks denoted with { % block some_name % }foo{ % endblock % } can be overridden
-by a child template that "extends" this file.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
+<?xml version="1.0" ?>
+<manifest
     package="org.chromium.chrome"
-    tools:ignore="MissingVersion" > <!-- android:versionCode and android:versionName is set through gyp. See build/common.gypi -->
-    <uses-sdk
-        android:minSdkVersion="24"
-        android:targetSdkVersion="28" />
-
-    <uses-feature android:glEsVersion="0x00020000" />
-
-    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
-    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
-
-    <uses-permission-sdk-23 android:name="android.permission.ACCESS_WIFI_STATE" />
-
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-
-    <!--
-      Enable runtime permissions as uses-permission in tip of tree builds
-      only for ease of development on Android L and earlier. For consumer
-      channels use "runtime permission" uses-permission-sdk-23 which provides
-      permission on Android M and later without a prompt.
-    -->
-    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
-
-    <uses-permission-sdk-23 android:name="android.permission.BLUETOOTH" />
-    <uses-permission-sdk-23 android:name="android.permission.BLUETOOTH_ADMIN" />
-    <uses-permission-sdk-23 android:name="android.permission.READ_CONTACTS" />
-    <uses-permission-sdk-23 android:name="android.permission.REORDER_TASKS" />
-    <uses-permission-sdk-23 android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
-
-    <uses-permission android:name="android.permission.CAMERA" />
-    <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
-    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
-    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
-    <uses-permission android:name="android.permission.NFC" />
-    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
-    <uses-permission android:name="android.permission.READ_SYNC_STATS" />
-    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
-    <uses-permission android:name="android.permission.RECORD_AUDIO" />
-    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
-    <uses-permission android:name="android.permission.VIBRATE" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
-    <!-- Indicates use of Android's VR-mode, available only on Android N+. -->
-    <uses-feature
-        android:name="android.software.vr.mode"
-        android:required="false" />
-    <!-- Indicates use of VR features that are available only on Daydream-ready devices. -->
-    <uses-feature
-        android:name="android.hardware.vr.high_performance"
-        android:required="false" />
-    <!--
-      Indicates that we don't need Chrome to be available on devices that only support landscape
-      orientation. This is needed because we have VR specific activities that declare the
-      android:orientation attribute.
-    -->
-    <uses-feature
-        android:name="android.hardware.screen.landscape"
-        android:required="false" />
-    <!-- Indicates that head tracking should be done in 6DoF, if available -->
-    <uses-feature
-        android:name="android.hardware.vr.headtracking"
-        android:required="false"
-        android:version="1" />
-
-    <permission
-        android:name="org.chromium.chrome.permission.CHILD_SERVICE"
-        android:protectionLevel="signature" />
-    <permission
-        android:name="org.chromium.chrome.permission.READ_WRITE_BOOKMARK_FOLDERS"
-        android:protectionLevel="signatureOrSystem" />
-    <permission
-        android:name="org.chromium.chrome.TOS_ACKED"
-        android:protectionLevel="signatureOrSystem" />
-    <!-- Only chrome can receive the messages and registration result -->
-    <permission
-        android:name="org.chromium.chrome.permission.C2D_MESSAGE"
-        android:protectionLevel="signature" />
-    <permission
-        android:name="org.chromium.chrome.permission.DEBUG"
-        android:label="Debug web pages"
-        android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
-        android:protectionLevel="signature" />
-
-    <uses-permission android:name="org.chromium.chrome.permission.C2D_MESSAGE" />
-    <uses-permission android:name="org.chromium.chrome.permission.READ_WRITE_BOOKMARK_FOLDERS" />
-    <uses-permission android:name="org.chromium.chrome.TOS_ACKED" />
-    <uses-permission android:name="com.chrome.permission.DEVICE_EXTRAS" />
-    <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
-    <uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" />
-    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
-    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
-    <uses-permission android:name="com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS" />
-
-    <!-- We may use GPS but it's not required -->
-    <uses-feature
-        android:name="android.hardware.location.gps"
-        android:required="false" />
-    <uses-feature
-        android:name="android.hardware.camera"
-        android:required="false" />
-
-    <!--
-      android.permission.RECORD_AUDIO makes this implied, however we don't
-      require a microphone.
-    -->
-    <uses-feature
-        android:name="android.hardware.microphone"
-        android:required="false" />
-    <!--
-      The app is usable with keyboard/mouse. This feature is implicitly true for
-      all applications and needs to be disabled manually.
-    -->
-    <uses-feature
-        android:name="android.hardware.touchscreen"
-        android:required="false" />
-    <!-- No keyset definitions should exist for any monochrome apks -->
-    <!--
- Set android:largeHeap to "true" to allow more than the default
-         Java heap limit (32Mb on Nexus S, 48Mb on Xoom).
-    -->
-    <application
-        android:name="org.chromium.chrome.browser.MonochromeApplication"
-        android:allowBackup="false"
-        android:extractNativeLibs="false"
+    tools:ignore="MissingVersion"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+  <uses-sdk android:minSdkVersion="24" android:targetSdkVersion="28"/>
+  <uses-permission-sdk-23 android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
+  <uses-permission-sdk-23 android:name="android.permission.REORDER_TASKS"/>
+  <uses-permission-sdk-23 android:name="android.permission.READ_CONTACTS"/>
+  <uses-permission-sdk-23 android:name="android.permission.BLUETOOTH_ADMIN"/>
+  <uses-permission-sdk-23 android:name="android.permission.BLUETOOTH"/>
+  <uses-permission-sdk-23 android:name="android.permission.ACCESS_WIFI_STATE"/>
+  <uses-permission android:name="org.chromium.chrome.permission.READ_WRITE_BOOKMARK_FOLDERS"/>
+  <uses-permission android:name="org.chromium.chrome.permission.C2D_MESSAGE"/>
+  <uses-permission android:name="org.chromium.chrome.TOS_ACKED"/>
+  <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
+  <uses-permission android:name="com.google.android.apps.now.CURRENT_ACCOUNT_ACCESS"/>
+  <uses-permission android:name="com.chrome.permission.DEVICE_EXTRAS"/>
+  <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
+  <uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"/>
+  <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
+  <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+  <uses-permission android:name="android.permission.WAKE_LOCK"/>
+  <uses-permission android:name="android.permission.VIBRATE"/>
+  <uses-permission android:name="android.permission.USE_CREDENTIALS"/>
+  <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
+  <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+  <uses-permission android:name="android.permission.READ_SYNC_STATS"/>
+  <uses-permission android:name="android.permission.READ_SYNC_SETTINGS"/>
+  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
+  <uses-permission android:name="android.permission.NFC"/>
+  <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+  <uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
+  <uses-permission android:name="android.permission.INTERNET"/>
+  <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
+  <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+  <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION"/>
+  <uses-permission android:name="android.permission.CAMERA"/>
+  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+  <uses-feature android:name="android.software.vr.mode" android:required="false"/>
+  <uses-feature android:name="android.hardware.vr.high_performance" android:required="false"/>
+  <uses-feature
+      android:name="android.hardware.vr.headtracking"
+      android:required="false"
+      android:version="1"/>
+  <uses-feature android:name="android.hardware.touchscreen" android:required="false"/>
+  <uses-feature android:name="android.hardware.screen.landscape" android:required="false"/>
+  <uses-feature android:name="android.hardware.microphone" android:required="false"/>
+  <uses-feature android:name="android.hardware.location.gps" android:required="false"/>
+  <uses-feature android:name="android.hardware.camera" android:required="false"/>
+  <uses-feature android:glEsVersion="0x00020000"/>
+  <permission
+      android:name="org.chromium.chrome.permission.READ_WRITE_BOOKMARK_FOLDERS"
+      android:protectionLevel="signatureOrSystem"/>
+  <permission
+      android:name="org.chromium.chrome.permission.CHILD_SERVICE"
+      android:protectionLevel="signature"/>
+  <permission
+      android:name="org.chromium.chrome.permission.C2D_MESSAGE"
+      android:protectionLevel="signature"/>
+  <permission
+      android:name="org.chromium.chrome.TOS_ACKED"
+      android:protectionLevel="signatureOrSystem"/>
+  <permission
+      android:label="Debug
+      web
+      pages"
+      android:name="org.chromium.chrome.permission.DEBUG"
+      android:permissionGroup="android.permission-group.DEVELOPMENT_TOOLS"
+      android:protectionLevel="signature"/>
+  <application
+      android:allowBackup="false"
+      android:extractNativeLibs="false"
+      android:icon="@drawable/ic_launcher"
+      android:label="@string/app_name"
+      android:largeHeap="false"
+      android:manageSpaceActivity="@string/manage_space_activity"
+      android:multiArch="true"
+      android:name="org.chromium.chrome.browser.MonochromeApplication"
+      android:networkSecurityConfig="@xml/network_security_config"
+      android:roundIcon="@drawable/ic_launcher_round"
+      android:supportsRtl="true"
+      android:use32bitAbi="true">
+    <activity
+        android:autoRemoveFromRecents="false"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:excludeFromRecents="true"
+        android:hardwareAccelerated="false"
+        android:launchMode="singleInstance"
+        android:name="org.chromium.chrome.browser.upgrade.UpgradeActivity"
+        android:persistableMode="persistNever"
+        android:taskAffinity=""
+        android:theme="@style/MainTheme"
+        android:windowSoftInputMode="adjustResize"/>
+    <activity
+        android:autoRemoveFromRecents="false"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/incognito_app_icon"
+        android:name="org.chromium.chrome.browser.document.IncognitoDocumentActivity"
+        android:persistableMode="persistNever"
+        android:taskAffinity=""
+        android:theme="@style/IncognitoTheme"
+        android:windowSoftInputMode="adjustResize"/>
+    <activity
+        android:autoRemoveFromRecents="false"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:name="org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity=""
+        android:theme="@style/MainTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:autoRemoveFromRecents="false"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:name="org.chromium.chrome.browser.document.DocumentActivity"
+        android:persistableMode="persistAcrossReboots"
+        android:taskAffinity=""
+        android:theme="@style/MainTheme"
+        android:windowSoftInputMode="adjustResize"/>
+    <activity
+        android:autoRemoveFromRecents="true"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:excludeFromRecents="true"
+        android:label="@string/fre_activity_label"
+        android:launchMode="singleInstance"
+        android:name="org.chromium.chrome.browser.firstrun.FirstRunActivity"
+        android:theme="@style/FirstRunTheme"
+        android:windowSoftInputMode="stateHidden|adjustPan"/>
+    <activity
+        android:autoRemoveFromRecents="true"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:excludeFromRecents="true"
+        android:label="@string/fre_activity_label"
+        android:launchMode="singleInstance"
+        android:name="org.chromium.chrome.browser.firstrun.LightweightFirstRunActivity"
+        android:theme="@style/SimpleDialog"
+        android:windowSoftInputMode="stateHidden|adjustPan"/>
+    <activity
+        android:autoRemoveFromRecents="true"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:excludeFromRecents="true"
+        android:label="@string/fre_activity_label"
+        android:launchMode="singleInstance"
+        android:name="org.chromium.chrome.browser.firstrun.TabbedModeFirstRunActivity"
+        android:theme="@style/TabbedModeFirstRunTheme"
+        android:windowSoftInputMode="stateHidden|adjustPan"/>
+    <activity
+        android:autoRemoveFromRecents="true"
+        android:name="org.chromium.chrome.browser.sync.ui.PassphraseActivity"
+        android:theme="@style/MainTheme"/>
+    <activity
+        android:clearTaskOnLaunch="true"
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:excludeFromRecents="true"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:label="Search"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.searchwidget.SearchActivity"
+        android:taskAffinity=""
+        android:theme="@style/SearchActivityTheme"
+        android:windowSoftInputMode="adjustResize"/>
+    <activity
+        android:configChanges="keyboardHidden|orientation|screenSize"
+        android:excludeFromRecents="true"
+        android:exported="false"
+        android:launchMode="singleTop"
+        android:name="com.google.ar.core.InstallActivity"
+        android:theme="@android:style/Theme.Material.Light.Dialog.Alert"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:enabled="false"
+        android:excludeFromRecents="true"
+        android:exported="true"
         android:icon="@drawable/ic_launcher"
-        android:label="@string/app_name"
-        android:largeHeap="false"
-        android:manageSpaceActivity="@string/manage_space_activity"
-        android:multiArch="true"
-        android:networkSecurityConfig="@xml/network_security_config"
-        android:roundIcon="@drawable/ic_launcher_round"
-        android:supportsRtl="true"
-        android:use32bitAbi="true" >
-
-        <!-- Samsung MultiWindow Support -->
-        <meta-data
-            android:name="com.samsung.android.sdk.multiwindow.enable"
-            android:value="true" />
-        <meta-data
-            android:name="com.samsung.android.sdk.multiwindow.penwindow.enable"
-            android:value="true" />
-        <meta-data
-            android:name="com.sec.android.support.multiwindow"
-            android:value="true" />
-        <meta-data
-            android:name="android.content.APP_RESTRICTIONS"
-            android:resource="@xml/app_restrictions" />
-
-        <!-- ARCore APK integration -->
-        <!-- This tag indicates that this application optionally uses ARCore. -->
-        <meta-data
-            android:name="com.google.ar.core"
-            android:value="optional" />
-        <!-- This value must match value present in ARCore SDK's .aar -->
-        <!--
-             TODO(https://crbug.com/917406): modify build scripts to
-             automatically pull this value in - the problem exists because in
-             bundle mode, the ARCore SDK is packaged into AR module and
-             the module installation will not automatically bring in DFM's
-             manifest entries.
-        -->
-        <meta-data
-            android:name="com.google.ar.core.min_apk_version"
-            android:value="181012000" />
-
-        <!-- Cast support -->
-        <meta-data
-            android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
-            android:value="org.chromium.chrome.browser.media.router.caf.CastOptionsProvider" />
-
-        <!--
-             Note: All activities directly or indirectly derived from ChromeActivity
-             must specify android:hardwareAccelerated="false".
-
-             Since this activity (shown in the launcher) and the application
-             (shown in Android's Settings/Apps list) share the same label, we
-             do not specify one here to allow it to inherit from the app.
-        -->
-        <activity
-            android:name="org.chromium.chrome.browser.document.ChromeLauncherActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:excludeFromRecents="true"
-            android:relinquishTaskIdentity="true"
-            android:taskAffinity=""
-            android:theme="@style/LauncherTheme" >
-
-            <!--
-                 TODO(mthiesse, b/72214458): This is a duplication of the icon metadata below.
-                 Daydream will actually ignore the metadata here, and use the metadata on the
-                 activity-alias. However, play store apk validation fails to find the icons on the
-                 alias, so we need to include them here to pass validation.
-            -->
-            <meta-data
-                android:name="com.google.android.vr.icon"
-                android:resource="@drawable/daydream_icon_foreground" />
-            <meta-data
-                android:name="com.google.android.vr.icon_background"
-                android:resource="@drawable/daydream_icon_background" />
-
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.IntentDispatcher"
-            android:exported="true"
-            android:targetActivity="org.chromium.chrome.browser.document.ChromeLauncherActivity" >
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-
-                <category android:name="android.intent.category.NOTIFICATION_PREFERENCES" />
-                <category android:name="com.google.intent.category.DAYDREAM" />
-            </intent-filter>
-            <!--
-                 Matches the common case of intents with no MIME type.
-                 Make sure to keep in sync with the next filter.
-            -->
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="com.google.intent.category.DAYDREAM" />
-
-                <data android:scheme="googlechrome" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:scheme="about" />
-                <data android:scheme="javascript" />
-            </intent-filter>
-            <!--
-                 Same filter as above but with MIME types.  Intents that
-                 do not specify a MIME type won't match.
-            -->
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="com.google.intent.category.DAYDREAM" />
-
-                <data android:scheme="googlechrome" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:scheme="about" />
-                <data android:scheme="javascript" />
-                <data android:scheme="content" />
-                <data android:mimeType="text/html" />
-                <data android:mimeType="text/plain" />
-                <data android:mimeType="application/xhtml+xml" />
-            </intent-filter>
-            <!-- MHTML support, used for snapshots -->
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-
-                <data android:scheme="file" />
-                <data android:scheme="content" />
-                <data android:mimeType="multipart/related" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-
-                <data android:scheme="file" />
-                <data android:scheme="content" />
-                <data android:mimeType="message/rfc822" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="com.google.intent.category.DAYDREAM" />
-
-                <data android:scheme="file" />
-                <data android:scheme="content" />
-                <data android:host="*" />
-                <data android:pathPattern="/.*\\.mhtml" />
-                <data android:pathPattern="/.*\\.mht" />
-            </intent-filter>
-            <!--
-                 Same filter as above but with mimeType="*/*". Used for
-                 handling intent send by ShareIt.
-            -->
-            <intent-filter>
-                <action android:name="android.intent.action.VIEW" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="com.google.intent.category.DAYDREAM" />
-
-                <data android:scheme="file" />
-                <data android:scheme="content" />
-                <data android:host="*" />
-                <data android:pathPattern="/.*\\.mhtml" />
-                <data android:pathPattern="/.*\\.mht" />
-                <data android:mimeType="*/*" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_SEARCH" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.speech.action.VOICE_SEARCH_RESULTS" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.nfc.action.NDEF_DISCOVERED" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.SEARCH" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="com.sec.android.airview.HOVER" />
-            </intent-filter>
-
-            <meta-data
-                android:name="android.app.searchable"
-                android:resource="@xml/searchable" />
-            <meta-data
-                android:name="com.google.android.vr.icon"
-                android:resource="@drawable/daydream_icon_foreground" />
-            <meta-data
-                android:name="com.google.android.vr.icon_background"
-                android:resource="@drawable/daydream_icon_background" />
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.media.MediaLauncherActivity"
-            android:enabled="false"
-            android:excludeFromRecents="true"
-            android:exported="true"
-            android:theme="@android:style/Theme.NoDisplay" > <!-- This will be selectively enabled at runtime. -->
-            <intent-filter tools:ignore="AppLinkUrlError" >
-                <action android:name="android.intent.action.VIEW" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-                <!-- TODO(https://crbug.com/800875): Limit these to supported MIME types. -->
-                <data android:mimeType="audio/*" />
-                <data android:mimeType="image/*" />
-                <data android:mimeType="video/*" />
-                <data android:scheme="file" />
-                <data android:scheme="content" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.LauncherShortcutActivity"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:taskAffinity=""
-            android:theme="@android:style/Theme.NoDisplay" />
-
-        <!-- Upgrade related -->
-        <activity
-            android:name="org.chromium.chrome.browser.upgrade.UpgradeActivity"
-            android:autoRemoveFromRecents="false"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:excludeFromRecents="true"
-            android:hardwareAccelerated="false"
-            android:launchMode="singleInstance"
-            android:persistableMode="persistNever"
-            android:taskAffinity=""
-            android:theme="@style/MainTheme"
-            android:windowSoftInputMode="adjustResize" >
-        </activity>
-
-        <service
-            android:name="org.chromium.chrome.browser.upgrade.UpgradeIntentService"
-            android:exported="false" />
-
-        <receiver
-            android:name="org.chromium.chrome.browser.upgrade.PackageReplacedBroadcastReceiver"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
-            </intent-filter>
-        </receiver>
-
-        <!-- Locale related -->
-        <receiver
-            android:name="org.chromium.chrome.browser.locale.LocaleChangedBroadcastReceiver"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="android.intent.action.LOCALE_CHANGED" />
-            </intent-filter>
-        </receiver>
-
-        <!-- Document mode Activities. -->
-        <activity
-            android:name="org.chromium.chrome.browser.document.DocumentActivity"
-            android:autoRemoveFromRecents="false"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:persistableMode="persistAcrossReboots"
-            android:taskAffinity=""
-            android:theme="@style/MainTheme"
-            android:windowSoftInputMode="adjustResize" >
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.document.DocumentActivity"
-            android:exported="false"
-            android:targetActivity="org.chromium.chrome.browser.document.DocumentActivity" />
-
-        <activity
-            android:name="org.chromium.chrome.browser.document.IncognitoDocumentActivity"
-            android:autoRemoveFromRecents="false"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/incognito_app_icon"
-            android:persistableMode="persistNever"
-            android:taskAffinity=""
-            android:theme="@style/IncognitoTheme"
-            android:windowSoftInputMode="adjustResize" >
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.document.IncognitoDocumentActivity"
-            android:exported="false"
-            android:targetActivity="org.chromium.chrome.browser.document.IncognitoDocumentActivity" />
-
-        <!-- Custom Tabs -->
-        <activity
-            android:name="org.chromium.chrome.browser.customtabs.CustomTabActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:theme="@style/MainTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.customtabs.PaymentHandlerActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:theme="@style/TranslucentMainTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <!--
-             SeparateTaskCustomTabActivity is a wrapper of CustomTabActivity. It provides the
-             general feeling of supporting multi tasks, even for versions that did not fully support
-             it.
-             TODO(arthursonzogni, tedchoc): Enabled this only on Android < 21 after M74.
-        -->
-        <activity
-            android:name="org.chromium.chrome.browser.customtabs.SeparateTaskCustomTabActivity"
-            android:autoRemoveFromRecents="false"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity=""
-            android:theme="@style/MainTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.incognito.IncognitoDisclosureActivity"
-            android:exported="false"
-            android:theme="@style/FullscreenTransparentActivityTheme" >
-        </activity>
-
-        <!-- ChromeTabbedActivity related -->
-        <activity
-            android:name="org.chromium.chrome.browser.ChromeTabbedActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="true"
-            android:hardwareAccelerated="false"
-            android:launchMode="singleTask"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:theme="@style/TabbedModeTheme"
-            android:windowSoftInputMode="adjustResize" >
-
-            <!--
-              Daydream api categorizes an activity to three categories: Cardboard only, hybrid
-              or Daydream. It does so by testing if intents can be resolved by the activity
-              that requests it.
-            -->
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity> <!-- TODO(crbug.com/780674): retarget .Main back to CTA for non-modern APK -->
-        <activity-alias
-            android:name="com.google.android.apps.chrome.Main"
-            android:exported="true"
-            android:targetActivity="org.chromium.chrome.browser.ChromeTabbedActivity" >
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.APP_BROWSER" />
-                <category android:name="android.intent.category.MULTIWINDOW_LAUNCHER" />
-            </intent-filter>
-
-            <meta-data
-                android:name="android.app.shortcuts"
-                android:resource="@xml/launchershortcuts" />
-
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.ChromeTabbedActivity2"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:launchMode="singleTask"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.ChromeTabbedActivity2"
-            android:theme="@style/TabbedModeTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:theme="@style/TabbedModeTheme"
-            android:windowSoftInputMode="adjustResize" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.touchless.NoTouchActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:launchMode="singleTask"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:theme="@style/MainTheme"
-            android:windowSoftInputMode="adjustResize" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.sync.ui.PassphraseActivity"
-            android:autoRemoveFromRecents="true"
-            android:theme="@style/MainTheme" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.firstrun.LightweightFirstRunActivity"
-            android:autoRemoveFromRecents="true"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:excludeFromRecents="true"
-            android:label="@string/fre_activity_label"
-            android:launchMode="singleInstance"
-            android:theme="@style/SimpleDialog"
-            android:windowSoftInputMode="stateHidden|adjustPan" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.firstrun.FirstRunActivity"
-            android:autoRemoveFromRecents="true"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:excludeFromRecents="true"
-            android:label="@string/fre_activity_label"
-            android:launchMode="singleInstance"
-            android:theme="@style/FirstRunTheme"
-            android:windowSoftInputMode="stateHidden|adjustPan" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.firstrun.TabbedModeFirstRunActivity"
-            android:autoRemoveFromRecents="true"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:excludeFromRecents="true"
-            android:label="@string/fre_activity_label"
-            android:launchMode="singleInstance"
-            android:theme="@style/TabbedModeFirstRunTheme"
-            android:windowSoftInputMode="stateHidden|adjustPan" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.vr.VrFirstRunActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode"
-            android:enableVrMode="@string/gvr_vr_mode_component"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:launchMode="singleInstance"
-            android:theme="@style/VrActivityTheme" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.signin.AccountSigninActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:theme="@style/DialogWhenLarge" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.signin.SigninActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="false"
-            android:theme="@style/DialogWhenLarge" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.preferences.Preferences"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="false"
-            android:label="@string/preferences"
-            android:theme="@style/PreferencesTheme" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.preferences.website.ManageSpaceActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="false"
-            android:label="@string/storage_management_activity_label"
-            android:theme="@style/ManageSpaceTheme" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.bookmarks.BookmarkActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="false"
-            android:theme="@style/FullscreenWhiteActivityTheme"
-            android:windowSoftInputMode="stateAlwaysHidden|adjustResize" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:theme="@style/DialogWhenLarge"
-            android:windowSoftInputMode="stateHidden" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.ADDBOOKMARK" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.bookmarks.BookmarkEditActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="false"
-            android:label="@string/edit_bookmark"
-            android:theme="@style/DialogWhenLarge"
-            android:windowSoftInputMode="stateHidden" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddEditFolderActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="false"
-            android:theme="@style/DialogWhenLarge" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.bookmarks.BookmarkFolderSelectActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="false"
-            android:label="@string/bookmark_choose_folder"
-            android:theme="@style/DialogWhenLarge"
-            android:windowSoftInputMode="stateAlwaysHidden" >
-        </activity>
-
-        <!-- Activities for downloads. -->
-        <activity
-            android:name="org.chromium.chrome.browser.download.DownloadActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="false"
-            android:theme="@style/FullscreenWhiteActivityTheme"
-            android:windowSoftInputMode="stateAlwaysHidden|adjustResize" >
-        </activity>
-
-        <!-- Activities for history. -->
-        <activity
-            android:name="org.chromium.chrome.browser.history.HistoryActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="false"
-            android:theme="@style/FullscreenWhiteActivityTheme"
-            android:windowSoftInputMode="stateAlwaysHidden|adjustResize" >
-        </activity>
-
-        <!--
-            Activities for webapps.
-            TODO(dfalcantara): Remove the aliases for the WebappActivities once we're pretty sure
-                               that users don't have any instances of the original Activity still
-                               running.
-        -->
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappLauncherActivity"
-            android:excludeFromRecents="true"
-            android:taskAffinity=""
-            android:theme="@android:style/Theme.NoDisplay" >
-            <intent-filter>
-                <action android:name="com.google.android.apps.chrome.webapps.WebappManager.ACTION_START_WEBAPP" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="org.webapk.ACTION_START_WEBAPK" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappManager"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappLauncherActivity" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:documentLaunchMode="intoExisting"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTop"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity"
-            android:label="@string/webapp_activity_title"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity0"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity0"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity0"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity0" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity1"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity1"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity1"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity1" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity2"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity2"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity2"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity2" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity3"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity3"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity3"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity3" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity4"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity4"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity4"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity4" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity5"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity5"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity5"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity5" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity6"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity6"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity6"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity6" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity7"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity7"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity7"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity7" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity8"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity8"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity8"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity8" >
-        </activity-alias>
-
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebappActivity9"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebappActivity9"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <activity-alias
-            android:name="com.google.android.apps.chrome.webapps.WebappActivity9"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity9" >
-        </activity-alias>
-        <!-- Activities for WebAPKs. -->
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.SameTaskWebApkActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:label="@string/webapp_activity_title"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:documentLaunchMode="intoExisting"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTop"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity0"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity0"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity1"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity1"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity2"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity2"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity3"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity3"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity4"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity4"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity5"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity5"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity6"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity6"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity7"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity7"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity8"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity8"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.webapps.WebApkActivity9"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:icon="@mipmap/app_shortcut_icon"
-            android:label="@string/webapp_activity_title"
-            android:launchMode="singleTask"
-            android:persistableMode="persistNever"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity9"
-            android:theme="@style/WebappTheme"
-            android:windowSoftInputMode="adjustResize" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.media.router.caf.remoting.CafExpandedControllerActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:excludeFromRecents="true"
-            android:hardwareAccelerated="true"
-            android:label="Chrome.CafExpandedControllerActivity"
-            android:launchMode="singleTask"
-            android:noHistory="true"
-            android:theme="@style/MainTheme" >
-        </activity>
-
-        <!-- This activity is used to restart the main Chrome process.  Should never be exported. -->
-        <activity
-            android:name="org.chromium.chrome.browser.BrowserRestartActivity"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:launchMode="singleInstance"
-            android:process=":browser_restart_process"
-            android:theme="@android:style/Theme.Translucent.NoTitleBar" >
-        </activity>
-
-        <!-- This activity is to expose the print option via the generic Android share action. -->
-        <activity
-            android:name="org.chromium.chrome.browser.printing.PrintShareActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:enabled="false"
-            android:excludeFromRecents="true"
-            android:exported="true"
-            android:icon="@drawable/print"
-            android:label="@string/print_share_activity_title"
-            android:noHistory="true"
-            android:theme="@android:style/Theme.NoDisplay" >
-            <intent-filter>
-                <action android:name="android.intent.action.SEND" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-
-                <data android:mimeType="text/plain" />
-            </intent-filter>
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:enabled="false"
-            android:excludeFromRecents="true"
-            android:exported="true"
-            android:icon="@drawable/ic_launcher"
-            android:label="@string/send_tab_to_self_share_activity_title"
-            android:noHistory="true"
-            android:theme="@android:style/Theme.NoDisplay" >
-            <intent-filter>
-                <action android:name="android.intent.action.SEND" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-
-                <data android:mimeType="text/plain" />
-            </intent-filter>
-        </activity>
-
-        <!-- Activity for dispatching intents to Instant Apps. -->
-        <activity
-            android:name="org.chromium.chrome.browser.instantapps.AuthenticatedProxyActivity"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:noHistory="true"
-            android:theme="@android:style/Theme.NoDisplay" >
-        </activity>
-        <activity
-            android:name="org.chromium.chrome.browser.vr.VrCancelAnimationActivity"
-            android:enableVrMode="@string/gvr_vr_mode_component"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:noHistory="true"
-            android:theme="@android:style/Theme.NoDisplay" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.dummy.action" />
-
-                <category android:name="com.google.intent.category.DAYDREAM" />
-                <category android:name="com.google.intent.category.CARDBOARD" />
-            </intent-filter>
-        </activity>
-
-        <!-- Activities for Browser Actions -->
-        <activity
-            android:name="org.chromium.chrome.browser.browseractions.BrowserActionActivity"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:exported="true"
-            android:theme="@style/FullscreenTransparentActivityTheme" >
-            <intent-filter>
-                <action android:name="androidx.browser.browseractions.browser_action_open" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-            </intent-filter>
-        </activity>
-
-        <service
-            android:name="org.chromium.chrome.browser.browseractions.BrowserActionsService"
-            android:exported="false" >
-        </service>
-
-        <!-- Components for Trusted Web Activities -->
-        <receiver
-            android:name="org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver"
-            android:exported="true" >
-            <intent-filter>
-                <data android:scheme="package" />
-
-                <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
-                <action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver.DEBUG" />
-            </intent-filter>
-        </receiver>
-
-        <!-- Not used at this point -->
-        <service
-            android:name="org.chromium.chrome.browser.browserservices.ClearDataService"
-            android:exported="false" >
-        </service>
-
-        <activity
-            android:name="org.chromium.chrome.browser.browserservices.ClearDataDialogActivity"
-            android:exported="false"
-            android:theme="@style/ClearDataDialogActivityTheme" />
-        <activity
-            android:name="org.chromium.chrome.browser.browserservices.ManageTrustedWebActivityDataActivity"
-            android:exported="true"
-            android:theme="@style/FullscreenTransparentActivityTheme" >
-            <intent-filter>
-                <action android:name="android.support.customtabs.action.ACTION_MANAGE_TRUSTED_WEB_ACTIVITY_DATA" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-
-                <data android:scheme="https" />
-            </intent-filter>
-        </activity>
-
-        <!-- Service for decoding images in a sandboxed process. -->
-        <service
-            android:name="org.chromium.chrome.browser.photo_picker.DecoderService"
-            android:description="@string/decoder_description"
-            android:exported="false"
-            android:isolatedProcess="true"
-            android:process=":decoder_service" />
-
-        <!-- Providers for chrome data. -->
-        <provider
-            android:name="org.chromium.chrome.browser.provider.ChromeBrowserProvider"
-            android:authorities="org.chromium.chrome.ChromeBrowserProvider;org.chromium.chrome.browser;org.chromium.chrome"
-            android:exported="true" >
-            <path-permission
-                android:path="/bookmarks/search_suggest_query"
-                android:readPermission="android.permission.GLOBAL_SEARCH" />
-        </provider>
-
-        <!-- Provider for FileProvider. -->
-        <provider
-            android:name="org.chromium.chrome.browser.util.ChromeFileProvider"
-            android:authorities="org.chromium.chrome.FileProvider"
-            android:exported="false"
-            android:grantUriPermissions="true" >
-            <meta-data
-                android:name="android.support.FILE_PROVIDER_PATHS"
-                android:resource="@xml/file_paths" />
-        </provider>
-
-        <!-- Sync adapter for browser invalidation. -->
-        <service
-            android:name="org.chromium.chrome.browser.invalidation.ChromeBrowserSyncAdapterService"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="android.content.SyncAdapter" />
-            </intent-filter>
-
-            <meta-data
-                android:name="android.content.SyncAdapter"
-                android:resource="@xml/syncadapter" />
-        </service>
-
-        <!-- Broadcast receiver that will be notified of account changes -->
-        <receiver android:name="org.chromium.chrome.browser.services.AccountsChangedReceiver" >
-            <intent-filter>
-                <action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
-            </intent-filter>
-        </receiver>
-
-        <!-- Download foreground service -->
-        <service
-            android:name="org.chromium.chrome.browser.download.DownloadForegroundService"
-            android:exported="false" >
-        </service>
-
-        <!-- Download broadcast manager service -->
-        <service
-            android:name="org.chromium.chrome.browser.download.DownloadBroadcastManager"
-            android:exported="false" >
-        </service>
-
-        <!-- Bookmarks widget -->
-        <receiver
-            android:name="com.google.android.apps.chrome.appwidget.bookmarks.BookmarkThumbnailWidgetProvider"
-            android:label="@string/bookmark_widget_title" >
-            <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
-                <action android:name=".BOOKMARK_APPWIDGET_UPDATE" />
-            </intent-filter>
-
-            <meta-data
-                android:name="android.appwidget.provider"
-                android:resource="@xml/bookmark_widget_info" />
-        </receiver>
-
-        <service
-            android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetService"
-            android:exported="false"
-            android:permission="android.permission.BIND_REMOTEVIEWS" />
-
-        <receiver
-            android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy"
-            android:exported="false" />
-
-        <!-- Search widget -->
-        <receiver
-            android:name="org.chromium.chrome.browser.searchwidget.SearchWidgetProvider"
-            android:label="@string/search_widget_title" >
-            <intent-filter>
-                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.searchwidget.START_TEXT_QUERY" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.searchwidget.START_VOICE_QUERY" />
-            </intent-filter>
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.searchwidget.UPDATE_ALL_WIDGETS" />
-            </intent-filter>
-
-            <meta-data
-                android:name="android.appwidget.provider"
-                android:resource="@xml/search_widget_info" />
-        </receiver>
-
-        <!-- Search Activity -->
-        <activity
-            android:name="org.chromium.chrome.browser.searchwidget.SearchActivity"
-            android:clearTaskOnLaunch="true"
-            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:hardwareAccelerated="false"
-            android:label="Search"
-            android:launchMode="singleTask"
-            android:taskAffinity=""
-            android:theme="@style/SearchActivityTheme"
-            android:windowSoftInputMode="adjustResize" />
-
-        <!-- Receiver for GCM messages. -->
-        <receiver
-            android:name="com.google.android.gms.gcm.GcmReceiver"
-            android:exported="true"
-            android:permission="com.google.android.c2dm.permission.SEND" >
-            <intent-filter>
-                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
-                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
-
-                <category android:name="org.chromium.chrome" />
-            </intent-filter>
-        </receiver>
-        <!-- GcmTaskService for registration for Invalidations. -->
-        <service
-            android:name="com.google.ipc.invalidation.ticl.android2.channel.GcmRegistrationTaskService"
-            android:exported="true"
-            android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE" >
-            <intent-filter>
-                <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
-            </intent-filter>
-        </service>
-        <!-- InstanceIDListenerService for token refresh events from GCM. -->
-        <service
-            android:name="com.google.ipc.invalidation.ticl.android2.channel.AndroidInstanceIDListenerService"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="com.google.android.gms.iid.InstanceID" />
-            </intent-filter>
-        </service>
-        <!-- GcmListenerService for messages from GCM. -->
-        <service
-            android:name="org.chromium.chrome.browser.services.gcm.ChromeGcmListenerService"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
-            </intent-filter>
-        </service>
-
-        <meta-data
-            android:name="ipc.invalidation.ticl.gcm_upstream_service_class"
-            android:value="org.chromium.chrome.browser.services.gcm.InvalidationGcmUpstreamSender" />
-
-        <service
-            android:name="org.chromium.chrome.browser.services.gcm.InvalidationGcmUpstreamSender"
-            android:exported="false" />
-
-        <!-- Notification service for sync. -->
-        <meta-data
-            android:name="ipc.invalidation.ticl.listener_service_class"
-            android:value="org.chromium.chrome.browser.invalidation.ChromeInvalidationClientService" />
-
-        <service
-            android:name="org.chromium.chrome.browser.invalidation.ChromeInvalidationClientService"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="com.google.ipc.invalidation.AUTH_TOKEN_REQUEST" />
-            </intent-filter>
-        </service>
-        <service
-            android:name="com.google.ipc.invalidation.ticl.android2.TiclService"
-            android:exported="false" />
-        <service
-            android:name="com.google.ipc.invalidation.ticl.android2.channel.AndroidMessageSenderService"
-            android:exported="false" />
-
-        <receiver
-            android:name="com.google.ipc.invalidation.ticl.android2.AndroidInternalScheduler$AlarmReceiver"
-            android:exported="false" />
-        <receiver
-            android:name="com.google.ipc.invalidation.external.client.contrib.AndroidListener$AlarmReceiver"
-            android:exported="false" />
-
-        <!-- Android Notification service listener -->
-        <service
-            android:name="org.chromium.chrome.browser.notifications.NotificationService"
-            android:exported="false" />
-
-        <receiver
-            android:name="org.chromium.chrome.browser.notifications.NotificationService$Receiver"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="org.chromium.chrome.browser.notifications.CLICK_NOTIFICATION" />
-                <action android:name="org.chromium.chrome.browser.notifications.CLOSE_NOTIFICATION" />
-            </intent-filter>
-        </receiver>
-
-        <!-- Android Notification job service -->
-        <service
-            android:name="org.chromium.chrome.browser.notifications.NotificationJobService"
-            android:exported="false"
-            android:permission="android.permission.BIND_JOB_SERVICE" />
-
-        <!-- Background Task Scheduler job service -->
-        <service
-            android:name="org.chromium.components.background_task_scheduler.BackgroundTaskJobService"
-            android:exported="false"
-            android:permission="android.permission.BIND_JOB_SERVICE" />
-
-        <!-- Background Task Scheduler GCM task service -->
-        <service
-            android:name="org.chromium.components.background_task_scheduler.BackgroundTaskGcmTaskService"
-            android:exported="true"
-            android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE" >
-            <intent-filter>
-                <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
-            </intent-filter>
-        </service>
-
-        <!-- GcmTaskService implementation to wake Chrome on scheduled events -->
-        <service
-            android:name="org.chromium.chrome.browser.ChromeBackgroundService"
-            android:exported="true"
-            android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE" >
-            <intent-filter>
-                <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
-            </intent-filter>
-        </service>
-        <service
-            android:name="org.chromium.chrome.browser.prerender.ChromePrerenderService"
-            android:exported="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.chrome.browser.customtabs.CustomTabsConnectionService"
-            android:exported="true"
-            tools:ignore="ExportedService" >
-            <intent-filter>
-                <action android:name="android.support.customtabs.action.CustomTabsService" />
-            </intent-filter>
-        </service>
-        <service android:name="android.support.customtabs.PostMessageService" />
-
-        <!-- Crash reporting services. -->
-        <service
-            android:name="org.chromium.chrome.browser.crash.ChromeMinidumpUploadJobService"
-            android:exported="false"
-            android:permission="android.permission.BIND_JOB_SERVICE" />
-        <service
-            android:name="org.chromium.chrome.browser.crash.MinidumpUploadService"
-            android:exported="false" />
-        <service
-            android:name="org.chromium.chrome.browser.omaha.OmahaClient"
-            android:exported="false" />
-        <service
-            android:name="org.chromium.chrome.browser.incognito.IncognitoNotificationService"
-            android:exported="false" />
-
-        <!--
-             The following service entries exist in order to allow us to
-             start more than one sandboxed process.
-        -->
-
-
-        <!--
-             NOTE: If you change the value of "android:process" for the below services,
-             you also need to update kHelperProcessExecutableName in chrome_constants.cc.
-        -->
-        <meta-data
-            android:name="org.chromium.content.browser.NUM_SANDBOXED_SERVICES"
-            android:value="40" />
-
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService0"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process0"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService1"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process1"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService2"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process2"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService3"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process3"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService4"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process4"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService5"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process5"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService6"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process6"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService7"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process7"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService8"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process8"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService9"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process9"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService10"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process10"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService11"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process11"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService12"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process12"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService13"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process13"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService14"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process14"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService15"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process15"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService16"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process16"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService17"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process17"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService18"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process18"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService19"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process19"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService20"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process20"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService21"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process21"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService22"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process22"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService23"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process23"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService24"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process24"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService25"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process25"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService26"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process26"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService27"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process27"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService28"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process28"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService29"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process29"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService30"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process30"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService31"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process31"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService32"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process32"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService33"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process33"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService34"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process34"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService35"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process35"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService36"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process36"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService37"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process37"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService38"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process38"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-        <service
-            android:name="org.chromium.content.app.SandboxedProcessService39"
-            android:exported="true"
-            android:externalService="true"
-            android:isolatedProcess="true"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":sandboxed_process39"
-            android:visibleToInstantApps="true"
-            tools:ignore="ExportedService" />
-
-        <meta-data
-            android:name="org.chromium.content.browser.NUM_PRIVILEGED_SERVICES"
-            android:value="5" />
-
-        <!-- TODO(tedchoc): Omit this for release builds as MultiDex is debug only. -->
-        <meta-data
-            android:name="org.chromium.chrome:privileged_process0.ignore_multidex"
-            android:value="true" />
-
-        <service
-            android:name="org.chromium.content.app.PrivilegedProcessService0"
-            android:exported="false"
-            android:isolatedProcess="false"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":privileged_process0" />
-        <!-- TODO(tedchoc): Omit this for release builds as MultiDex is debug only. -->
-        <meta-data
-            android:name="org.chromium.chrome:privileged_process1.ignore_multidex"
-            android:value="true" />
-
-        <service
-            android:name="org.chromium.content.app.PrivilegedProcessService1"
-            android:exported="false"
-            android:isolatedProcess="false"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":privileged_process1" />
-        <!-- TODO(tedchoc): Omit this for release builds as MultiDex is debug only. -->
-        <meta-data
-            android:name="org.chromium.chrome:privileged_process2.ignore_multidex"
-            android:value="true" />
-
-        <service
-            android:name="org.chromium.content.app.PrivilegedProcessService2"
-            android:exported="false"
-            android:isolatedProcess="false"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":privileged_process2" />
-        <!-- TODO(tedchoc): Omit this for release builds as MultiDex is debug only. -->
-        <meta-data
-            android:name="org.chromium.chrome:privileged_process3.ignore_multidex"
-            android:value="true" />
-
-        <service
-            android:name="org.chromium.content.app.PrivilegedProcessService3"
-            android:exported="false"
-            android:isolatedProcess="false"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":privileged_process3" />
-        <!-- TODO(tedchoc): Omit this for release builds as MultiDex is debug only. -->
-        <meta-data
-            android:name="org.chromium.chrome:privileged_process4.ignore_multidex"
-            android:value="true" />
-
-        <service
-            android:name="org.chromium.content.app.PrivilegedProcessService4"
-            android:exported="false"
-            android:isolatedProcess="false"
-            android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
-            android:process=":privileged_process4" />
-
-        <receiver
-            android:name="org.chromium.chrome.browser.download.DownloadBroadcastReceiver"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED" />
-            </intent-filter>
-        </receiver>
-        <receiver
-            android:name="org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$Receiver"
-            android:exported="false" />
-        <receiver
-            android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$OpenUrlReceiver"
-            android:exported="false" />
-        <receiver
-            android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$DeleteReceiver"
-            android:exported="false" />
-        <receiver
-            android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$TimeoutReceiver"
-            android:exported="false" />
-        <receiver
-            android:name="org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$ClickReceiver"
-            android:exported="false" />
-        <receiver
-            android:name="org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$SettingsReceiver"
-            android:exported="false" />
-        <receiver
-            android:name="org.chromium.chrome.browser.offlinepages.AutoFetchNotifier$CompleteNotificationReceiver"
-            android:exported="false" />
-        <receiver
-            android:name="org.chromium.chrome.browser.offlinepages.AutoFetchNotifier$InProgressCancelReceiver"
-            android:exported="false" />
-
-        <service
-            android:name="org.chromium.chrome.browser.media.MediaCaptureNotificationService"
-            android:exported="false" />
-        <service
-            android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackListenerService"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </service>
-        <service
-            android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationListenerService"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </service>
-        <service
-            android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastListenerService"
-            android:exported="false" >
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </service>
-
-        <receiver android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackMediaButtonReceiver" >
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </receiver>
-        <receiver android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationMediaButtonReceiver" >
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </receiver>
-        <receiver android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastMediaButtonReceiver" >
-            <intent-filter>
-                <action android:name="android.intent.action.MEDIA_BUTTON" />
-            </intent-filter>
-        </receiver>
-
-        <service
-            android:name="org.chromium.chrome.browser.tracing.TracingNotificationService"
-            android:exported="false" />
-
-        <meta-data
-            android:name="org.chromium.content.browser.SMART_CLIP_PROVIDER"
-            android:value="org.chromium.content_public.browser.SmartClipProvider" />
-
-        <!--
-             Media route controllers to use for remote playback (cast).
-             This is here, rather than in code, since it varies between upstream and downstream,
-             yet we need this list of classes in the notification service, which belongs upstream
-             and doesn't run the downstream startup code. The Cast code will, for each media element,
-             choose the first MediaRouteController that can play it, so the order of the list can be important.
-             The most specific MediaRouteControllers should be listed first, followed by more generic ones.
-             The downstream manifest replaces this block, and hence replaces the list of media route
-             controllers with its own list.
-        -->
-        <meta-data
-            android:name="org.chromium.content.browser.REMOTE_MEDIA_PLAYERS"
-            android:value="org.chromium.chrome.browser.media.remote.DefaultMediaRouteController" />
-        <meta-data
-            android:name="com.android.webview.WebViewLibrary"
-            android:value="libmonochrome.so" />
-
-        <activity
-            android:name="com.android.webview.chromium.LicenseActivity"
-            android:label="@string/license_activity_title" >
-            <intent-filter>
-                <action android:name="android.settings.WEBVIEW_LICENSE" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-
-            <meta-data
-                android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
-                android:value="true" />
-        </activity>
-
-        <provider
-            android:name="com.android.webview.chromium.LicenseContentProvider"
-            android:authorities="org.chromium.chrome.LicenseContentProvider"
-            android:exported="true" />
-        <!--
-                     If you change the variations services, also see
-                     android_webview/test/shell/AndroidManifest.xml.
-        -->
-        <service
-            android:name="org.chromium.android_webview.services.VariationsSeedServer"
-            android:exported="true"
-            android:process=":webview_service" />
-        <service
-            android:name="org.chromium.android_webview.services.AwVariationsSeedFetcher"
-            android:exported="false"
-            android:permission="android.permission.BIND_JOB_SERVICE"
-            android:process=":webview_service" />
-        <service
-            android:name="org.chromium.android_webview.services.CrashReceiverService"
-            android:exported="true"
-            android:process=":webview_service" />
-        <service
-            android:name="org.chromium.android_webview.services.AwMinidumpUploadJobService"
-            android:exported="true"
-            android:permission="android.permission.BIND_JOB_SERVICE"
-            android:process=":webview_service" />
-
-        <meta-data
-            android:name="android.arch.lifecycle.VERSION"
-            android:value="27.0.0-SNAPSHOT" />
-        <meta-data
-            android:name="com.google.android.gms.version"
-            android:value="@integer/google_play_services_version" />
-
-        <activity
-            android:name="com.google.android.gms.common.api.GoogleApiActivity"
-            android:exported="false"
-            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
-
-        <receiver
-            android:name="com.google.android.gms.cast.framework.media.MediaIntentReceiver"
-            android:exported="false" />
-
-        <service
-            android:name="com.google.android.gms.cast.framework.media.MediaNotificationService"
-            android:exported="false" />
-        <service
-            android:name="com.google.android.gms.cast.framework.ReconnectionService"
-            android:exported="false" /> <!-- This activity is critical for installing ARCore when it is not already present. -->
-        <activity
-            android:name="com.google.ar.core.InstallActivity"
-            android:configChanges="keyboardHidden|orientation|screenSize"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:launchMode="singleTop"
-            android:theme="@android:style/Theme.Material.Light.Dialog.Alert" />
-    </application>
-
+        android:label="@string/send_tab_to_self_share_activity_title"
+        android:name="org.chromium.chrome.browser.send_tab_to_self.SendTabToSelfShareActivity"
+        android:noHistory="true"
+        android:theme="@android:style/Theme.NoDisplay">
+      <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="text/plain"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:enabled="false"
+        android:excludeFromRecents="true"
+        android:exported="true"
+        android:icon="@drawable/print"
+        android:label="@string/print_share_activity_title"
+        android:name="org.chromium.chrome.browser.printing.PrintShareActivity"
+        android:noHistory="true"
+        android:theme="@android:style/Theme.NoDisplay">
+      <intent-filter>
+        <action android:name="android.intent.action.SEND"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="text/plain"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:excludeFromRecents="true"
+        android:hardwareAccelerated="true"
+        android:label="Chrome.CafExpandedControllerActivity"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.media.router.caf.remoting.CafExpandedControllerActivity"
+        android:noHistory="true"
+        android:theme="@style/MainTheme"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/bookmark_choose_folder"
+        android:name="org.chromium.chrome.browser.bookmarks.BookmarkFolderSelectActivity"
+        android:theme="@style/DialogWhenLarge"
+        android:windowSoftInputMode="stateAlwaysHidden"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/edit_bookmark"
+        android:name="org.chromium.chrome.browser.bookmarks.BookmarkEditActivity"
+        android:theme="@style/DialogWhenLarge"
+        android:windowSoftInputMode="stateHidden"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/preferences"
+        android:name="org.chromium.chrome.browser.preferences.Preferences"
+        android:theme="@style/PreferencesTheme"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:label="@string/storage_management_activity_label"
+        android:name="org.chromium.chrome.browser.preferences.website.ManageSpaceActivity"
+        android:theme="@style/ManageSpaceTheme"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.bookmarks.BookmarkActivity"
+        android:theme="@style/FullscreenWhiteActivityTheme"
+        android:windowSoftInputMode="stateAlwaysHidden|adjustResize"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddEditFolderActivity"
+        android:theme="@style/DialogWhenLarge"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.download.DownloadActivity"
+        android:theme="@style/FullscreenWhiteActivityTheme"
+        android:windowSoftInputMode="stateAlwaysHidden|adjustResize"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.history.HistoryActivity"
+        android:theme="@style/FullscreenWhiteActivityTheme"
+        android:windowSoftInputMode="stateAlwaysHidden|adjustResize"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.signin.SigninActivity"
+        android:theme="@style/DialogWhenLarge"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:exported="true"
+        android:name="org.chromium.chrome.browser.browseractions.BrowserActionActivity"
+        android:theme="@style/FullscreenTransparentActivityTheme">
+      <intent-filter>
+        <action android:name="androidx.browser.browseractions.browser_action_open"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:scheme="http"/>
+        <data android:scheme="https"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:name="org.chromium.chrome.browser.bookmarks.BookmarkAddActivity"
+        android:theme="@style/DialogWhenLarge"
+        android:windowSoftInputMode="stateHidden">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.ADDBOOKMARK"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize"
+        android:name="org.chromium.chrome.browser.signin.AccountSigninActivity"
+        android:theme="@style/DialogWhenLarge"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode"
+        android:enableVrMode="@string/gvr_vr_mode_component"
+        android:excludeFromRecents="true"
+        android:exported="false"
+        android:launchMode="singleInstance"
+        android:name="org.chromium.chrome.browser.vr.VrFirstRunActivity"
+        android:theme="@style/VrActivityTheme">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:documentLaunchMode="intoExisting"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTop"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:documentLaunchMode="intoExisting"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTop"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:excludeFromRecents="true"
+        android:name="org.chromium.chrome.browser.document.ChromeLauncherActivity"
+        android:relinquishTaskIdentity="true"
+        android:taskAffinity=""
+        android:theme="@style/LauncherTheme">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+      <meta-data
+          android:name="com.google.android.vr.icon"
+          android:resource="@drawable/daydream_icon_foreground"/>
+      <meta-data
+          android:name="com.google.android.vr.icon_background"
+          android:resource="@drawable/daydream_icon_background"/>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity0"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity0"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity1"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity1"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity2"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity2"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity3"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity3"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity4"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity4"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity5"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity5"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity6"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity6"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity7"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity7"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity8"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity8"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebApkActivity9"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebApkActivity9"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity0"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity0"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity1"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity1"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity2"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity2"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity3"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity3"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity4"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity4"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity5"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity5"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity6"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity6"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity7"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity7"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity8"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity8"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.webapps.WebappActivity9"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.webapps.WebappActivity9"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:label="@string/webapp_activity_title"
+        android:name="org.chromium.chrome.browser.webapps.SameTaskWebApkActivity"
+        android:persistableMode="persistNever"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:theme="@style/WebappTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.ChromeTabbedActivity2"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:taskAffinity="org.chromium.chrome.ChromeTabbedActivity2"
+        android:theme="@style/TabbedModeTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.touchless.NoTouchActivity"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:theme="@style/MainTheme"
+        android:windowSoftInputMode="adjustResize"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:name="org.chromium.chrome.browser.customtabs.CustomTabActivity"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:theme="@style/MainTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:name="org.chromium.chrome.browser.customtabs.PaymentHandlerActivity"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:theme="@style/TranslucentMainTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="false"
+        android:hardwareAccelerated="false"
+        android:name="org.chromium.chrome.browser.multiwindow.MultiInstanceChromeTabbedActivity"
+        android:theme="@style/TabbedModeTheme"
+        android:windowSoftInputMode="adjustResize"/>
+    <activity
+        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|mcc|mnc|screenLayout|smallestScreenSize|uiMode|density"
+        android:exported="true"
+        android:hardwareAccelerated="false"
+        android:launchMode="singleTask"
+        android:name="org.chromium.chrome.browser.ChromeTabbedActivity"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:theme="@style/TabbedModeTheme"
+        android:windowSoftInputMode="adjustResize">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:enableVrMode="@string/gvr_vr_mode_component"
+        android:excludeFromRecents="true"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.vr.VrCancelAnimationActivity"
+        android:noHistory="true"
+        android:theme="@android:style/Theme.NoDisplay">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:excludeFromRecents="true"
+        android:exported="false"
+        android:launchMode="singleInstance"
+        android:name="org.chromium.chrome.browser.BrowserRestartActivity"
+        android:process=":browser_restart_process"
+        android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
+    <activity
+        android:excludeFromRecents="true"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.LauncherShortcutActivity"
+        android:taskAffinity=""
+        android:theme="@android:style/Theme.NoDisplay"/>
+    <activity
+        android:excludeFromRecents="true"
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.instantapps.AuthenticatedProxyActivity"
+        android:noHistory="true"
+        android:theme="@android:style/Theme.NoDisplay"/>
+    <activity
+        android:excludeFromRecents="true"
+        android:name="org.chromium.chrome.browser.webapps.WebappLauncherActivity"
+        android:taskAffinity=""
+        android:theme="@android:style/Theme.NoDisplay">
+      <intent-filter>
+        <action
+            android:name="com.google.android.apps.chrome.webapps.WebappManager.ACTION_START_WEBAPP"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="org.webapk.ACTION_START_WEBAPK"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:exported="false"
+        android:name="com.google.android.gms.common.api.GoogleApiActivity"
+        android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
+    <activity
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.browserservices.ClearDataDialogActivity"
+        android:theme="@style/ClearDataDialogActivityTheme"/>
+    <activity
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.incognito.IncognitoDisclosureActivity"
+        android:theme="@style/FullscreenTransparentActivityTheme"/>
+    <activity
+        android:exported="true"
+        android:name="org.chromium.chrome.browser.browserservices.ManageTrustedWebActivityDataActivity"
+        android:theme="@style/FullscreenTransparentActivityTheme">
+      <intent-filter>
+        <action
+            android:name="android.support.customtabs.action.ACTION_MANAGE_TRUSTED_WEB_ACTIVITY_DATA"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:scheme="https"/>
+      </intent-filter>
+    </activity>
+    <activity
+        android:label="@string/license_activity_title"
+        android:name="com.android.webview.chromium.LicenseActivity">
+      <intent-filter>
+        <action android:name="android.settings.WEBVIEW_LICENSE"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>
+      <meta-data
+          android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+          android:value="true"/>
+    </activity>
+    <activity
+        android:enabled="false"
+        android:excludeFromRecents="true"
+        android:exported="true"
+        android:name="org.chromium.chrome.browser.media.MediaLauncherActivity"
+        android:theme="@android:style/Theme.NoDisplay">
+      <intent-filter tools:ignore="AppLinkUrlError">
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="audio/*"/>
+        <data android:mimeType="image/*"/>
+        <data android:mimeType="video/*"/>
+        <data android:scheme="content"/>
+        <data android:scheme="file"/>
+      </intent-filter>
+    </activity>
+    <activity-alias
+        android:exported="false"
+        android:name="com.google.android.apps.chrome.document.DocumentActivity"
+        android:targetActivity="org.chromium.chrome.browser.document.DocumentActivity"/>
+    <activity-alias
+        android:exported="false"
+        android:name="com.google.android.apps.chrome.document.IncognitoDocumentActivity"
+        android:targetActivity="org.chromium.chrome.browser.document.IncognitoDocumentActivity"/>
+    <activity-alias
+        android:exported="true"
+        android:name="com.google.android.apps.chrome.IntentDispatcher"
+        android:targetActivity="org.chromium.chrome.browser.document.ChromeLauncherActivity">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN"/>
+        <category android:name="android.intent.category.NOTIFICATION_PREFERENCES"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.MEDIA_SEARCH"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.SEARCH"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+        <data android:host="*"/>
+        <data android:mimeType="*/*"/>
+        <data android:pathPattern="/.*\\.mht"/>
+        <data android:pathPattern="/.*\\.mhtml"/>
+        <data android:scheme="content"/>
+        <data android:scheme="file"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+        <data android:host="*"/>
+        <data android:pathPattern="/.*\\.mht"/>
+        <data android:pathPattern="/.*\\.mhtml"/>
+        <data android:scheme="content"/>
+        <data android:scheme="file"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+        <data android:mimeType="application/xhtml+xml"/>
+        <data android:mimeType="text/html"/>
+        <data android:mimeType="text/plain"/>
+        <data android:scheme="about"/>
+        <data android:scheme="content"/>
+        <data android:scheme="googlechrome"/>
+        <data android:scheme="http"/>
+        <data android:scheme="https"/>
+        <data android:scheme="javascript"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+        <data android:scheme="about"/>
+        <data android:scheme="googlechrome"/>
+        <data android:scheme="http"/>
+        <data android:scheme="https"/>
+        <data android:scheme="javascript"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="message/rfc822"/>
+        <data android:scheme="content"/>
+        <data android:scheme="file"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:mimeType="multipart/related"/>
+        <data android:scheme="content"/>
+        <data android:scheme="file"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <data android:scheme="http"/>
+        <data android:scheme="https"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="android.speech.action.VOICE_SEARCH_RESULTS"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="com.sec.android.airview.HOVER"/>
+      </intent-filter>
+      <meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
+      <meta-data
+          android:name="com.google.android.vr.icon"
+          android:resource="@drawable/daydream_icon_foreground"/>
+      <meta-data
+          android:name="com.google.android.vr.icon_background"
+          android:resource="@drawable/daydream_icon_background"/>
+    </activity-alias>
+    <activity-alias
+        android:exported="true"
+        android:name="com.google.android.apps.chrome.Main"
+        android:targetActivity="org.chromium.chrome.browser.ChromeTabbedActivity">
+      <intent-filter>
+        <action android:name="android.intent.action.MAIN"/>
+        <category android:name="android.intent.category.APP_BROWSER"/>
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <category android:name="android.intent.category.DEFAULT"/>
+        <category android:name="android.intent.category.LAUNCHER"/>
+        <category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.dummy.action"/>
+        <category android:name="com.google.intent.category.CARDBOARD"/>
+        <category android:name="com.google.intent.category.DAYDREAM"/>
+      </intent-filter>
+      <meta-data android:name="android.app.shortcuts" android:resource="@xml/launchershortcuts"/>
+    </activity-alias>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity0"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity0"/>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity1"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity1"/>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity2"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity2"/>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity3"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity3"/>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity4"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity4"/>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity5"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity5"/>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity6"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity6"/>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity7"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity7"/>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity8"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity8"/>
+    <activity-alias
+        android:icon="@mipmap/app_shortcut_icon"
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity9"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity9"/>
+    <activity-alias
+        android:label="@string/webapp_activity_title"
+        android:name="com.google.android.apps.chrome.webapps.WebappActivity"
+        android:resizeableActivity="true"
+        android:supportsPictureInPicture="true"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappActivity"/>
+    <activity-alias
+        android:name="com.google.android.apps.chrome.webapps.WebappManager"
+        android:targetActivity="org.chromium.chrome.browser.webapps.WebappLauncherActivity"/>
+    <meta-data android:name="android.arch.lifecycle.VERSION" android:value="27.0.0-SNAPSHOT"/>
+    <meta-data
+        android:name="android.content.APP_RESTRICTIONS"
+        android:resource="@xml/app_restrictions"/>
+    <meta-data android:name="com.android.webview.WebViewLibrary" android:value="libmonochrome.so"/>
+    <meta-data
+        android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
+        android:value="org.chromium.chrome.browser.media.router.caf.CastOptionsProvider"/>
+    <meta-data
+        android:name="com.google.android.gms.version"
+        android:value="@integer/google_play_services_version"/>
+    <meta-data android:name="com.google.ar.core" android:value="optional"/>
+    <meta-data android:name="com.google.ar.core.min_apk_version" android:value="181012000"/>
+    <meta-data android:name="com.samsung.android.sdk.multiwindow.enable" android:value="true"/>
+    <meta-data
+        android:name="com.samsung.android.sdk.multiwindow.penwindow.enable"
+        android:value="true"/>
+    <meta-data android:name="com.sec.android.support.multiwindow" android:value="true"/>
+    <meta-data
+        android:name="ipc.invalidation.ticl.gcm_upstream_service_class"
+        android:value="org.chromium.chrome.browser.services.gcm.InvalidationGcmUpstreamSender"/>
+    <meta-data
+        android:name="ipc.invalidation.ticl.listener_service_class"
+        android:value="org.chromium.chrome.browser.invalidation.ChromeInvalidationClientService"/>
+    <meta-data
+        android:name="org.chromium.chrome:privileged_process0.ignore_multidex"
+        android:value="true"/>
+    <meta-data
+        android:name="org.chromium.chrome:privileged_process1.ignore_multidex"
+        android:value="true"/>
+    <meta-data
+        android:name="org.chromium.chrome:privileged_process2.ignore_multidex"
+        android:value="true"/>
+    <meta-data
+        android:name="org.chromium.chrome:privileged_process3.ignore_multidex"
+        android:value="true"/>
+    <meta-data
+        android:name="org.chromium.chrome:privileged_process4.ignore_multidex"
+        android:value="true"/>
+    <meta-data
+        android:name="org.chromium.content.browser.NUM_PRIVILEGED_SERVICES"
+        android:value="5"/>
+    <meta-data
+        android:name="org.chromium.content.browser.NUM_SANDBOXED_SERVICES"
+        android:value="40"/>
+    <meta-data
+        android:name="org.chromium.content.browser.REMOTE_MEDIA_PLAYERS"
+        android:value="org.chromium.chrome.browser.media.remote.DefaultMediaRouteController"/>
+    <meta-data
+        android:name="org.chromium.content.browser.SMART_CLIP_PROVIDER"
+        android:value="org.chromium.content_public.browser.SmartClipProvider"/>
+    <provider
+        android:authorities="org.chromium.chrome.ChromeBrowserProvider;org.chromium.chrome.browser;org.chromium.chrome"
+        android:exported="true"
+        android:name="org.chromium.chrome.browser.provider.ChromeBrowserProvider">
+      <path-permission
+          android:path="/bookmarks/search_suggest_query"
+          android:readPermission="android.permission.GLOBAL_SEARCH"/>
+    </provider>
+    <provider
+        android:authorities="org.chromium.chrome.FileProvider"
+        android:exported="false"
+        android:grantUriPermissions="true"
+        android:name="org.chromium.chrome.browser.util.ChromeFileProvider">
+      <meta-data
+          android:name="android.support.FILE_PROVIDER_PATHS"
+          android:resource="@xml/file_paths"/>
+    </provider>
+    <provider
+        android:authorities="org.chromium.chrome.LicenseContentProvider"
+        android:exported="true"
+        android:name="com.android.webview.chromium.LicenseContentProvider"/>
+    <receiver
+        android:exported="false"
+        android:name="com.google.android.gms.cast.framework.media.MediaIntentReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="com.google.ipc.invalidation.external.client.contrib.AndroidListener$AlarmReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="com.google.ipc.invalidation.ticl.android2.AndroidInternalScheduler$AlarmReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetProxy"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.download.DownloadBroadcastReceiver">
+      <intent-filter>
+        <action android:name="android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"/>
+      </intent-filter>
+    </receiver>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.locale.LocaleChangedBroadcastReceiver">
+      <intent-filter>
+        <action android:name="android.intent.action.LOCALE_CHANGED"/>
+      </intent-filter>
+    </receiver>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.notifications.NotificationIntentInterceptor$Receiver"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.notifications.NotificationService$Receiver">
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.notifications.CLICK_NOTIFICATION"/>
+        <action android:name="org.chromium.chrome.browser.notifications.CLOSE_NOTIFICATION"/>
+      </intent-filter>
+    </receiver>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$DeleteReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$OpenUrlReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.ntp.ContentSuggestionsNotifier$TimeoutReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.offlinepages.AutoFetchNotifier$CompleteNotificationReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.offlinepages.AutoFetchNotifier$InProgressCancelReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$ClickReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.offlinepages.prefetch.PrefetchedPagesNotifier$SettingsReceiver"/>
+    <receiver
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.upgrade.PackageReplacedBroadcastReceiver">
+      <intent-filter>
+        <action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
+      </intent-filter>
+    </receiver>
+    <receiver
+        android:exported="true"
+        android:name="com.google.android.gms.gcm.GcmReceiver"
+        android:permission="com.google.android.c2dm.permission.SEND">
+      <intent-filter>
+        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
+        <action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
+        <category android:name="org.chromium.chrome"/>
+      </intent-filter>
+    </receiver>
+    <receiver
+        android:exported="true"
+        android:name="org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver">
+      <intent-filter>
+        <action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
+        <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED"/>
+        <data android:scheme="package"/>
+      </intent-filter>
+      <intent-filter>
+        <action
+            android:name="org.chromium.chrome.browser.browserservices.ClientAppBroadcastReceiver.DEBUG"/>
+      </intent-filter>
+    </receiver>
+    <receiver
+        android:label="@string/bookmark_widget_title"
+        android:name="com.google.android.apps.chrome.appwidget.bookmarks.BookmarkThumbnailWidgetProvider">
+      <intent-filter>
+        <action android:name=".BOOKMARK_APPWIDGET_UPDATE"/>
+        <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+      </intent-filter>
+      <meta-data
+          android:name="android.appwidget.provider"
+          android:resource="@xml/bookmark_widget_info"/>
+    </receiver>
+    <receiver
+        android:label="@string/search_widget_title"
+        android:name="org.chromium.chrome.browser.searchwidget.SearchWidgetProvider">
+      <intent-filter>
+        <action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.searchwidget.START_TEXT_QUERY"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.searchwidget.START_VOICE_QUERY"/>
+      </intent-filter>
+      <intent-filter>
+        <action android:name="org.chromium.chrome.browser.searchwidget.UPDATE_ALL_WIDGETS"/>
+      </intent-filter>
+      <meta-data
+          android:name="android.appwidget.provider"
+          android:resource="@xml/search_widget_info"/>
+    </receiver>
+    <receiver
+        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastMediaButtonReceiver">
+      <intent-filter>
+        <action android:name="android.intent.action.MEDIA_BUTTON"/>
+      </intent-filter>
+    </receiver>
+    <receiver
+        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackMediaButtonReceiver">
+      <intent-filter>
+        <action android:name="android.intent.action.MEDIA_BUTTON"/>
+      </intent-filter>
+    </receiver>
+    <receiver
+        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationMediaButtonReceiver">
+      <intent-filter>
+        <action android:name="android.intent.action.MEDIA_BUTTON"/>
+      </intent-filter>
+    </receiver>
+    <receiver android:name="org.chromium.chrome.browser.services.AccountsChangedReceiver">
+      <intent-filter>
+        <action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
+      </intent-filter>
+    </receiver>
+    <service
+        android:description="@string/decoder_description"
+        android:exported="false"
+        android:isolatedProcess="true"
+        android:name="org.chromium.chrome.browser.photo_picker.DecoderService"
+        android:process=":decoder_service"/>
+    <service
+        android:exported="false"
+        android:isolatedProcess="false"
+        android:name="org.chromium.content.app.PrivilegedProcessService0"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":privileged_process0"/>
+    <service
+        android:exported="false"
+        android:isolatedProcess="false"
+        android:name="org.chromium.content.app.PrivilegedProcessService1"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":privileged_process1"/>
+    <service
+        android:exported="false"
+        android:isolatedProcess="false"
+        android:name="org.chromium.content.app.PrivilegedProcessService2"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":privileged_process2"/>
+    <service
+        android:exported="false"
+        android:isolatedProcess="false"
+        android:name="org.chromium.content.app.PrivilegedProcessService3"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":privileged_process3"/>
+    <service
+        android:exported="false"
+        android:isolatedProcess="false"
+        android:name="org.chromium.content.app.PrivilegedProcessService4"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":privileged_process4"/>
+    <service
+        android:exported="false"
+        android:name="com.google.android.gms.cast.framework.ReconnectionService"/>
+    <service
+        android:exported="false"
+        android:name="com.google.android.gms.cast.framework.media.MediaNotificationService"/>
+    <service
+        android:exported="false"
+        android:name="com.google.ipc.invalidation.ticl.android2.TiclService"/>
+    <service
+        android:exported="false"
+        android:name="com.google.ipc.invalidation.ticl.android2.channel.AndroidInstanceIDListenerService">
+      <intent-filter>
+        <action android:name="com.google.android.gms.iid.InstanceID"/>
+      </intent-filter>
+    </service>
+    <service
+        android:exported="false"
+        android:name="com.google.ipc.invalidation.ticl.android2.channel.AndroidMessageSenderService"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.android_webview.services.AwVariationsSeedFetcher"
+        android:permission="android.permission.BIND_JOB_SERVICE"
+        android:process=":webview_service"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.bookmarkswidget.BookmarkWidgetService"
+        android:permission="android.permission.BIND_REMOTEVIEWS"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.browseractions.BrowserActionsService"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.browserservices.ClearDataService"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.crash.ChromeMinidumpUploadJobService"
+        android:permission="android.permission.BIND_JOB_SERVICE"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.crash.MinidumpUploadService"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.download.DownloadBroadcastManager"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.download.DownloadForegroundService"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.incognito.IncognitoNotificationService"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.invalidation.ChromeBrowserSyncAdapterService">
+      <intent-filter>
+        <action android:name="android.content.SyncAdapter"/>
+      </intent-filter>
+      <meta-data android:name="android.content.SyncAdapter" android:resource="@xml/syncadapter"/>
+    </service>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.invalidation.ChromeInvalidationClientService">
+      <intent-filter>
+        <action android:name="com.google.ipc.invalidation.AUTH_TOKEN_REQUEST"/>
+      </intent-filter>
+    </service>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.media.MediaCaptureNotificationService"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$CastListenerService">
+      <intent-filter>
+        <action android:name="android.intent.action.MEDIA_BUTTON"/>
+      </intent-filter>
+    </service>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PlaybackListenerService">
+      <intent-filter>
+        <action android:name="android.intent.action.MEDIA_BUTTON"/>
+      </intent-filter>
+    </service>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.media.ui.MediaNotificationManager$PresentationListenerService">
+      <intent-filter>
+        <action android:name="android.intent.action.MEDIA_BUTTON"/>
+      </intent-filter>
+    </service>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.notifications.NotificationJobService"
+        android:permission="android.permission.BIND_JOB_SERVICE"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.notifications.NotificationService"/>
+    <service android:exported="false" android:name="org.chromium.chrome.browser.omaha.OmahaClient"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.services.gcm.ChromeGcmListenerService">
+      <intent-filter>
+        <action android:name="com.google.android.c2dm.intent.RECEIVE"/>
+      </intent-filter>
+    </service>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.services.gcm.InvalidationGcmUpstreamSender"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.tracing.TracingNotificationService"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.chrome.browser.upgrade.UpgradeIntentService"/>
+    <service
+        android:exported="false"
+        android:name="org.chromium.components.background_task_scheduler.BackgroundTaskJobService"
+        android:permission="android.permission.BIND_JOB_SERVICE"/>
+    <service
+        android:exported="true"
+        android:name="com.google.ipc.invalidation.ticl.android2.channel.GcmRegistrationTaskService"
+        android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">
+      <intent-filter>
+        <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
+      </intent-filter>
+    </service>
+    <service
+        android:exported="true"
+        android:name="org.chromium.android_webview.services.AwMinidumpUploadJobService"
+        android:permission="android.permission.BIND_JOB_SERVICE"
+        android:process=":webview_service"/>
+    <service
+        android:exported="true"
+        android:name="org.chromium.android_webview.services.CrashReceiverService"
+        android:process=":webview_service"/>
+    <service
+        android:exported="true"
+        android:name="org.chromium.android_webview.services.VariationsSeedServer"
+        android:process=":webview_service"/>
+    <service
+        android:exported="true"
+        android:name="org.chromium.chrome.browser.ChromeBackgroundService"
+        android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">
+      <intent-filter>
+        <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
+      </intent-filter>
+    </service>
+    <service
+        android:exported="true"
+        android:name="org.chromium.components.background_task_scheduler.BackgroundTaskGcmTaskService"
+        android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">
+      <intent-filter>
+        <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY"/>
+      </intent-filter>
+    </service>
+    <service android:name="android.support.customtabs.PostMessageService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService0"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process0"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService1"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process1"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService10"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process10"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService11"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process11"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService12"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process12"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService13"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process13"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService14"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process14"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService15"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process15"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService16"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process16"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService17"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process17"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService18"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process18"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService19"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process19"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService2"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process2"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService20"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process20"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService21"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process21"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService22"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process22"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService23"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process23"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService24"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process24"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService25"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process25"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService26"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process26"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService27"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process27"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService28"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process28"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService29"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process29"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService3"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process3"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService30"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process30"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService31"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process31"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService32"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process32"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService33"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process33"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService34"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process34"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService35"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process35"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService36"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process36"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService37"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process37"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService38"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process38"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService39"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process39"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService4"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process4"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService5"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process5"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService6"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process6"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService7"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process7"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService8"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process8"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:externalService="true"
+        android:isolatedProcess="true"
+        android:name="org.chromium.content.app.SandboxedProcessService9"
+        android:permission="org.chromium.chrome.permission.CHILD_SERVICE"
+        android:process=":sandboxed_process9"
+        android:visibleToInstantApps="true"
+        tools:ignore="ExportedService"/>
+    <service
+        android:exported="true"
+        android:name="org.chromium.chrome.browser.customtabs.CustomTabsConnectionService"
+        tools:ignore="ExportedService">
+      <intent-filter>
+        <action android:name="android.support.customtabs.action.CustomTabsService"/>
+      </intent-filter>
+    </service>
+    <service
+        android:exported="true"
+        android:name="org.chromium.chrome.browser.prerender.ChromePrerenderService"
+        tools:ignore="ExportedService"/>
+  </application>
 </manifest>
\ No newline at end of file
diff --git a/chrome/android/java/res/drawable-hdpi/preview_pin_round.png b/chrome/android/java/res/drawable-hdpi/preview_pin_round.png
index ab15f52..0f2f045 100644
--- a/chrome/android/java/res/drawable-hdpi/preview_pin_round.png
+++ b/chrome/android/java/res/drawable-hdpi/preview_pin_round.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-mdpi/preview_pin_round.png b/chrome/android/java/res/drawable-mdpi/preview_pin_round.png
index a44a72f2..0b021ac 100644
--- a/chrome/android/java/res/drawable-mdpi/preview_pin_round.png
+++ b/chrome/android/java/res/drawable-mdpi/preview_pin_round.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xhdpi/preview_pin_round.png b/chrome/android/java/res/drawable-xhdpi/preview_pin_round.png
index ce2768d..280d3a5 100644
--- a/chrome/android/java/res/drawable-xhdpi/preview_pin_round.png
+++ b/chrome/android/java/res/drawable-xhdpi/preview_pin_round.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxhdpi/preview_pin_round.png b/chrome/android/java/res/drawable-xxhdpi/preview_pin_round.png
index ea25928..c871569 100644
--- a/chrome/android/java/res/drawable-xxhdpi/preview_pin_round.png
+++ b/chrome/android/java/res/drawable-xxhdpi/preview_pin_round.png
Binary files differ
diff --git a/chrome/android/java/res/drawable-xxxhdpi/preview_pin_round.png b/chrome/android/java/res/drawable-xxxhdpi/preview_pin_round.png
index 08f2f470..33614ef 100644
--- a/chrome/android/java/res/drawable-xxxhdpi/preview_pin_round.png
+++ b/chrome/android/java/res/drawable-xxxhdpi/preview_pin_round.png
Binary files differ
diff --git a/chrome/android/java/res/drawable/contacts_big.xml b/chrome/android/java/res/drawable/contacts_big.xml
new file mode 100644
index 0000000..6a85a4b7
--- /dev/null
+++ b/chrome/android/java/res/drawable/contacts_big.xml
@@ -0,0 +1,18 @@
+<?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="64dp"
+        android:height="64dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:pathData="M0 0h24v24H0zm0 0h24v24H0zm0 0h24v24H0z" />
+    <path
+        android:fillColor="@color/black_alpha_38"
+        android:pathData="M20 0H4v2h16V0zM4 24h16v-2H4v2zM20 4H4c-1.1 0-2 0.9-2 2v12c0 1.1 0.9 2 2 2h16c1.1 0 2-0.9 2-2V6c0-1.1-0.9-2-2-2zm-8 2.75c1.24 0 2.25 1.01 2.25 2.25s-1.01 2.25-2.25 2.25S9.75 10.24 9.75 9 10.76 6.75 12 6.75zM17 17H7v-1.5c0-1.67 3.33-2.5 5-2.5s5 0.83 5 2.5V17z" />
+</vector>
diff --git a/chrome/android/java/res/drawable/content_suggestions_card_corner_bottom.xml b/chrome/android/java/res/drawable/content_suggestions_card_corner_bottom.xml
index 0a008893..bb58b5e 100644
--- a/chrome/android/java/res/drawable/content_suggestions_card_corner_bottom.xml
+++ b/chrome/android/java/res/drawable/content_suggestions_card_corner_bottom.xml
@@ -7,8 +7,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     tools:targetApi="21"
-    android:width="@dimen/content_suggestions_card_modern_corner_radius"
-    android:height="@dimen/content_suggestions_card_modern_corner_radius"
+    android:width="@dimen/default_card_corner_radius"
+    android:height="@dimen/default_card_corner_radius"
     android:viewportWidth="10"
     android:viewportHeight="10">
     <path
diff --git a/chrome/android/java/res/drawable/content_suggestions_card_corner_top.xml b/chrome/android/java/res/drawable/content_suggestions_card_corner_top.xml
index 873617b..3e13349 100644
--- a/chrome/android/java/res/drawable/content_suggestions_card_corner_top.xml
+++ b/chrome/android/java/res/drawable/content_suggestions_card_corner_top.xml
@@ -7,8 +7,8 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
     tools:targetApi="21"
-    android:width="@dimen/content_suggestions_card_modern_corner_radius"
-    android:height="@dimen/content_suggestions_card_modern_corner_radius"
+    android:width="@dimen/default_card_corner_radius"
+    android:height="@dimen/default_card_corner_radius"
     android:viewportWidth="10"
     android:viewportHeight="10">
     <path
diff --git a/chrome/android/java/res/drawable/contextual_suggestions_placeholder_thumbnail.xml b/chrome/android/java/res/drawable/contextual_suggestions_placeholder_thumbnail.xml
index 59ad487..b1c8a444 100644
--- a/chrome/android/java/res/drawable/contextual_suggestions_placeholder_thumbnail.xml
+++ b/chrome/android/java/res/drawable/contextual_suggestions_placeholder_thumbnail.xml
@@ -7,5 +7,5 @@
     <solid
         android:color="@color/thumbnail_placeholder_on_white_bg" />
     <corners
-        android:radius="@dimen/snippets_thumbnail_small_corner_radius" />
+        android:radius="@dimen/default_card_corner_radius" />
 </shape>
\ No newline at end of file
diff --git a/chrome/android/java/res/drawable/hairline_border_card_background.xml b/chrome/android/java/res/drawable/hairline_border_card_background.xml
index 09942a3..03444e4 100644
--- a/chrome/android/java/res/drawable/hairline_border_card_background.xml
+++ b/chrome/android/java/res/drawable/hairline_border_card_background.xml
@@ -8,5 +8,5 @@
     android:shape="rectangle" >
     <solid android:color="@android:color/white" />
     <stroke android:width="1dp" android:color="@color/hairline_stroke_color"/>
-    <corners android:radius="@dimen/content_suggestions_card_modern_corner_radius" />
+    <corners android:radius="@dimen/default_card_corner_radius" />
 </shape>
diff --git a/chrome/android/java/res/drawable/ic_lock_24dp.xml b/chrome/android/java/res/drawable/ic_lock_24dp.xml
deleted file mode 100644
index bad28f0..0000000
--- a/chrome/android/java/res/drawable/ic_lock_24dp.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?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"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportHeight="24"
-    android:viewportWidth="24"
-    tools:targetApi="21">
-
-    <path
-        android:pathData="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 0.9-2 2v10c0 1.1 0.9 2 2 2h12c1.1 0 2-0.9 2-2V10c0-1.1-0.9-2-2-2zm-6 9c-1.1 0-2-0.9-2-2s0.9-2 2-2 2 0.9 2 2-0.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"
-        android:fillColor="@color/google_yellow_600"/>
-</vector>
diff --git a/chrome/android/java/res/drawable/selected_tab_background.xml b/chrome/android/java/res/drawable/selected_tab_background.xml
new file mode 100644
index 0000000..5260d9b
--- /dev/null
+++ b/chrome/android/java/res/drawable/selected_tab_background.xml
@@ -0,0 +1,11 @@
+<?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. -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <stroke
+        android:width="3dp"
+        android:color="@color/modern_blue_600" />
+    <corners android:radius="@dimen/default_card_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/chrome/android/java/res/drawable/tab_grid_card_background.xml b/chrome/android/java/res/drawable/tab_grid_card_background.xml
new file mode 100644
index 0000000..55cb8e00
--- /dev/null
+++ b/chrome/android/java/res/drawable/tab_grid_card_background.xml
@@ -0,0 +1,9 @@
+<?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. -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="@android:color/white"/>
+    <corners android:radius="@dimen/default_card_corner_radius" />
+</shape>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/content_suggestions_card_modern_reversed.xml b/chrome/android/java/res/layout/content_suggestions_card_modern_reversed.xml
index 15a3df9..b0c58ba 100644
--- a/chrome/android/java/res/layout/content_suggestions_card_modern_reversed.xml
+++ b/chrome/android/java/res/layout/content_suggestions_card_modern_reversed.xml
@@ -116,8 +116,8 @@
          cannot select which corners must be rounded, they are all rounded. -->
     <ImageView
         android:id="@+id/corner_top"
-        android:layout_width="@dimen/content_suggestions_card_modern_corner_radius"
-        android:layout_height="@dimen/content_suggestions_card_modern_corner_radius"
+        android:layout_width="@dimen/default_card_corner_radius"
+        android:layout_height="@dimen/default_card_corner_radius"
         android:layout_alignTop="@id/card_contents"
         android:layout_alignParentEnd="true"
         tools:ignore="ContentDescription"
@@ -126,8 +126,8 @@
 
     <ImageView
         android:id="@+id/corner_bottom"
-        android:layout_width="@dimen/content_suggestions_card_modern_corner_radius"
-        android:layout_height="@dimen/content_suggestions_card_modern_corner_radius"
+        android:layout_width="@dimen/default_card_corner_radius"
+        android:layout_height="@dimen/default_card_corner_radius"
         android:layout_alignBottom="@id/card_contents"
         android:layout_alignParentEnd="true"
         tools:ignore="ContentDescription"
diff --git a/chrome/android/java/res/layout/data_reduction_main_menu_item.xml b/chrome/android/java/res/layout/data_reduction_main_menu_item.xml
index 60fbb74..444fd16 100644
--- a/chrome/android/java/res/layout/data_reduction_main_menu_item.xml
+++ b/chrome/android/java/res/layout/data_reduction_main_menu_item.xml
@@ -5,6 +5,7 @@
 
 <org.chromium.chrome.browser.preferences.datareduction.DataReductionMainMenuItem
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/data_reduction_menu_item"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -23,13 +24,14 @@
         android:paddingBottom="12dp"
         style="@style/AppMenuItem" >
 
+        <!-- ContentDescription is set in Java code. -->
         <ImageView
+            tools:ignore="ContentDescription"
             android:id="@+id/chart_icon"
             android:src="@drawable/data_reduction_main_menu_icon"
             android:layout_width="@dimen/data_reduction_main_menu_icon_width"
             android:layout_height="match_parent"
-            android:layout_gravity="start|center_vertical"
-            android:contentDescription="@string/data_reduction_title" />
+            android:layout_gravity="start|center_vertical" />
 
         <LinearLayout
             android:layout_width="wrap_content"
diff --git a/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml b/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml
new file mode 100644
index 0000000..f2bbbe1
--- /dev/null
+++ b/chrome/android/java/res/layout/fre_data_reduction_proxy_lite_mode.xml
@@ -0,0 +1,92 @@
+<?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.
+-->
+<org.chromium.chrome.browser.firstrun.FirstRunView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginBottom="@dimen/fre_button_bar_height"
+        android:fillViewport="true">
+
+        <LinearLayout
+            android:id="@+id/fre_main_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical"
+            android:gravity="center_horizontal">
+
+            <TextView
+                android:id="@+id/title"
+                android:text="@string/data_reduction_promo_title_lite_mode"
+                style="@style/FreTitle"/>
+
+            <!-- The orientation of this view is changed dynamically to give a nicer layout when in
+            landscape mode on devices with small screens. -->
+            <LinearLayout
+                android:id="@+id/fre_image_and_content"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:gravity="center_horizontal"
+                android:layout_marginTop="@dimen/fre_vertical_spacing">
+
+                <ImageView
+                    android:id="@+id/image"
+                    android:layout_width="wrap_content"
+                    android:layout_height="@dimen/fre_image_height"
+                    tools:ignore="ContentDescription"
+                    android:src="@drawable/data_reduction_illustration" />
+
+                <LinearLayout
+                    android:id="@+id/fre_content_wrapper"
+                    android:layout_width="0dp"
+                    android:layout_height="0dp"
+                    android:layout_weight="1"
+                    android:layout_marginTop="@dimen/fre_vertical_spacing"
+                    android:layout_marginEnd="@dimen/fre_content_margin"
+                    android:layout_marginStart="@dimen/fre_content_margin"
+                    android:orientation="vertical" >
+
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginBottom="@dimen/fre_vertical_spacing"
+                        android:gravity="start"
+                        android:lineSpacingMultiplier="1.4"
+                        android:text="@string/data_reduction_promo_summary_lite_mode"
+                        android:textAppearance="@style/TextAppearance.BlackBodyDefault" />
+
+                    <android.support.v7.widget.SwitchCompat
+                        android:id="@+id/enable_data_saver_switch"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:lineSpacingMultiplier="1.4"
+                        android:showText="false"
+                        android:text="@string/data_reduction_enabled_switch_lite_mode"
+                        style="@style/TextAppearance.BlackTitle2" />
+
+                </LinearLayout>
+            </LinearLayout>
+        </LinearLayout>
+    </ScrollView>
+
+    <!-- fre_button_bar_height = 52dp = layout_height + layout_marginBottom -->
+    <org.chromium.ui.widget.ButtonCompat
+        android:id="@+id/next_button"
+        android:layout_width="wrap_content"
+        android:layout_height="36dp"
+        android:layout_marginBottom="16dp"
+        android:layout_gravity="bottom|center_horizontal"
+        android:paddingStart="@dimen/fre_button_padding"
+        android:paddingEnd="@dimen/fre_button_padding"
+        android:text="@string/next"
+        style="@style/FilledButton.Flat" />
+</org.chromium.chrome.browser.firstrun.FirstRunView>
diff --git a/chrome/android/java/res/layout/signin_view.xml b/chrome/android/java/res/layout/signin_view.xml
index 5037f6c..7d91af6 100644
--- a/chrome/android/java/res/layout/signin_view.xml
+++ b/chrome/android/java/res/layout/signin_view.xml
@@ -28,6 +28,8 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_alignParentTop="true"
+                android:paddingTop="24dp"
+                android:paddingBottom="24dp"
                 android:background="@color/signin_header_animation_background"
                 android:scaleType="center"
                 app:srcCompat="@drawable/signin_header_animation"
@@ -39,7 +41,7 @@
                 android:layout_below="@id/signin_header_image"
                 android:layout_marginStart="16dp"
                 android:layout_marginTop="16dp"
-                android:layout_marginEnd="@dimen/signin_margin_end"
+                android:layout_marginEnd="16dp"
                 android:textAppearance="@style/TextAppearance.BlackHeadline"
                 tools:text="@string/signin_title"/>
             <LinearLayout
@@ -49,7 +51,7 @@
                 android:layout_below="@id/signin_title"
                 android:layout_marginStart="16dp"
                 android:layout_marginTop="16dp"
-                android:layout_marginEnd="@dimen/signin_margin_end"
+                android:layout_marginEnd="16dp"
                 android:background="@drawable/account_picker_background"
                 android:orientation="horizontal"
                 android:gravity="center_vertical"
@@ -62,8 +64,8 @@
                     android:id="@+id/account_image"
                     android:layout_width="56dp"
                     android:layout_height="40dp"
-                    tools:ignore="ContentDescription"
                     android:scaleType="fitStart"
+                    tools:ignore="ContentDescription"
                     tools:src="@drawable/logo_avatar_anonymous"/>
                 <LinearLayout
                     android:layout_width="0dp"
@@ -97,28 +99,14 @@
                     tools:src="@drawable/ic_expand_more_black_24dp"
                     tools:tint="@color/dark_mode_tint"/>
             </LinearLayout>
-            <ImageView
-                android:id="@+id/signin_sync_icon"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
-                android:layout_alignTop="@+id/signin_sync_title"
-                android:layout_alignBottom="@+id/signin_sync_description"
-                android:layout_alignParentStart="true"
-                android:layout_centerVertical="true"
-                android:layout_marginStart="@dimen/signin_margin_start"
-                app:srcCompat="@drawable/permission_background_sync"
-                app:tint="@color/google_green_600"
-                tools:ignore="ContentDescription"/>
             <TextView
                 android:id="@+id/signin_sync_title"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_below="@id/signin_account_picker"
-                android:layout_alignParentEnd="true"
-                android:layout_marginStart="@dimen/signin_drawable_padding"
+                android:layout_marginStart="16dp"
                 android:layout_marginTop="28dp"
-                android:layout_marginEnd="@dimen/signin_margin_end"
-                android:layout_toEndOf="@id/signin_sync_icon"
+                android:layout_marginEnd="16dp"
                 android:textAppearance="@style/TextAppearance.BlackTitle1"
                 tools:text="@string/signin_sync_title"/>
             <TextView
@@ -126,98 +114,25 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_below="@id/signin_sync_title"
-                android:layout_alignParentEnd="true"
-                android:layout_marginStart="@dimen/signin_drawable_padding"
-                android:layout_marginEnd="@dimen/signin_margin_end"
-                android:layout_toEndOf="@id/signin_sync_icon"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="16dp"
                 android:textAppearance="@style/TextAppearance.BlackBody"
                 tools:text="@string/signin_sync_description"/>
-            <ImageView
-                android:id="@+id/signin_tap_to_search_icon"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
-                android:layout_alignTop="@+id/signin_tap_to_search_title"
-                android:layout_alignBottom="@+id/signin_tap_to_search_description"
-                android:layout_alignParentStart="true"
-                android:layout_centerVertical="true"
-                android:layout_marginStart="@dimen/signin_margin_start"
-                app:srcCompat="@drawable/ic_search"
-                app:tint="@color/default_icon_color_blue"
-                tools:ignore="ContentDescription"/>
-            <TextView
-                android:id="@+id/signin_tap_to_search_title"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/signin_sync_description"
-                android:layout_alignParentEnd="true"
-                android:layout_marginStart="@dimen/signin_drawable_padding"
-                android:layout_marginTop="24dp"
-                android:layout_marginEnd="@dimen/signin_margin_end"
-                android:layout_toEndOf="@id/signin_tap_to_search_icon"
-                android:textAppearance="@style/TextAppearance.BlackTitle1"
-                tools:text="@string/signin_tap_to_search_title"/>
-            <TextView
-                android:id="@+id/signin_tap_to_search_description"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/signin_tap_to_search_title"
-                android:layout_alignParentEnd="true"
-                android:layout_marginStart="@dimen/signin_drawable_padding"
-                android:layout_marginEnd="@dimen/signin_margin_end"
-                android:layout_toEndOf="@id/signin_tap_to_search_icon"
-                android:textAppearance="@style/TextAppearance.BlackBody"
-                tools:text="@string/signin_tap_to_search_description"/>
-            <ImageView
-                android:id="@+id/signin_safe_browsing_icon"
-                android:layout_width="24dp"
-                android:layout_height="24dp"
-                android:layout_alignTop="@+id/signin_safe_browsing_title"
-                android:layout_alignBottom="@+id/signin_safe_browsing_description"
-                android:layout_alignParentStart="true"
-                android:layout_centerVertical="true"
-                android:layout_marginStart="@dimen/signin_margin_start"
-                app:srcCompat="@drawable/ic_lock_24dp"
-                tools:ignore="ContentDescription"/>
-            <TextView
-                android:id="@+id/signin_safe_browsing_title"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/signin_tap_to_search_description"
-                android:layout_alignParentEnd="true"
-                android:layout_marginStart="@dimen/signin_drawable_padding"
-                android:layout_marginTop="24dp"
-                android:layout_marginEnd="@dimen/signin_margin_end"
-                android:layout_toEndOf="@id/signin_safe_browsing_icon"
-                android:textAppearance="@style/TextAppearance.BlackTitle1"
-                tools:text="@string/signin_safe_browsing_title"/>
-            <TextView
-                android:id="@+id/signin_safe_browsing_description"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/signin_safe_browsing_title"
-                android:layout_alignParentEnd="true"
-                android:layout_marginStart="@dimen/signin_drawable_padding"
-                android:layout_marginEnd="@dimen/signin_margin_end"
-                android:layout_toEndOf="@id/signin_safe_browsing_icon"
-                android:textAppearance="@style/TextAppearance.BlackBody"
-                tools:text="@string/signin_safe_browsing_description"/>
             <View
                 android:id="@+id/signin_divider"
                 style="@style/HorizontalDivider"
-                android:layout_below="@id/signin_safe_browsing_description"
-                android:layout_marginStart="@dimen/signin_margin_start"
+                android:layout_below="@id/signin_sync_description"
+                android:layout_marginStart="16dp"
                 android:layout_marginTop="12dp"
-                android:layout_marginEnd="@dimen/signin_margin_end"/>
+                android:layout_marginEnd="16dp"/>
             <TextView
                 android:id="@+id/signin_details_description"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_below="@id/signin_divider"
-                android:layout_alignParentStart="true"
-                android:layout_alignParentEnd="true"
-                android:layout_marginStart="@dimen/signin_drawable_padding"
+                android:layout_marginStart="16dp"
                 android:layout_marginTop="16dp"
-                android:layout_marginEnd="@dimen/signin_margin_end"
+                android:layout_marginEnd="16dp"
                 android:textAppearance="@style/TextAppearance.BlackBody"
                 tools:text="@string/signin_details_description"/>
         </RelativeLayout>
@@ -230,12 +145,12 @@
         android:padding="16dp">
         <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/negative_button"
+            style="@style/TextButton"
             android:layout_width="wrap_content"
             android:layout_height="36dp"
             android:paddingStart="@dimen/fre_button_padding"
             android:paddingEnd="@dimen/fre_button_padding"
-            tools:text="@string/no_thanks"
-            style="@style/TextButton" />
+            tools:text="@string/no_thanks"/>
         <View
             android:layout_width="0dp"
             android:layout_height="0dp"
@@ -243,21 +158,21 @@
             android:visibility="invisible"/>
         <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/positive_button"
+            style="@style/FilledButton.Flat"
             android:layout_width="wrap_content"
             android:layout_height="36dp"
             android:paddingStart="@dimen/fre_button_padding"
             android:paddingEnd="@dimen/fre_button_padding"
-            tools:text="@string/signin_accept_button"
-            style="@style/FilledButton.Flat" />
+            tools:text="@string/signin_accept_button"/>
         <org.chromium.ui.widget.ButtonCompat
             android:id="@+id/more_button"
+            style="@style/TextButton"
             android:layout_width="wrap_content"
             android:layout_height="36dp"
             android:drawableEnd="@drawable/down_arrow"
             android:drawablePadding="8dp"
             android:visibility="gone"
-            tools:text="@string/more"
-            style="@style/TextButton" />
+            tools:text="@string/more"/>
         <View
             android:id="@+id/positive_button_end_padding"
             android:layout_width="0dp"
diff --git a/chrome/android/java/res/layout/tab_grid_card_item.xml b/chrome/android/java/res/layout/tab_grid_card_item.xml
new file mode 100644
index 0000000..b2b8e69
--- /dev/null
+++ b/chrome/android/java/res/layout/tab_grid_card_item.xml
@@ -0,0 +1,59 @@
+<?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. -->
+<!-- TODO(crbug/928388): Spec issues with this layout: Handle elevation for KitKat. Touch target
+     for close button is too small. Text height needs to be verified for XL text while we are using
+     FrameLayout as parent -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="200dp"
+    android:background="@drawable/tab_grid_card_background"
+    android:elevation="4dp"
+    android:layout_margin="8dp">
+        <ImageView
+            android:id="@+id/tab_favicon"
+            android:layout_width="@dimen/tab_grid_favicon_size"
+            android:layout_height="@dimen/tab_grid_favicon_size"
+            android:padding="8dp"
+            android:importantForAccessibility="no"
+            android:src="@drawable/ic_omnibox_page"/>
+
+        <TextView
+            android:id="@+id/tab_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="32dp"
+            android:layout_marginEnd="32dp"
+            android:layout_marginStart="32dp"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:paddingBottom="6dp"
+            android:paddingTop="6dp"
+            android:textAppearance="@style/TextAppearance.BlackTitle2"/>
+        <ImageView
+            android:id="@+id/close_button"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:scaleType="center"
+            android:layout_gravity="end"
+            android:contentDescription="@string/accessibility_tabstrip_btn_close_tab"
+            android:src="@drawable/btn_delete_24dp"/>
+
+        <org.chromium.ui.widget.RoundedCornerImageView
+            android:id="@+id/tab_thumbnail"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginTop="32dp"
+            android:gravity="center_horizontal"
+            android:scaleType="centerCrop"
+            android:importantForAccessibility="no"
+            android:src="@color/thumbnail_placeholder_on_white_bg"
+            app:cornerRadiusBottomStart="@dimen/default_card_corner_radius"
+            app:cornerRadiusBottomEnd="@dimen/default_card_corner_radius"/>
+
+        <View
+            style="@style/HorizontalDivider"
+            android:layout_marginTop="32dp"/>
+</FrameLayout>
diff --git a/chrome/android/java/res/layout/tab_grid_recycler_view_layout.xml b/chrome/android/java/res/layout/tab_grid_recycler_view_layout.xml
new file mode 100644
index 0000000..f6c7afb1
--- /dev/null
+++ b/chrome/android/java/res/layout/tab_grid_recycler_view_layout.xml
@@ -0,0 +1,18 @@
+<?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. -->
+<org.chromium.chrome.browser.tasks.tab_list_ui.TabListRecyclerView
+    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:id="@+id/tab_grid_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingStart="16dp"
+    android:paddingEnd="16dp"
+    android:layout_marginTop="@dimen/control_container_height"
+    android:visibility="invisible"
+    android:background="@android:color/white"
+    app:layoutManager="GridLayoutManager"
+    tools:listitem="@layout/tab_grid_card_item"/>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 978efa7..b5c0d59 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -52,6 +52,9 @@
     <dimen name="tab_title_favicon_start_padding">10dp</dimen>
     <dimen name="tab_title_favicon_end_padding">7dp</dimen>
 
+    <!-- Card dimensions -->
+    <dimen name="default_card_corner_radius">8dp</dimen>
+
     <!-- Custom Menu dimensions -->
     <dimen name="menu_width">258dp</dimen>
     <dimen name="menu_negative_software_vertical_offset">0dp</dimen>
@@ -195,10 +198,6 @@
     <dimen name="signin_chooser_padding">16dp</dimen>
     <dimen name="signin_screen_top_padding">50dp</dimen>
 
-    <dimen name="signin_margin_start">22dp</dimen>
-    <dimen name="signin_margin_end">16dp</dimen>
-    <dimen name="signin_drawable_padding">22dp</dimen>
-
     <!-- Signin promo dimensions -->
     <dimen name="signin_promo_account_image_size">48dp</dimen>
     <dimen name="signin_promo_cold_state_image_size">24dp</dimen>
@@ -351,7 +350,6 @@
     <dimen name="ntp_progress_indicator_diameter">56dp</dimen>
     <dimen name="snippets_thumbnail_size">124dp</dimen>
     <dimen name="snippets_thumbnail_size_small">72dp</dimen>
-    <dimen name="snippets_thumbnail_small_corner_radius">8dp</dimen>
     <!-- The default padding for the peeking card and the amount the card peeks by in the current
          peeking card animation (the calculations assume this is the same). -->
     <dimen name="snippets_padding">16dp</dimen>
@@ -361,7 +359,6 @@
     <dimen name="snippets_offline_icon_size">18sp</dimen>
     <dimen name="ntp_suggestions_footer_vertical_padding">24dp</dimen>
     <dimen name="content_suggestions_card_modern_margin">12dp</dimen>
-    <dimen name="content_suggestions_card_modern_corner_radius">8dp</dimen>
     <!-- Used to offset content_suggestions_card_modern_margin applied to the bottom of the
          last card. -->
     <dimen name="content_suggestions_action_modern_margin_top">-16dp</dimen>
@@ -583,4 +580,6 @@
 
     <!-- Autofill Assistant dimensions -->
     <dimen name="autofill_assistant_details_image_size">48dp</dimen>
+
+    <dimen name="tab_grid_favicon_size">32dp</dimen>
 </resources>
diff --git a/chrome/android/java/res/xml/data_reduction_preferences_off_lite_mode.xml b/chrome/android/java/res/xml/data_reduction_preferences_off_lite_mode.xml
new file mode 100644
index 0000000..28784ce
--- /dev/null
+++ b/chrome/android/java/res/xml/data_reduction_preferences_off_lite_mode.xml
@@ -0,0 +1,18 @@
+<?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. -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+                  xmlns:app="http://schemas.android.com/apk/res-auto" >
+
+    <org.chromium.chrome.browser.preferences.TextMessagePreference
+        android:title="@string/data_reduction_benefits_description_lite_mode" />
+
+    <org.chromium.chrome.browser.preferences.TextMessagePreference
+        android:title="@string/data_reduction_description_lite_mode" />
+
+    <org.chromium.chrome.browser.preferences.LearnMorePreference
+        android:key="data_reduction_learn_more"
+        app:helpContext="@string/help_context_data_reduction" />
+</PreferenceScreen>
diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
index 31da2060..429beba 100644
--- a/chrome/android/java/res/xml/main_preferences.xml
+++ b/chrome/android/java/res/xml/main_preferences.xml
@@ -89,11 +89,11 @@
         android:key="languages"
         android:order="16"
         android:title="@string/prefs_languages"/>
-    <org.chromium.chrome.browser.preferences.ChromeBasePreference
-        android:fragment="org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferences"
+    <org.chromium.chrome.browser.preferences.datareduction.DataReductionPreference
+        android:fragment="org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferenceFragment"
         android:key="data_reduction"
         android:order="17"
-        android:title="@string/data_reduction_title"/>
+        android:title="@string/data_reduction_title_lite_mode"/>
     <org.chromium.chrome.browser.preferences.ChromeBasePreference
         android:fragment="org.chromium.chrome.browser.preferences.download.DownloadPreferences"
         android:key="downloads"
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 f9c5c08..d332f0a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -1495,6 +1495,13 @@
 
             mComponent.resolveContextualSuggestionsCoordinator();
         }
+
+        // TODO(yusufo) : Move the condition here to FeatureUtils and the resolution to a
+        //  ChromeTabbedActivity specific place.
+        if (!isTablet() && !SysUtils.isLowEndDevice()
+                && ChromeFeatureList.isEnabled(ChromeFeatureList.TAB_GRID_LAYOUT_ANDROID)) {
+            mComponent.resolveTabGridCoordinator();
+        }
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
index b4b20c9..4598b20a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeApplication.java
@@ -64,14 +64,13 @@
     public void onCreate() {
         super.onCreate();
         FontPreloadingWorkaround.maybeInstallWorkaround(this);
-        if (isBrowserProcess()) initDefaultNightMode();
     }
 
     // Called by the framework for ALL processes. Runs before ContentProviders are created.
     // Quirk: context.getApplicationContext() returns null during this method.
     @Override
     protected void attachBaseContext(Context context) {
-        boolean isBrowserProcess = isBrowserProcess();
+        boolean isBrowserProcess = !ContextUtils.getProcessName().contains(":");
         if (isBrowserProcess) UmaUtils.recordMainEntryPointTime();
         super.attachBaseContext(context);
         ContextUtils.initApplicationContext(this);
@@ -131,10 +130,6 @@
         AsyncTask.takeOverAndroidThreadPool();
     }
 
-    private static boolean isBrowserProcess() {
-        return !ContextUtils.getProcessName().contains(":");
-    }
-
     private static Boolean shouldUseDebugFlags() {
         return ChromePreferenceManager.getInstance().readBoolean(
                 ChromePreferenceManager.COMMAND_LINE_ON_NON_ROOTED_ENABLED_KEY, false);
@@ -229,7 +224,8 @@
         });
     }
 
-    private void initDefaultNightMode() {
+    // TODO(huayinz): move this to somewhere else.
+    public void initDefaultNightMode() {
         if (FeatureUtilities.isNightModeAvailable()) {
             // TODO(huayinz): Initialize default night mode based on settings.
             AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
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 29d16b6..90ccb6b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeFeatureList.java
@@ -165,6 +165,7 @@
     public static final String ANDROID_SITE_SETTINGS_UI_REFRESH = "AndroidSiteSettingsUIRefresh";
     public static final String APP_NOTIFICATION_STATUS_MESSAGING = "AppNotificationStatusMessaging";
     public static final String AUTOFILL_ASSISTANT = "AutofillAssistant";
+    public static final String AUTOFILL_MANUAL_FALLBACK_ANDROID = "AutofillManualFallbackAndroid";
     public static final String AUTOFILL_REFRESH_STYLE_ANDROID = "AutofillRefreshStyleAndroid";
     public static final String AUTOFILL_KEYBOARD_ACCESSORY = "AutofillKeyboardAccessory";
     public static final String CAPTIVE_PORTAL_CERTIFICATE_LIST = "CaptivePortalCertificateList";
@@ -207,6 +208,7 @@
     public static final String CONTEXTUAL_SUGGESTIONS_IPH_REVERSE_SCROLL =
             "ContextualSuggestionsIPHReverseScroll";
     public static final String CUSTOM_CONTEXT_MENU = "CustomContextMenu";
+    public static final String DATA_SAVER_LITE_MODE_REBRANDING = "DataSaverLiteModeRebranding";
     public static final String DONT_PREFETCH_LIBRARIES = "DontPrefetchLibraries";
     public static final String DOWNLOAD_HOME_SHOW_STORAGE_INFO = "DownloadHomeShowStorageInfo";
     public static final String DOWNLOAD_PROGRESS_INFOBAR = "DownloadProgressInfoBar";
@@ -289,6 +291,7 @@
     public static final String SPANNABLE_INLINE_AUTOCOMPLETE = "SpannableInlineAutocomplete";
     public static final String SUBRESOURCE_FILTER = "SubresourceFilter";
     public static final String QUERY_IN_OMNIBOX = "QueryInOmnibox";
+    public static final String TAB_GRID_LAYOUT_ANDROID = "TabGridLayoutAndroid";
     public static final String TAB_REPARENTING = "TabReparenting";
     public static final String TAB_SWITCHER_ON_RETURN = "TabSwitcherOnReturn";
     public static final String TRANSLATE_ANDROID_MANUAL_TRIGGER = "TranslateAndroidManualTrigger";
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/CreditCardAccessorySheetCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/CreditCardAccessorySheetCoordinator.java
new file mode 100644
index 0000000..c1cfc52
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/CreditCardAccessorySheetCoordinator.java
@@ -0,0 +1,37 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.autofill.keyboard_accessory;
+
+import android.content.Context;
+import android.support.annotation.Nullable;
+import android.support.v7.content.res.AppCompatResources;
+import android.support.v7.widget.RecyclerView;
+
+import org.chromium.chrome.R;
+
+/**
+ * This component is a tab that can be added to the {@link ManualFillingCoordinator}. This tab
+ * allows selecting credit card information from a sheet below the keyboard accessory.
+ */
+public class CreditCardAccessorySheetCoordinator extends AccessorySheetTabCoordinator {
+    /**
+     * Creates the credit cards tab.
+     * @param context The {@link Context} containing resources like icons and layouts for this tab.
+     * @param scrollListener An optional listener that will be bound to the inflated recycler view.
+     */
+    public CreditCardAccessorySheetCoordinator(
+            Context context, @Nullable RecyclerView.OnScrollListener scrollListener) {
+        super( // TODO(crbug.com/926365): Add an appropriate icon, and restructure this class to use
+               // an Icon Provider with a static instance of the resource.
+                AppCompatResources.getDrawable(context, R.drawable.ic_info_outline_grey),
+                // TODO(crbug.com/926365): Add strings and resources properly.
+                "Open credit card sheet", "Credit card sheet is open",
+                // TODO(crbug.com/926365): Add a new layout, or generalize the existing one.
+                R.layout.password_accessory_sheet, AccessoryTabType.CREDIT_CARDS, scrollListener);
+    }
+
+    @Override
+    public void onTabShown() {}
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
index 759bc02b..9d2a0d5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMetricsRecorder.java
@@ -278,6 +278,9 @@
      */
     static void recordSheetTrigger(
             @AccessoryTabType int tabType, @AccessorySheetTrigger int bucket) {
+        // TODO(crbug.com/926372): Add metrics capabilities for credit cards.
+        if (tabType == AccessoryTabType.CREDIT_CARDS) return;
+
         RecordHistogram.recordEnumeratedHistogram(
                 getHistogramForType(UMA_KEYBOARD_ACCESSORY_SHEET_TRIGGERED, tabType), bucket,
                 AccessorySheetTrigger.COUNT);
@@ -301,6 +304,9 @@
 
     static void recordSuggestionSelected(
             @AccessoryTabType int tabType, @AccessorySuggestionType int bucket) {
+        // TODO(crbug.com/926372): Add metrics capabilities for credit cards.
+        if (tabType == AccessoryTabType.CREDIT_CARDS) return;
+
         RecordHistogram.recordEnumeratedHistogram(
                 getHistogramForType(
                         UMA_KEYBOARD_ACCESSORY_SHEET_SUGGESTION_SELECTED, AccessoryTabType.ALL),
@@ -319,6 +325,9 @@
      */
     static void recordSheetSuggestions(
             @AccessoryTabType int tabType, ListModel<AccessorySheetDataPiece> suggestionList) {
+        // TODO(crbug.com/926372): Add metrics capabilities for credit cards.
+        if (tabType == AccessoryTabType.CREDIT_CARDS) return;
+
         int interactiveSuggestions = 0;
         for (int i = 0; i < suggestionList.size(); ++i) {
             if (getType(suggestionList.get(i)) == PASSWORD_INFO) {
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 ade8934e..aab3ba19 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
@@ -32,6 +32,7 @@
         mActivity = (ChromeActivity) windowAndroid.getActivity().get();
         mManualFillingCoordinator = mActivity.getManualFillingController();
         mManualFillingCoordinator.registerPasswordProvider(mSheetDataProvider);
+        mManualFillingCoordinator.registerCreditCardProvider();
         mManualFillingCoordinator.registerActionProvider(mActionProvider);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
index a352ddd..69071cb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingCoordinator.java
@@ -105,6 +105,10 @@
         mMediator.registerPasswordProvider(sheetDataProvider);
     }
 
+    void registerCreditCardProvider() {
+        mMediator.registerCreditCardProvider();
+    }
+
     public void showWhenKeyboardIsVisible() {
         mMediator.showWhenKeyboardIsVisible();
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
index f7eb73e..83b64f3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/ManualFillingMediator.java
@@ -104,6 +104,8 @@
         ActionProviderCacheAdapter mActionsProvider;
         @Nullable
         PasswordAccessorySheetCoordinator mPasswordAccessorySheet;
+        @Nullable
+        CreditCardAccessorySheetCoordinator mCreditCardAccessorySheet;
     }
 
     // TODO(fhorschig): Do we need a MapObservable type? (This would be only observer though).
@@ -227,6 +229,11 @@
         accessorySheet.registerDataProvider(dataProvider);
     }
 
+    void registerCreditCardProvider() {
+        CreditCardAccessorySheetCoordinator accessorySheet = getCreditCardAccessorySheet();
+        if (accessorySheet == null) return;
+    }
+
     void registerActionProvider(KeyboardAccessoryData.PropertyProvider<Action[]> actionProvider) {
         if (!isInitialized()) return;
         if (mActiveBrowserTab == null) return;
@@ -431,6 +438,9 @@
         if (state.mPasswordAccessorySheet != null) {
             addTab(state.mPasswordAccessorySheet.getTab());
         }
+        if (state.mCreditCardAccessorySheet != null) {
+            addTab(state.mCreditCardAccessorySheet.getTab());
+        }
         if (state.mActionsProvider != null) state.mActionsProvider.notifyAboutCachedItems();
     }
 
@@ -482,6 +492,27 @@
     }
 
     @VisibleForTesting
+    @Nullable
+    CreditCardAccessorySheetCoordinator getCreditCardAccessorySheet() {
+        if (!isInitialized()) return null;
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.AUTOFILL_MANUAL_FALLBACK_ANDROID)) {
+            return null;
+        }
+        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.EXPERIMENTAL_UI)
+                && !ChromeFeatureList.isEnabled(ChromeFeatureList.PASSWORDS_KEYBOARD_ACCESSORY)) {
+            return null;
+        }
+        if (mActiveBrowserTab == null) return null; // No need for a sheet if there is no tab.
+        AccessoryState state = getOrCreateAccessoryState(mActiveBrowserTab);
+        if (state.mCreditCardAccessorySheet == null) {
+            state.mCreditCardAccessorySheet = new CreditCardAccessorySheetCoordinator(
+                    mActivity, mAccessorySheet.getScrollListener());
+            addTab(state.mCreditCardAccessorySheet.getTab());
+        }
+        return state.mCreditCardAccessorySheet;
+    }
+
+    @VisibleForTesting
     void setInsetObserverViewSupplier(Supplier<InsetObserverView> insetObserverViewSupplier) {
         mInsetObserverViewSupplier = insetObserverViewSupplier;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java
index 615afec55..572e01c0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/PasswordAccessorySheetViewBinder.java
@@ -4,14 +4,18 @@
 
 package org.chromium.chrome.browser.autofill.keyboard_accessory;
 
+import static org.chromium.ui.base.LocalizationUtils.isLayoutRtl;
+
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.Nullable;
+import android.support.v4.view.ViewCompat;
 import android.support.v7.content.res.AppCompatResources;
 import android.support.v7.widget.RecyclerView;
 import android.text.method.PasswordTransformationMethod;
+import android.view.Gravity;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -104,14 +108,17 @@
                 info.getFaviconProvider().fetchFavicon(
                         mIconSize, icon -> setIconForBitmap(username, icon));
             }
-            username.setPadding(mPadding, 0, mPadding, 0);
+            ViewCompat.setPaddingRelative(username, mPadding, 0, mPadding, 0);
             // Passwords have no icon, so increase the offset.
-            password.setPadding(2 * mPadding + mIconSize, 0, mPadding, 0);
+            ViewCompat.setPaddingRelative(password, 2 * mPadding + mIconSize, 0, mPadding, 0);
         }
 
         private void bindTextView(TextView text, KeyboardAccessoryData.UserInfo.Field field) {
             text.setTransformationMethod(
                     field.isObfuscated() ? new PasswordTransformationMethod() : null);
+            // With transformation, the character set forces a LTR gravity. Therefore, invert it:
+            text.setGravity(Gravity.CENTER_VERTICAL
+                    | (isLayoutRtl() && field.isObfuscated() ? Gravity.END : Gravity.START));
             text.setText(field.getDisplayText());
             text.setContentDescription(field.getA11yDescription());
             text.setOnClickListener(!field.isSelectable() ? null : src -> field.triggerSelection());
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
index 4af9c65c..e0af559 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/AssistantCoordinator.java
@@ -111,7 +111,7 @@
         mBottomBarCoordinator.expand();
 
         // Hide everything except header.
-        mModel.getOverlayModel().set(AssistantOverlayModel.STATE, AssistantOverlayState.hidden());
+        mModel.getOverlayModel().set(AssistantOverlayModel.STATE, AssistantOverlayState.HIDDEN);
         mModel.getDetailsModel().clearDetails();
         mModel.getPaymentRequestModel().set(AssistantPaymentRequestModel.OPTIONS, null);
         mModel.getCarouselModel().clearChips();
@@ -142,7 +142,7 @@
         mModel.getHeaderModel().set(AssistantHeaderModel.CLOSE_VISIBLE, false);
 
         // Show overlay to prevent user from interacting with the page during onboarding.
-        mModel.getOverlayModel().set(AssistantOverlayModel.STATE, AssistantOverlayState.full());
+        mModel.getOverlayModel().set(AssistantOverlayModel.STATE, AssistantOverlayState.FULL);
 
         // Disable swiping for the onboarding because it interferes with letting the user scroll
         // the onboarding contents.
@@ -161,7 +161,7 @@
 
                     // Hide overlay.
                     mModel.getOverlayModel().set(
-                            AssistantOverlayModel.STATE, AssistantOverlayState.hidden());
+                            AssistantOverlayModel.STATE, AssistantOverlayState.HIDDEN);
 
                     onAccept.run();
                 });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
index 965d276..5e20763 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AnimatedProgressBar.java
@@ -49,35 +49,37 @@
     }
 
     /**
-     * Set the progress to {@code progress} if it is higher than the current progress, or do nothing
-     * if it is not (hence it is OK to call this method with the same value multiple times).
+     * Set the progress to {@code progress}. The transition to the new progress value is
+     * animated.
      */
-    public void maybeIncreaseProgress(int progress) {
-        if (progress > mLastProgress) {
-            ValueAnimator progressAnimation = ValueAnimator.ofInt(mLastProgress, progress);
-            progressAnimation.setDuration(PROGRESS_BAR_SPEED_MS * (progress - mLastProgress) / 100);
-            progressAnimation.setInterpolator(CompositorAnimator.ACCELERATE_INTERPOLATOR);
-            progressAnimation.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (mPendingIncreaseAnimations.isEmpty()) {
-                        mIsRunningProgressAnimation = false;
-                    } else {
-                        mIsRunningProgressAnimation = true;
-                        mPendingIncreaseAnimations.poll().start();
-                    }
+    public void setProgress(int progress) {
+        if (progress == mLastProgress) {
+            return;
+        }
+        ValueAnimator progressAnimation = ValueAnimator.ofInt(mLastProgress, progress);
+        progressAnimation.setDuration(
+                PROGRESS_BAR_SPEED_MS * Math.abs(progress - mLastProgress) / 100);
+        progressAnimation.setInterpolator(CompositorAnimator.ACCELERATE_INTERPOLATOR);
+        progressAnimation.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (mPendingIncreaseAnimations.isEmpty()) {
+                    mIsRunningProgressAnimation = false;
+                } else {
+                    mIsRunningProgressAnimation = true;
+                    mPendingIncreaseAnimations.poll().start();
                 }
-            });
-            progressAnimation.addUpdateListener(
-                    animation -> mProgressBar.setProgress((int) animation.getAnimatedValue()));
-            mLastProgress = progress;
-
-            if (mIsRunningProgressAnimation) {
-                mPendingIncreaseAnimations.offer(progressAnimation);
-            } else {
-                mIsRunningProgressAnimation = true;
-                progressAnimation.start();
             }
+        });
+        progressAnimation.addUpdateListener(
+                animation -> mProgressBar.setProgress((int) animation.getAnimatedValue()));
+        mLastProgress = progress;
+
+        if (mIsRunningProgressAnimation) {
+            mPendingIncreaseAnimations.offer(progressAnimation);
+        } else {
+            mIsRunningProgressAnimation = true;
+            progressAnimation.start();
         }
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
index 75aedf63..b1549b7c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/header/AssistantHeaderViewBinder.java
@@ -52,7 +52,7 @@
             view.mCloseButton.setVisibility(
                     model.get(AssistantHeaderModel.CLOSE_VISIBLE) ? View.VISIBLE : View.GONE);
         } else if (AssistantHeaderModel.PROGRESS == propertyKey) {
-            view.mProgressBar.maybeIncreaseProgress(model.get(AssistantHeaderModel.PROGRESS));
+            view.mProgressBar.setProgress(model.get(AssistantHeaderModel.PROGRESS));
         } else if (AssistantHeaderModel.PROGRESS_PULSING == propertyKey) {
             if (model.get(AssistantHeaderModel.PROGRESS_PULSING)) {
                 view.mProgressBar.enablePulsing();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java
index a65d624..2496af2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java
@@ -29,8 +29,9 @@
         // TODO(crbug.com/806868): Bind model to view through a ViewBinder instead.
         model.addObserver((source, propertyKey) -> {
             if (AssistantOverlayModel.STATE == propertyKey) {
-                AssistantOverlayState newState = model.get(AssistantOverlayModel.STATE);
-                setState(newState != null ? newState : AssistantOverlayState.hidden());
+                setState(model.get(AssistantOverlayModel.STATE));
+            } else if (AssistantOverlayModel.TOUCHABLE_AREA == propertyKey) {
+                mTouchEventFilter.setTouchableArea(model.get(AssistantOverlayModel.TOUCHABLE_AREA));
             } else if (AssistantOverlayModel.DELEGATE == propertyKey) {
                 mTouchEventFilter.setDelegate(model.get(AssistantOverlayModel.DELEGATE));
             }
@@ -52,12 +53,12 @@
     /**
      * Set the overlay state.
      */
-    private void setState(AssistantOverlayState state) {
-        if (state.isFull() && !mActivity.isViewObscuringAllTabs()) {
+    private void setState(@AssistantOverlayState int state) {
+        if (state == AssistantOverlayState.FULL && !mActivity.isViewObscuringAllTabs()) {
             mActivity.addViewObscuringAllTabs(mTouchEventFilter);
         }
 
-        if (!state.isFull() && mActivity.isViewObscuringAllTabs()) {
+        if (state != AssistantOverlayState.FULL && mActivity.isViewObscuringAllTabs()) {
             mActivity.removeViewObscuringAllTabs(mTouchEventFilter);
         }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java
index a70c5b8..c47824c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java
@@ -18,34 +18,31 @@
  */
 @JNINamespace("autofill_assistant")
 public class AssistantOverlayModel extends PropertyModel {
-    public static final WritableObjectPropertyKey<AssistantOverlayState> STATE =
+    public static final WritableIntPropertyKey STATE = new WritableIntPropertyKey();
+
+    public static final WritableObjectPropertyKey<List<RectF>> TOUCHABLE_AREA =
             new WritableObjectPropertyKey<>();
 
     public static final WritableObjectPropertyKey<AssistantOverlayDelegate> DELEGATE =
             new WritableObjectPropertyKey<>();
 
     public AssistantOverlayModel() {
-        super(STATE, DELEGATE);
+        super(STATE, TOUCHABLE_AREA, DELEGATE);
     }
 
     @CalledByNative
-    private void setHidden() {
-        set(STATE, AssistantOverlayState.hidden());
+    private void setState(@AssistantOverlayState int state) {
+        set(STATE, state);
     }
 
     @CalledByNative
-    private void setFull() {
-        set(STATE, AssistantOverlayState.full());
-    }
-
-    @CalledByNative
-    private void setPartial(float[] coords) {
+    private void setTouchableArea(float[] coords) {
         List<RectF> boxes = new ArrayList<>();
         for (int i = 0; i < coords.length; i += 4) {
             boxes.add(new RectF(/* left= */ coords[i], /* top= */ coords[i + 1],
                     /* right= */ coords[i + 2], /* bottom= */ coords[i + 3]));
         }
-        set(STATE, AssistantOverlayState.partial(boxes));
+        set(TOUCHABLE_AREA, boxes);
     }
 
     @CalledByNative
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayState.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayState.java
deleted file mode 100644
index 3cabaea..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayState.java
+++ /dev/null
@@ -1,93 +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.autofill_assistant.overlay;
-
-import android.graphics.RectF;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * A state class for the overlay.
- */
-public abstract class AssistantOverlayState {
-    private AssistantOverlayState() {}
-
-    boolean isHidden() {
-        return this instanceof Hidden;
-    }
-
-    boolean isFull() {
-        return this instanceof Full;
-    }
-
-    boolean isPartial() {
-        return this instanceof Partial;
-    }
-
-    List<RectF> boxes() {
-        if (this instanceof Partial) {
-            return ((Partial) this).mBoxes;
-        }
-        return Collections.emptyList();
-    }
-
-    /**
-     * Return an AssistantOverlayState representing a hidden overlay.
-     */
-    public static AssistantOverlayState hidden() {
-        return Hidden.INSTANCE;
-    }
-
-    /**
-     * Return an AssistantOverlayState representing a full overlay.
-     */
-    public static AssistantOverlayState full() {
-        return Full.INSTANCE;
-    }
-
-    /**
-     * Return an AssistantOverlayState representing a partial overlay.
-     */
-    public static AssistantOverlayState partial(List<RectF> boxes) {
-        return new Partial(boxes);
-    }
-
-    private static class Hidden extends AssistantOverlayState {
-        private static final AssistantOverlayState INSTANCE = new Hidden();
-
-        // Default equals method is correct as there can be only one instance of
-        // AssistantOverlayState.Hidden.
-    }
-
-    private static class Full extends AssistantOverlayState {
-        private static final AssistantOverlayState INSTANCE = new Full();
-
-        // Default equals method is correct as there can be only one instance of
-        // AssistantOverlayState.Full.
-    }
-
-    private static class Partial extends AssistantOverlayState {
-        private final List<RectF> mBoxes;
-
-        private Partial(List<RectF> boxes) {
-            mBoxes = boxes;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-
-            Partial partial = (Partial) o;
-
-            return mBoxes.equals(partial.mBoxes);
-        }
-    }
-}
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 4ada116..b3a5b2f 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
@@ -31,6 +31,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -63,20 +64,18 @@
 
     /** A mode that describes what's happening to the current gesture. */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({NO_GESTURE_MODE, TRACKING_GESTURE_MODE, FORWARDING_GESTURE_MODE})
-    private @interface GestureMode {}
-
-    /** There's no current gesture. */
-    private static final int NO_GESTURE_MODE = 0;
-
-    /**
-     * The current gesture is being tracked and buffered. The gesture might later on transition to
-     * forwarding mode or it might be abandoned.
-     */
-    private static final int TRACKING_GESTURE_MODE = 1;
-
-    /** The current gesture is being forwarded to the content view. */
-    private static final int FORWARDING_GESTURE_MODE = 2;
+    @IntDef({GestureMode.NONE, GestureMode.TRACKING, GestureMode.FORWARDING})
+    private @interface GestureMode {
+        /** There's no current gesture. */
+        int NONE = 0;
+        /**
+         * The current gesture is being tracked and buffered. The gesture might later on transition
+         * to forwarding mode or it might be abandoned.
+         */
+        int TRACKING = 1;
+        /** The current gesture is being forwarded to the content view. */
+        int FORWARDING = 2;
+    }
 
     private AssistantOverlayDelegate mDelegate;
     private ChromeFullscreenManager mFullscreenManager;
@@ -85,7 +84,10 @@
     private final Paint mGrayOut;
     private final Paint mClear;
 
-    private AssistantOverlayState mCurrentState = AssistantOverlayState.hidden();
+    @AssistantOverlayState
+    private int mCurrentState = AssistantOverlayState.HIDDEN;
+
+    private List<RectF> mTouchableArea = Collections.emptyList();
 
     /** Padding added between the element area and the grayed-out area. */
     private final float mPaddingPx;
@@ -244,34 +246,30 @@
     /**
      * Set the current state of the overlay.
      */
-    public void setState(AssistantOverlayState newState) {
-        if (newState.equals(mCurrentState)) {
-            return;
-        }
-
-        // Partial overlay has precedence over full overlay.
-        // TODO(crbug.com/806868): Remove this precedence by making sure we don't set a full overlay
-        // when there is a partial overlay already. This class shouldn't dictate whether it accepts
-        // its state or not.
-        if (mCurrentState.isPartial() && newState.isFull()) {
-            return;
-        }
-
+    public void setState(@AssistantOverlayState int newState) {
         mCurrentState = newState;
 
         // Reset tap counter each time we hide the overlay.
-        if (newState.isHidden()) {
+        if (mCurrentState == AssistantOverlayState.HIDDEN) {
             mUnexpectedTapTimes.clear();
         }
 
-        if (newState.isPartial()) {
-            clearOffsets();
-        }
-
         updateVisibility();
         invalidate();
     }
 
+    /**
+     * 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();
+        }
+    }
+
     private void updateVisibility() {
         if (AccessibilityUtil.isAccessibilityEnabled()) {
             // Touch exploration is fully disabled if there's an overlay in front. In this case, the
@@ -280,10 +278,10 @@
             //
             // TODO(crbug.com/806868): filter elements available to touch exploration, when it
             // is enabled.
-            setVisibility(!mCurrentState.isFull() ? View.GONE : View.VISIBLE);
+            setVisibility(mCurrentState != AssistantOverlayState.FULL ? View.GONE : View.VISIBLE);
         }
 
-        setAlpha(mCurrentState.isHidden() ? 0.0f : 1.0f);
+        setAlpha(mCurrentState == AssistantOverlayState.HIDDEN ? 0.0f : 1.0f);
     }
 
     private void clearOffsets() {
@@ -300,9 +298,14 @@
         }
 
         // Note that partial overlays have precedence over full overlays
-        if (mCurrentState.isPartial()) return dispatchTouchEventWithPartialOverlay(event);
-        if (mCurrentState.isFull()) return dispatchTouchEventWithFullOverlay(event);
-        return dispatchTouchEventWithNoOverlay();
+        switch (mCurrentState) {
+            case AssistantOverlayState.PARTIAL:
+                return dispatchTouchEventWithPartialOverlay(event);
+            case AssistantOverlayState.FULL:
+                return dispatchTouchEventWithFullOverlay(event);
+            default:
+                return dispatchTouchEventWithNoOverlay();
+        }
     }
 
     private boolean dispatchTouchEventWithNoOverlay() {
@@ -354,7 +357,7 @@
 
                 // Track the gesture in case this is a tap, which we should handle, or a
                 // scroll/fling/pinch, which we should forward.
-                mCurrentGestureMode = TRACKING_GESTURE_MODE;
+                mCurrentGestureMode = GestureMode.TRACKING;
                 mCurrentGestureBuffer.add(MotionEvent.obtain(event));
                 mScrollDetector.onTouchEvent(event);
                 mTapDetector.onTouchEvent(event);
@@ -362,7 +365,7 @@
 
             case MotionEvent.ACTION_MOVE: // Continues a gesture.
                 switch (mCurrentGestureMode) {
-                    case TRACKING_GESTURE_MODE:
+                    case GestureMode.TRACKING:
                         if (mScrollDetector.onTouchEvent(event)) {
                             // The current gesture is a scroll or a fling. Forward it.
                             startForwardingGesture(event);
@@ -374,7 +377,7 @@
                         mCurrentGestureBuffer.add(MotionEvent.obtain(event));
                         return true;
 
-                    case FORWARDING_GESTURE_MODE:
+                    case GestureMode.FORWARDING:
                         mCompositorView.dispatchTouchEvent(event);
                         return true;
 
@@ -385,12 +388,12 @@
             case MotionEvent.ACTION_POINTER_DOWN: // Continues a multi-touch gesture
             case MotionEvent.ACTION_POINTER_UP:
                 switch (mCurrentGestureMode) {
-                    case TRACKING_GESTURE_MODE:
+                    case GestureMode.TRACKING:
                         // The current gesture has just become a multi-touch gesture. Forward it.
                         startForwardingGesture(event);
                         return true;
 
-                    case FORWARDING_GESTURE_MODE:
+                    case GestureMode.FORWARDING:
                         mCompositorView.dispatchTouchEvent(event);
                         return true;
 
@@ -401,14 +404,14 @@
             case MotionEvent.ACTION_UP: // Ends a gesture
             case MotionEvent.ACTION_CANCEL:
                 switch (mCurrentGestureMode) {
-                    case TRACKING_GESTURE_MODE:
+                    case GestureMode.TRACKING:
                         if (mTapDetector.onTouchEvent(event)) {
                             onUnexpectedTap(event);
                         }
                         resetCurrentGesture();
                         return true;
 
-                    case FORWARDING_GESTURE_MODE:
+                    case GestureMode.FORWARDING:
                         mCompositorView.dispatchTouchEvent(event);
                         resetCurrentGesture();
                         return true;
@@ -424,7 +427,7 @@
 
     /** Clears all information about the current gesture. */
     private void resetCurrentGesture() {
-        mCurrentGestureMode = NO_GESTURE_MODE;
+        mCurrentGestureMode = GestureMode.NONE;
         cleanupCurrentGestureBuffer();
     }
 
@@ -438,7 +441,7 @@
 
     /** Enables forwarding of the current gesture, starting with {@link currentEvent}. */
     private void startForwardingGesture(MotionEvent currentEvent) {
-        mCurrentGestureMode = FORWARDING_GESTURE_MODE;
+        mCurrentGestureMode = GestureMode.FORWARDING;
         for (MotionEvent event : mCurrentGestureBuffer) {
             mCompositorView.dispatchTouchEvent(event);
         }
@@ -468,7 +471,7 @@
     @SuppressLint("CanvasSize")
     protected void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        if (mCurrentState.isHidden()) {
+        if (mCurrentState == AssistantOverlayState.HIDDEN) {
             return;
         }
         canvas.drawPaint(mGrayOut);
@@ -483,8 +486,12 @@
             canvas.drawRect(0, yBottom, width, canvas.getHeight(), mClear);
         }
 
+        if (mCurrentState != AssistantOverlayState.PARTIAL) {
+            return;
+        }
+
         int height = yBottom - yTop;
-        for (RectF rect : mCurrentState.boxes()) {
+        for (RectF rect : mTouchableArea) {
             mDrawRect.left = rect.left * width - mPaddingPx;
             mDrawRect.top =
                     yTop + rect.top * height - mPaddingPx - mBrowserScrollOffsetY - mOffsetY;
@@ -584,7 +591,7 @@
     }
 
     private boolean isInTouchableArea(float x, float y) {
-        for (RectF rect : mCurrentState.boxes()) {
+        for (RectF rect : mTouchableArea) {
             if (rect.contains(x, y, x, y)) {
                 return true;
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProvider.java
index d155a19..06afa52 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarkswidget/BookmarkWidgetProvider.java
@@ -10,9 +10,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.widget.RemoteViews;
 
@@ -82,11 +80,7 @@
      * Refreshes all Chrome Bookmark widgets.
      */
     public static void refreshAllWidgets(Context context) {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
-                && !context.getPackageManager().hasSystemFeature(
-                           PackageManager.FEATURE_APP_WIDGETS)) {
-            return;
-        }
+        if (AppWidgetManager.getInstance(context) == null) return;
 
         context.sendBroadcast(new Intent(
                 getBookmarkAppWidgetUpdateAction(context),
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
index ce16837..4f14d44c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/contextualsearch/ContextualSearchPanel.java
@@ -15,7 +15,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.compositor.LayerTitleCache;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel;
-import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.PanelProgressObserver;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelContent;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager;
 import org.chromium.chrome.browser.compositor.bottombar.OverlayPanelManager.PanelPriority;
@@ -25,7 +24,6 @@
 import org.chromium.chrome.browser.compositor.scene_layer.SceneOverlayLayer;
 import org.chromium.chrome.browser.contextualsearch.ContextualSearchManagementDelegate;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.util.MathUtils;
 import org.chromium.chrome.browser.widget.ScrimView;
 import org.chromium.chrome.browser.widget.ScrimView.ScrimParams;
@@ -108,8 +106,8 @@
 
     @Override
     protected void initializeUiState() {
-        mUseGenericSheetUx = mActivity.supportsContextualSuggestionsBottomSheet()
-                && FeatureUtilities.areContextualSuggestionsEnabled(mActivity);
+        mUseGenericSheetUx = false;
+        // TODO(crbug.com/831783): Clean up this code.
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
index f3b821a..7ef1b3c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerAdapter.java
@@ -215,7 +215,7 @@
     @Override
     public int getItemCount() {
         if (mSearchResults != null) return mSearchResults.size();
-        if (mContactDetails == null) return 0;
+        if (mContactDetails == null || mContactDetails.size() == 0) return 0;
         // Add one entry to account for the Select All checkbox, when not searching.
         return mContactDetails.size() + (mSearchMode ? 0 : 1);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
index 61b6105..961f37c 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contacts_picker/PickerCategoryView.java
@@ -7,6 +7,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.res.Resources;
+import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.v7.widget.LinearLayoutManager;
 import android.support.v7.widget.RecyclerView;
 import android.util.JsonWriter;
@@ -136,6 +137,11 @@
         View root = LayoutInflater.from(context).inflate(R.layout.contacts_picker_dialog, this);
         mSelectableListLayout =
                 (SelectableListLayout<ContactDetails>) root.findViewById(R.id.selectable_list);
+        mSelectableListLayout.initializeEmptyView(
+                VectorDrawableCompat.create(
+                        mActivity.getResources(), R.drawable.contacts_big, mActivity.getTheme()),
+                R.string.contacts_picker_no_contacts_found,
+                R.string.contacts_picker_no_contacts_found);
 
         mPickerAdapter = new PickerAdapter(this, context.getContentResolver(), formattedOrigin);
         mRecyclerView = mSelectableListLayout.initializeRecyclerView(mPickerAdapter);
@@ -237,7 +243,7 @@
         }
 
         boolean allSelected = selectedItems.size() == mPickerAdapter.getItemCount() - 1;
-        mTopView.updateSelectAllCheckbox(allSelected);
+        if (mTopView != null) mTopView.updateSelectAllCheckbox(allSelected);
     }
 
     // RecyclerView.RecyclerListener:
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java
index 4e2f0a22..e3a8449 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/crash/MinidumpUploadService.java
@@ -24,6 +24,7 @@
 import org.chromium.components.background_task_scheduler.TaskIds;
 import org.chromium.components.minidump_uploader.CrashFileManager;
 import org.chromium.components.minidump_uploader.MinidumpUploadCallable;
+import org.chromium.components.minidump_uploader.MinidumpUploadCallable.MinidumpUploadStatus;
 import org.chromium.components.minidump_uploader.MinidumpUploadJobService;
 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionManager;
 
@@ -199,13 +200,13 @@
         // Try to upload minidump
         MinidumpUploadCallable minidumpUploadCallable =
                 createMinidumpUploadCallable(minidumpFile, logfile);
-        @MinidumpUploadCallable.MinidumpUploadStatus int uploadStatus =
-                minidumpUploadCallable.call();
+        @MinidumpUploadStatus
+        int uploadStatus = minidumpUploadCallable.call();
 
-        if (uploadStatus == MinidumpUploadCallable.UPLOAD_SUCCESS) {
+        if (uploadStatus == MinidumpUploadStatus.SUCCESS) {
             // Only update UMA stats if an intended and successful upload.
             incrementCrashSuccessUploadCount(minidumpFileName);
-        } else if (uploadStatus == MinidumpUploadCallable.UPLOAD_FAILURE) {
+        } else if (uploadStatus == MinidumpUploadStatus.FAILURE) {
             // Unable to upload minidump. Incrementing try number and restarting.
             ++tries;
             if (tries == MAX_TRIES_ALLOWED) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityComponent.java b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityComponent.java
index 256fc347..c3ee20b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityComponent.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/dependency_injection/ChromeActivityComponent.java
@@ -6,6 +6,7 @@
 
 import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsCoordinator;
 import org.chromium.chrome.browser.contextual_suggestions.ContextualSuggestionsModule;
+import org.chromium.chrome.browser.tasks.tab_list_ui.TabGridCoordinator;
 
 import dagger.Subcomponent;
 
@@ -19,4 +20,5 @@
 
     // Temporary getters for DI migration process.
     ContextualSuggestionsCoordinator resolveContextualSuggestionsCoordinator();
+    TabGridCoordinator resolveTabGridCoordinator();
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DataReductionProxyFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DataReductionProxyFirstRunFragment.java
index 8c0a983..451ad00 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DataReductionProxyFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/DataReductionProxyFirstRunFragment.java
@@ -15,6 +15,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
+import org.chromium.chrome.browser.preferences.datareduction.DataReductionBrandingResourceProvider;
 import org.chromium.chrome.browser.preferences.datareduction.DataReductionPromoUtils;
 
 /**
@@ -32,7 +33,9 @@
     @Override
     public View onCreateView(
             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        return inflater.inflate(R.layout.fre_data_reduction_proxy, container, false);
+        return inflater.inflate(DataReductionBrandingResourceProvider.getFirstRunLayout(
+                                        R.layout.fre_data_reduction_proxy),
+                container, false);
     }
 
     @Override
@@ -49,9 +52,13 @@
                 DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
                         v.getContext(), enableDataSaverSwitch.isChecked());
                 if (enableDataSaverSwitch.isChecked()) {
-                    enableDataSaverSwitch.setText(R.string.data_reduction_enabled_switch);
+                    enableDataSaverSwitch.setText(
+                            DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                                    R.string.data_reduction_enabled_switch));
                 } else {
-                    enableDataSaverSwitch.setText(R.string.data_reduction_disabled_switch);
+                    enableDataSaverSwitch.setText(
+                            DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                                    R.string.data_reduction_disabled_switch));
                 }
             }
         });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBarDelegate.java
index 20a30673..c900f3e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBarDelegate.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/DataReductionPromoInfoBarDelegate.java
@@ -10,6 +10,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
+import org.chromium.chrome.browser.preferences.datareduction.DataReductionBrandingResourceProvider;
 import org.chromium.chrome.browser.preferences.datareduction.DataReductionProxyUma;
 import org.chromium.content_public.browser.WebContents;
 import org.chromium.ui.widget.Toast;
@@ -52,8 +53,11 @@
         DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
                 context, true);
         Toast.makeText(context,
-                context.getString(R.string.data_reduction_enabled_toast),
-                Toast.LENGTH_LONG).show();
+                     context.getString(
+                             DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                                     R.string.data_reduction_enabled_toast)),
+                     Toast.LENGTH_LONG)
+                .show();
     }
 
     /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsLitePageInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsLitePageInfoBar.java
index 3ae0d930..65c93ec8 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsLitePageInfoBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/PreviewsLitePageInfoBar.java
@@ -9,7 +9,7 @@
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.chrome.browser.ResourceId;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
-import org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferences;
+import org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferenceFragment;
 
 /**
  * An InfoBar that lets the user know that Data Saver Lite Mode now also applies to HTTPS pages.
@@ -34,6 +34,6 @@
         Bundle fragmentArgs = new Bundle();
         fragmentArgs.putBoolean(FROM_INFOBAR, true);
         PreferencesLauncher.launchSettingsPage(
-                getContext(), DataReductionPreferences.class, fragmentArgs);
+                getContext(), DataReductionPreferenceFragment.class, fragmentArgs);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
index 17602b22..ef31b2b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/init/ChromeBrowserInitializer.java
@@ -215,7 +215,6 @@
             new AsyncTask<Void>() {
                 @Override
                 protected Void doInBackground() {
-                    ContextUtils.getAppSharedPreferences();
                     DocumentTabModelImpl.warmUpSharedPrefs(mApplication);
                     ActivityAssigner.warmUpSharedPrefs(mApplication);
                     DownloadManagerService.warmUpSharedPrefs();
@@ -224,7 +223,6 @@
             }
                     .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
         } else {
-            ContextUtils.getAppSharedPreferences();
             DocumentTabModelImpl.warmUpSharedPrefs(mApplication);
             ActivityAssigner.warmUpSharedPrefs(mApplication);
             DownloadManagerService.warmUpSharedPrefs();
@@ -247,6 +245,7 @@
         DeviceUtils.addDeviceSpecificUserAgentSwitch();
         ApplicationStatus.registerStateListenerForAllActivities(
                 createActivityStateListener());
+        mApplication.initDefaultNightMode();
 
         mPreInflationStartupComplete = true;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java
index 13382d5..7590b260 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/modaldialog/AppModalPresenter.java
@@ -4,9 +4,11 @@
 
 package org.chromium.chrome.browser.modaldialog;
 
+import android.app.Activity;
 import android.app.Dialog;
-import android.content.Context;
+import android.support.v4.view.ViewCompat;
 import android.view.LayoutInflater;
+import android.view.Window;
 
 import org.chromium.chrome.R;
 import org.chromium.ui.modaldialog.DialogDismissalCause;
@@ -18,7 +20,7 @@
 
 /** The presenter that shows a {@link ModalDialogView} in an Android dialog. */
 public class AppModalPresenter extends ModalDialogManager.Presenter {
-    private final Context mContext;
+    private final Activity mActivity;
     private Dialog mDialog;
     private PropertyModelChangeProcessor<PropertyModel, ModalDialogView, PropertyKey>
             mModelChangeProcessor;
@@ -35,13 +37,25 @@
         }
     }
 
-    public AppModalPresenter(Context context) {
-        mContext = context;
+    /**
+     * @param activity The {@link Activity} on which dialog views will be created and shown.
+     */
+    public AppModalPresenter(Activity activity) {
+        mActivity = activity;
     }
 
     @Override
     protected void addDialogView(PropertyModel model) {
-        mDialog = new Dialog(mContext, R.style.ModalDialogTheme);
+        // If the activity's decor view is not attached to window, we don't show the dialog because
+        // the window manager might have revoked the window token for this activity. See
+        // https://crbug.com/926688.
+        Window window = mActivity.getWindow();
+        if (window == null || !ViewCompat.isAttachedToWindow(window.getDecorView())) {
+            dismissCurrentDialog(DialogDismissalCause.NOT_ATTACHED_TO_WINDOW);
+            return;
+        }
+
+        mDialog = new Dialog(mActivity, R.style.ModalDialogTheme);
         mDialog.setOnCancelListener(dialogInterface
                 -> dismissCurrentDialog(DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE));
         // Cancel on touch outside should be disabled by default. The ModelChangeProcessor wouldn't
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java
index 9f6b9a8..d7739ff 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java
@@ -13,14 +13,9 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import org.chromium.base.BuildInfo;
-import org.chromium.base.Log;
-import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
 import org.chromium.chrome.browser.identity.SettingsSecureBasedIdentificationGenerator;
 import org.chromium.chrome.browser.identity.UniqueIdentificationGeneratorFactory;
-import org.chromium.chrome.browser.init.ProcessInitializationHandler;
-import org.chromium.components.signin.AccountManagerFacade;
-import org.chromium.components.signin.ChromeSigninController;
 import org.chromium.ui.base.DeviceFormFactor;
 
 import java.io.IOException;
@@ -103,12 +98,6 @@
             serializer.attribute(null, "lang", getLanguage());
             serializer.attribute(null, "installage", String.valueOf(installAge));
             serializer.attribute(null, "ap", getAdditionalParameters());
-            // <code>_numaccounts</code> is actually number of profiles, which is always one for
-            // Chrome Android.
-            serializer.attribute(null, "_numaccounts", "1");
-            serializer.attribute(null, "_numgoogleaccountsondevice",
-                    String.valueOf(getNumGoogleAccountsOnDevice()));
-            serializer.attribute(null, "_numsignedin", String.valueOf(getNumSignedIn()));
             serializer.attribute(
                     null, "_dl_mgr_disabled", String.valueOf(getDownloadManagerState()));
 
@@ -188,43 +177,6 @@
     }
 
     /**
-     * Returns the number of accounts on the device, bucketed into:
-     * 0 accounts, 1 account, or 2+ accounts.
-     *
-     * @return Number of accounts on the device, bucketed as above.
-     */
-    @VisibleForTesting
-    public int getNumGoogleAccountsOnDevice() {
-        // RequestGenerator may be invoked from JobService or AlarmManager (through OmahaService),
-        // so have to make sure AccountManagerFacade instance is initialized.
-        ThreadUtils.runOnUiThreadBlocking(
-                () -> ProcessInitializationHandler.getInstance().initializePreNative());
-        int numAccounts = 0;
-        try {
-            numAccounts = AccountManagerFacade.get().getGoogleAccounts().size();
-        } catch (Exception e) {
-            Log.e(TAG, "Can't get number of accounts.", e);
-        }
-        switch (numAccounts) {
-            case 0:
-                return 0;
-            case 1:
-                return 1;
-            default:
-                return 2;
-        }
-    }
-
-    /**
-     * Determine number of accounts signed in.
-     */
-    @VisibleForTesting
-    public int getNumSignedIn() {
-        // We only have a single account.
-        return ChromeSigninController.get().isSignedIn() ? 1 : 0;
-    }
-
-    /**
      * Returns DownloadManager system service enabled state as
      * -1 - manager state unknown
      *  0 - manager enabled
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index be2bed7..3d805d2 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -42,6 +42,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.TimeUnit;
 
 /**
  * The URL text entry view for the Omnibox.
@@ -58,6 +59,25 @@
     private static final CachedMetrics.ActionEvent ACTION_LONG_PRESS_SHARE =
             new CachedMetrics.ActionEvent("Omnibox.LongPress.Share");
 
+    private static final CachedMetrics.TimesHistogramSample TIME_UNTIL_COPY =
+            new CachedMetrics.TimesHistogramSample(
+                    "Omnibox.TimeUntilFirst.Copy", TimeUnit.MILLISECONDS);
+    private static final CachedMetrics.TimesHistogramSample TIME_UNTIL_CUT =
+            new CachedMetrics.TimesHistogramSample(
+                    "Omnibox.TimeUntilFirst.Cut", TimeUnit.MILLISECONDS);
+    private static final CachedMetrics.TimesHistogramSample TIME_UNTIL_SHARE =
+            new CachedMetrics.TimesHistogramSample(
+                    "Omnibox.TimeUntilFirst.Share", TimeUnit.MILLISECONDS);
+
+    @IntDef({OmniboxAction.CUT, OmniboxAction.COPY, OmniboxAction.SHARE})
+    @Retention(RetentionPolicy.SOURCE)
+    /** Actions that can be taken from the omnibox. */
+    public @interface OmniboxAction {
+        int CUT = 0;
+        int COPY = 1;
+        int SHARE = 2;
+    }
+
     // TODO(tedchoc): Replace with EditorInfoCompat#IME_FLAG_NO_PERSONALIZED_LEARNING or
     //                EditorInfo#IME_FLAG_NO_PERSONALIZED_LEARNING as soon as either is available in
     //                all build config types.
@@ -68,6 +88,12 @@
     private static final int MAX_DISPLAYABLE_LENGTH = 4000;
     private static final int MAX_DISPLAYABLE_LENGTH_LOW_END = 1000;
 
+    /** The last time that the omnibox was focused. */
+    private long mLastOmniboxFocusTime;
+
+    /** Whether a timing event should be recorded. This will be true once per omnibox focus. */
+    private boolean mShouldRecordTimingEvent;
+
     private boolean mFirstDrawComplete;
 
     /**
@@ -264,6 +290,30 @@
     }
 
     /**
+     * Record than an action occurred in the omnibox.
+     * @param actionTaken The action taken that triggered the recording.
+     * @param lastOmniboxFocusTime The time that the last omnibox focus event occurred.
+     */
+    public static void recordTimedActionForMetrics(
+            @OmniboxAction int actionTaken, long lastOmniboxFocusTime) {
+        final long finalTime = System.currentTimeMillis() - lastOmniboxFocusTime;
+        assert finalTime >= 0;
+        switch (actionTaken) {
+            case OmniboxAction.COPY:
+                TIME_UNTIL_COPY.record(finalTime);
+                break;
+            case OmniboxAction.CUT:
+                TIME_UNTIL_CUT.record(finalTime);
+                break;
+            case OmniboxAction.SHARE:
+                TIME_UNTIL_SHARE.record(finalTime);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
      * Initialize the delegate that allows interaction with the Window.
      */
     public void setWindowDelegate(WindowDelegate windowDelegate) {
@@ -304,7 +354,9 @@
 
         if (focused) {
             mPendingScroll = false;
+            mLastOmniboxFocusTime = System.currentTimeMillis();
         }
+        mShouldRecordTimingEvent = focused;
 
         fixupTextDirection();
     }
@@ -577,6 +629,12 @@
             } else {
                 ACTION_LONG_PRESS_COPY.record();
             }
+            if (mShouldRecordTimingEvent) {
+                recordTimedActionForMetrics(
+                        id == android.R.id.copy ? OmniboxAction.COPY : OmniboxAction.CUT,
+                        mLastOmniboxFocusTime);
+                mShouldRecordTimingEvent = false;
+            }
             String currentText = getText().toString();
             String replacementCutCopyText = mTextContextMenuDelegate.getReplacementCutCopyText(
                     currentText, getSelectionStart(), getSelectionEnd());
@@ -604,6 +662,10 @@
 
         if (id == android.R.id.shareText) {
             ACTION_LONG_PRESS_SHARE.record();
+            if (mShouldRecordTimingEvent) {
+                recordTimedActionForMetrics(OmniboxAction.SHARE, mLastOmniboxFocusTime);
+                mShouldRecordTimingEvent = false;
+            }
         }
 
         return super.onTextContextMenuItem(id);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
index 1b0744d6..47b96a1b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusMediator.java
@@ -22,7 +22,6 @@
     private boolean mDarkTheme;
     private boolean mUrlHasFocus;
     private boolean mFirstSuggestionIsSearchQuery;
-    private boolean mVerboseStatusAllowed;
     private boolean mVerboseStatusSpaceAvailable;
     private boolean mPageIsPreview;
     private boolean mPageIsOffline;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
index e70f0f7a..4d99c95 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/status/StatusView.java
@@ -31,7 +31,6 @@
     private TextView mVerboseStatusTextView;
     private View mSeparatorView;
     private View mStatusExtraSpace;
-    private View mLocationBarButtonContainer;
 
     private boolean mAnimationsEnabled;
     private boolean mAnimatingStatusIconShow;
@@ -134,8 +133,13 @@
         // Action 3: Specify icon content. Use TransitionDrawable whenever object is visible.
         if (targetIcon != null) {
             if (!isIconHidden) {
-                TransitionDrawable newImage = new TransitionDrawable(
-                        new Drawable[] {mIconView.getDrawable(), targetIcon});
+                Drawable existingDrawable = mIconView.getDrawable();
+                if (existingDrawable instanceof TransitionDrawable
+                        && ((TransitionDrawable) existingDrawable).getNumberOfLayers() == 2) {
+                    existingDrawable = ((TransitionDrawable) existingDrawable).getDrawable(1);
+                }
+                TransitionDrawable newImage =
+                        new TransitionDrawable(new Drawable[] {existingDrawable, targetIcon});
 
                 mIconView.setImageDrawable(newImage);
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
index 94e27f7..64dedfc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/suggestions/editurl/EditUrlSuggestionProcessor.java
@@ -18,6 +18,8 @@
 import org.chromium.chrome.browser.ActivityTabProvider;
 import org.chromium.chrome.browser.ChromeFeatureList;
 import org.chromium.chrome.browser.omnibox.OmniboxSuggestionType;
+import org.chromium.chrome.browser.omnibox.UrlBar;
+import org.chromium.chrome.browser.omnibox.UrlBar.OmniboxAction;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinator.SuggestionProcessor;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestionUiType;
@@ -111,6 +113,12 @@
     /** The original title of the page. */
     private String mOriginalTitle;
 
+    /** The last time that the omnibox was focused. */
+    private long mLastOmniboxFocusTime;
+
+    /** Whether a timing event should be recorded. This will be true once per omnibox focus. */
+    private boolean mShouldRecordTimingEvent;
+
     /**
      * @param locationBarDelegate A means of modifying the location bar.
      * @param selectionHandler A mechanism for handling selection of the edit URL suggestion item.
@@ -216,13 +224,15 @@
 
     @Override
     public void onUrlFocusChange(boolean hasFocus) {
-        if (!hasFocus) {
+        if (hasFocus) {
+            mLastOmniboxFocusTime = System.currentTimeMillis();
+        } else {
             mOriginalUrl = null;
             mOriginalTitle = null;
             mHasClearedOmniboxForFocus = false;
             mLastProcessedSuggestion = null;
-            return;
         }
+        mShouldRecordTimingEvent = hasFocus;
     }
 
     @Override
@@ -233,10 +243,18 @@
         if (R.id.url_copy_icon == view.getId()) {
             ENUMERATED_SUGGESTION_ACTION.record(SuggestionAction.COPY);
             ACTION_EDIT_URL_SUGGESTION_COPY.record();
+            if (mShouldRecordTimingEvent) {
+                UrlBar.recordTimedActionForMetrics(OmniboxAction.COPY, mLastOmniboxFocusTime);
+                mShouldRecordTimingEvent = false;
+            }
             Clipboard.getInstance().copyUrlToClipboard(mLastProcessedSuggestion.getUrl());
         } else if (R.id.url_share_icon == view.getId()) {
             ENUMERATED_SUGGESTION_ACTION.record(SuggestionAction.SHARE);
             ACTION_EDIT_URL_SUGGESTION_SHARE.record();
+            if (mShouldRecordTimingEvent) {
+                UrlBar.recordTimedActionForMetrics(OmniboxAction.SHARE, mLastOmniboxFocusTime);
+                mShouldRecordTimingEvent = false;
+            }
             mLocationBarDelegate.clearOmniboxFocus();
             // TODO(mdjones): This should only share the displayed URL instead of the background
             //                tab.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
index db80fb76..fe79143 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -1670,7 +1670,11 @@
      * localhost and file:// scheme origins to verify its behavior.
      */
     private boolean shouldEnforceCanMakePaymentQueryQuota() {
-        return !OriginSecurityChecker.isOriginLocalhostOrFile(mWebContents.getLastCommittedUrl())
+        // If |mWebContents| is destroyed, don't bother checking the localhost or file:// scheme
+        // exemption. It doesn't really matter anyways.
+        return mWebContents.isDestroyed()
+                || !OriginSecurityChecker.isOriginLocalhostOrFile(
+                        mWebContents.getLastCommittedUrl())
                 || sIsLocalCanMakePaymentQueryQuotaEnforcedForTest;
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
index 66b5165..d6e06d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/MainPreferences.java
@@ -21,7 +21,7 @@
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.password_manager.ManagePasswordsReferrer;
 import org.chromium.chrome.browser.preferences.autofill_assistant.AutofillAssistantPreferences;
-import org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferences;
+import org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferenceFragment;
 import org.chromium.chrome.browser.preferences.developer.DeveloperPreferences;
 import org.chromium.chrome.browser.search_engines.TemplateUrl;
 import org.chromium.chrome.browser.search_engines.TemplateUrlService;
@@ -223,7 +223,7 @@
 
         ChromeBasePreference dataReduction =
                 (ChromeBasePreference) findPreference(PREF_DATA_REDUCTION);
-        dataReduction.setSummary(DataReductionPreferences.generateSummary(getResources()));
+        dataReduction.setSummary(DataReductionPreferenceFragment.generateSummary(getResources()));
     }
 
     private Preference addPreferenceIfAbsent(String key) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionBrandingResourceProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionBrandingResourceProvider.java
new file mode 100644
index 0000000..0542e87
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionBrandingResourceProvider.java
@@ -0,0 +1,113 @@
+// 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.preferences.datareduction;
+
+import android.support.annotation.LayoutRes;
+import android.support.annotation.StringRes;
+import android.support.annotation.XmlRes;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeFeatureList;
+
+/**
+ * This class provides Android resource IDs for the Data Saver/Lite Mode feature. This feature is
+ * being rebranded, but the rebrand is controlled by a feature flag.
+ *
+ * TODO(crbug.com/909915): Remove this class and callsites when fully rolled out.
+ */
+public final class DataReductionBrandingResourceProvider {
+    /**
+     * Given a DataSaver string resource, maybe return the string resource for Lite Mode rebranding
+     * if it is enabled. If not, the same string resource is returned.
+     *
+     * @param resource The string resource to check.
+     * @return The string resource to use with respect to the Lite Mode rebranding.
+     */
+    public static @StringRes int getDataSaverBrandedString(@StringRes int resource) {
+        if (shouldUseLiteMode()) {
+            return mapToLiteModeString(resource);
+        }
+        return resource;
+    }
+
+    /**
+     * Given a DataSaver first run layout resource, maybe return the first run layout
+     * resource for Lite Mode rebranding if it is enabled. If not, the same resource is returned.
+     *
+     * @param resource The layout resource to check.
+     * @return The resource to use with respect to the Lite Mode rebranding.
+     */
+    public static @LayoutRes int getFirstRunLayout(@LayoutRes int resource) {
+        if (shouldUseLiteMode()) {
+            return mapToLiteModeLayout(resource);
+        }
+        return resource;
+    }
+
+    /**
+     * Given a DataSaver preferences XML resource, maybe return the preferences XML
+     * resource for Lite Mode rebranding if it is enabled. If not, the same resource is returned
+     *
+     * @param resource The XML resource to check.
+     * @return The resource to use with respect to the Lite Mode rebranding.
+     */
+    public static @XmlRes int getPreferencesOffXml(@XmlRes int resource) {
+        if (shouldUseLiteMode()) {
+            return mapToLiteModeXml(resource);
+        }
+        return resource;
+    }
+
+    private static boolean shouldUseLiteMode() {
+        return ChromeFeatureList.isEnabled(ChromeFeatureList.DATA_SAVER_LITE_MODE_REBRANDING);
+    }
+
+    private static @StringRes int mapToLiteModeString(@StringRes int resource) {
+        // Enumerates all strings used by Data Saver or Lite Mode. Not all strings are changed in
+        // the Lite Mode rebrand, so some strings are returned as is.
+        if (resource == R.string.data_reduction_title) {
+            return R.string.data_reduction_title_lite_mode;
+        }
+        if (resource == R.string.data_reduction_usage_reset_statistics_confirmation_title) {
+            return R.string.data_reduction_usage_reset_statistics_confirmation_title_lite_mode;
+        }
+        if (resource == R.string.data_reduction_usage_reset_statistics_confirmation_dialog) {
+            return R.string.data_reduction_usage_reset_statistics_confirmation_dialog_lite_mode;
+        }
+        if (resource == R.string.data_reduction_promo_title) {
+            return R.string.data_reduction_promo_title_lite_mode;
+        }
+        if (resource == R.string.data_reduction_promo_summary) {
+            return R.string.data_reduction_promo_summary_lite_mode;
+        }
+        if (resource == R.string.data_reduction_enable_button) {
+            return R.string.data_reduction_enable_button_lite_mode;
+        }
+        if (resource == R.string.data_reduction_enabled_switch) {
+            return R.string.data_reduction_enabled_switch_lite_mode;
+        }
+        if (resource == R.string.data_reduction_disabled_switch) {
+            return R.string.data_reduction_disabled_switch_lite_mode;
+        }
+        if (resource == R.string.data_reduction_enabled_toast) {
+            return R.string.data_reduction_enabled_toast_lite_mode;
+        }
+        return resource;
+    }
+
+    private static @LayoutRes int mapToLiteModeLayout(@LayoutRes int resource) {
+        if (resource == R.layout.fre_data_reduction_proxy) {
+            return R.layout.fre_data_reduction_proxy_lite_mode;
+        }
+        return resource;
+    }
+
+    private static @XmlRes int mapToLiteModeXml(@XmlRes int resource) {
+        if (resource == R.xml.data_reduction_preferences_off) {
+            return R.xml.data_reduction_preferences_off_lite_mode;
+        }
+        return resource;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuItem.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuItem.java
index ef7b334..beb47d1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuItem.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuItem.java
@@ -46,6 +46,10 @@
 
         TextView itemText = (TextView) findViewById(R.id.menu_item_text);
         TextView itemSummary = (TextView) findViewById(R.id.menu_item_summary);
+        ImageView icon = (ImageView) findViewById(R.id.chart_icon);
+        icon.setContentDescription(getContext().getString(
+                DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                        R.string.data_reduction_title)));
 
         if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled()) {
             DataReductionProxyUma.dataReductionProxyUIAction(
@@ -76,7 +80,6 @@
             itemText.setTextColor(textColorLink);
 
             // Reset the icon to blue.
-            ImageView icon = (ImageView) findViewById(R.id.chart_icon);
             LayerDrawable layers = (LayerDrawable) icon.getDrawable();
             Drawable chart = layers.findDrawableByLayerId(R.id.main_menu_chart);
             chart.setColorFilter(null);
@@ -84,11 +87,11 @@
             DataReductionProxyUma.dataReductionProxyUIAction(
                     DataReductionProxyUma.ACTION_MAIN_MENU_DISPLAYED_OFF);
 
-            itemText.setText(R.string.data_reduction_title);
+            itemText.setText(DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                    R.string.data_reduction_title));
             itemSummary.setText(R.string.text_off);
 
             // Make the icon grey.
-            ImageView icon = (ImageView) findViewById(R.id.chart_icon);
             LayerDrawable layers = (LayerDrawable) icon.getDrawable();
             Drawable chart = layers.findDrawableByLayerId(R.id.main_menu_chart);
             ColorMatrix matrix = new ColorMatrix();
@@ -103,9 +106,9 @@
     public void onClick(View v) {
         RecordUserAction.record("MobileMenuDataSaverOpened");
         Bundle fragmentArgs = new Bundle();
-        fragmentArgs.putBoolean(DataReductionPreferences.FROM_MAIN_MENU, true);
+        fragmentArgs.putBoolean(DataReductionPreferenceFragment.FROM_MAIN_MENU, true);
         PreferencesLauncher.launchSettingsPage(
-                getContext(), DataReductionPreferences.class, fragmentArgs);
+                getContext(), DataReductionPreferenceFragment.class, fragmentArgs);
 
         Tracker tracker = TrackerFactory.getTrackerForProfile(Profile.getLastUsedProfile());
         tracker.notifyEvent(EventConstants.DATA_SAVER_DETAIL_OPENED);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreference.java
new file mode 100644
index 0000000..8dff384
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreference.java
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences.datareduction;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.preferences.ChromeBasePreference;
+
+/**
+ * Custom preference to enable programmatically overriding the Preference's title string.
+ */
+public class DataReductionPreference extends ChromeBasePreference {
+    /**
+     * Constructor for inflating from XML.
+     */
+    public DataReductionPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected View onCreateView(ViewGroup parent) {
+        View view = super.onCreateView(parent);
+
+        TextView iconView = (TextView) view.findViewById(android.R.id.title);
+        iconView.setText(getContext().getResources().getString(
+                DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                        R.string.data_reduction_title)));
+
+        return view;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferenceFragment.java
new file mode 100644
index 0000000..985ea6bb
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferenceFragment.java
@@ -0,0 +1,189 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.preferences.datareduction;
+
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceChangeListener;
+import android.preference.PreferenceFragment;
+import android.support.graphics.drawable.VectorDrawableCompat;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+
+import org.chromium.base.CommandLine;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.help.HelpAndFeedback;
+import org.chromium.chrome.browser.infobar.PreviewsLitePageInfoBar;
+import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
+import org.chromium.chrome.browser.preferences.ChromeSwitchPreference;
+import org.chromium.chrome.browser.preferences.PreferenceUtils;
+import org.chromium.chrome.browser.profiles.Profile;
+import org.chromium.chrome.browser.snackbar.DataReductionPromoSnackbarController;
+import org.chromium.chrome.browser.util.IntentUtils;
+
+/**
+ * Settings fragment that allows the user to configure Data Saver.
+ */
+public class DataReductionPreferenceFragment extends PreferenceFragment {
+    public static final String FROM_MAIN_MENU = "FromMainMenu";
+
+    public static final String PREF_DATA_REDUCTION_SWITCH = "data_reduction_switch";
+
+    // This is the same as Chromium data_reduction_proxy::switches::kEnableDataReductionProxy.
+    private static final String ENABLE_DATA_REDUCTION_PROXY = "enable-spdy-proxy-auth";
+
+    private boolean mIsEnabled;
+    private boolean mWasEnabledAtCreation;
+    /** Whether the current Activity is started from the snackbar promo. */
+    private boolean mFromPromo;
+    private boolean mFromMainMenu;
+    private boolean mFromInfobar;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        PreferenceUtils.addPreferencesFromResource(this, R.xml.data_reduction_preferences);
+        getActivity().setTitle(DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                R.string.data_reduction_title));
+        boolean isEnabled = DataReductionProxySettings.getInstance().isDataReductionProxyEnabled();
+        mIsEnabled = !isEnabled;
+        mWasEnabledAtCreation = isEnabled;
+        updatePreferences(isEnabled);
+
+        setHasOptionsMenu(true);
+
+        mFromPromo = IntentUtils.safeGetBoolean(
+                getArguments(), DataReductionPromoSnackbarController.FROM_PROMO, false);
+        mFromMainMenu = IntentUtils.safeGetBoolean(getArguments(), FROM_MAIN_MENU, false);
+        mFromInfobar = IntentUtils.safeGetBoolean(
+                getArguments(), PreviewsLitePageInfoBar.FROM_INFOBAR, false);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+
+        if (mWasEnabledAtCreation && !mIsEnabled) {
+            // If the user manually disables Data Saver, don't show the infobar promo.
+            DataReductionPromoUtils.saveInfoBarPromoDisplayed();
+        }
+
+        int statusChange;
+        if (mFromPromo) {
+            statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_SNACKBAR_LINK_CLICKED
+                                      : DataReductionProxyUma.ACTION_SNACKBAR_LINK_CLICKED_DISABLED;
+        } else if (mFromMainMenu) {
+            if (mWasEnabledAtCreation) {
+                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_MAIN_MENU_ON_TO_ON
+                                          : DataReductionProxyUma.ACTION_MAIN_MENU_ON_TO_OFF;
+            } else {
+                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_MAIN_MENU_OFF_TO_ON
+                                          : DataReductionProxyUma.ACTION_MAIN_MENU_OFF_TO_OFF;
+            }
+        } else if (mFromInfobar) {
+            if (mWasEnabledAtCreation) {
+                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_INFOBAR_ON_TO_ON
+                                          : DataReductionProxyUma.ACTION_INFOBAR_ON_TO_OFF;
+            } else {
+                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_INFOBAR_OFF_TO_ON
+                                          : DataReductionProxyUma.ACTION_INFOBAR_OFF_TO_OFF;
+            }
+        } else if (mWasEnabledAtCreation) {
+            statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_ON_TO_ON
+                                      : DataReductionProxyUma.ACTION_ON_TO_OFF;
+        } else {
+            statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_OFF_TO_ON
+                                      : DataReductionProxyUma.ACTION_OFF_TO_OFF;
+        }
+        DataReductionProxyUma.dataReductionProxyUIAction(statusChange);
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        menu.clear();
+        MenuItem help =
+                menu.add(Menu.NONE, R.id.menu_id_targeted_help, Menu.NONE, R.string.menu_help);
+        help.setIcon(VectorDrawableCompat.create(
+                getResources(), R.drawable.ic_help_and_feedback, getActivity().getTheme()));
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() == R.id.menu_id_targeted_help) {
+            HelpAndFeedback.getInstance(getActivity())
+                    .show(getActivity(), getString(R.string.help_context_data_reduction),
+                            Profile.getLastUsedProfile(), null);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Switches preference screens depending on whether data reduction is enabled/disabled.
+     * @param isEnabled Indicates whether data reduction is enabled.
+     */
+    public void updatePreferences(boolean isEnabled) {
+        if (mIsEnabled == isEnabled) return;
+        getPreferenceScreen().removeAll();
+        createDataReductionSwitch(isEnabled);
+        if (isEnabled) {
+            PreferenceUtils.addPreferencesFromResource(this, R.xml.data_reduction_preferences);
+        } else {
+            PreferenceUtils.addPreferencesFromResource(this,
+                    DataReductionBrandingResourceProvider.getPreferencesOffXml(
+                            R.xml.data_reduction_preferences_off));
+        }
+        mIsEnabled = isEnabled;
+    }
+
+    /**
+     * Returns summary string.
+     */
+    public static String generateSummary(Resources resources) {
+        if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled()) {
+            String percent =
+                    DataReductionProxySettings.getInstance().getContentLengthPercentSavings();
+            return resources.getString(
+                    DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                            R.string.data_reduction_menu_item_summary),
+                    percent);
+        } else {
+            return (String) resources.getText(R.string.text_off);
+        }
+    }
+
+    private void createDataReductionSwitch(boolean isEnabled) {
+        final ChromeSwitchPreference dataReductionSwitch =
+                new ChromeSwitchPreference(getActivity(), null);
+        dataReductionSwitch.setKey(PREF_DATA_REDUCTION_SWITCH);
+        dataReductionSwitch.setSummaryOn(R.string.text_on);
+        dataReductionSwitch.setSummaryOff(R.string.text_off);
+        dataReductionSwitch.setDrawDivider(true);
+        dataReductionSwitch.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+            @Override
+            public boolean onPreferenceChange(Preference preference, Object newValue) {
+                DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
+                        dataReductionSwitch.getContext(), (boolean) newValue);
+                DataReductionPreferenceFragment.this.updatePreferences((boolean) newValue);
+                return true;
+            }
+        });
+        dataReductionSwitch.setManagedPreferenceDelegate(preference -> {
+            return CommandLine.getInstance().hasSwitch(ENABLE_DATA_REDUCTION_PROXY)
+                    || DataReductionProxySettings.getInstance().isDataReductionProxyManaged();
+        });
+
+        getPreferenceScreen().addPreference(dataReductionSwitch);
+
+        // Note: setting the switch state before the preference is added to the screen results in
+        // some odd behavior where the switch state doesn't always match the internal enabled state
+        // (e.g. the switch will say "On" when data reduction is really turned off), so
+        // .setChecked() should be called after .addPreference()
+        dataReductionSwitch.setChecked(isEnabled);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java
deleted file mode 100644
index 6f4b164e..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java
+++ /dev/null
@@ -1,188 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-package org.chromium.chrome.browser.preferences.datareduction;
-
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.preference.PreferenceFragment;
-import android.support.graphics.drawable.VectorDrawableCompat;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-
-import org.chromium.base.CommandLine;
-import org.chromium.chrome.R;
-import org.chromium.chrome.browser.help.HelpAndFeedback;
-import org.chromium.chrome.browser.infobar.PreviewsLitePageInfoBar;
-import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings;
-import org.chromium.chrome.browser.preferences.ChromeSwitchPreference;
-import org.chromium.chrome.browser.preferences.PreferenceUtils;
-import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.snackbar.DataReductionPromoSnackbarController;
-import org.chromium.chrome.browser.util.IntentUtils;
-
-/**
- * Settings fragment that allows the user to configure Data Saver.
- */
-public class DataReductionPreferences extends PreferenceFragment {
-    public static final String FROM_MAIN_MENU = "FromMainMenu";
-
-    public static final String PREF_DATA_REDUCTION_SWITCH = "data_reduction_switch";
-
-    // This is the same as Chromium data_reduction_proxy::switches::kEnableDataReductionProxy.
-    private static final String ENABLE_DATA_REDUCTION_PROXY = "enable-spdy-proxy-auth";
-
-    private boolean mIsEnabled;
-    private boolean mWasEnabledAtCreation;
-    /** Whether the current Activity is started from the snackbar promo. */
-    private boolean mFromPromo;
-    private boolean mFromMainMenu;
-    private boolean mFromInfobar;
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        PreferenceUtils.addPreferencesFromResource(this, R.xml.data_reduction_preferences);
-        getActivity().setTitle(R.string.data_reduction_title);
-        boolean isEnabled =
-                DataReductionProxySettings.getInstance().isDataReductionProxyEnabled();
-        mIsEnabled = !isEnabled;
-        mWasEnabledAtCreation = isEnabled;
-        updatePreferences(isEnabled);
-
-        setHasOptionsMenu(true);
-
-        mFromPromo = IntentUtils.safeGetBoolean(
-                getArguments(), DataReductionPromoSnackbarController.FROM_PROMO, false);
-        mFromMainMenu = IntentUtils.safeGetBoolean(getArguments(), FROM_MAIN_MENU, false);
-        mFromInfobar = IntentUtils.safeGetBoolean(
-                getArguments(), PreviewsLitePageInfoBar.FROM_INFOBAR, false);
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-
-        if (mWasEnabledAtCreation && !mIsEnabled) {
-            // If the user manually disables Data Saver, don't show the infobar promo.
-            DataReductionPromoUtils.saveInfoBarPromoDisplayed();
-        }
-
-        int statusChange;
-        if (mFromPromo) {
-            statusChange = mIsEnabled
-                    ? DataReductionProxyUma.ACTION_SNACKBAR_LINK_CLICKED
-                    : DataReductionProxyUma.ACTION_SNACKBAR_LINK_CLICKED_DISABLED;
-        } else if (mFromMainMenu) {
-            if (mWasEnabledAtCreation) {
-                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_MAIN_MENU_ON_TO_ON
-                                          : DataReductionProxyUma.ACTION_MAIN_MENU_ON_TO_OFF;
-            } else {
-                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_MAIN_MENU_OFF_TO_ON
-                                          : DataReductionProxyUma.ACTION_MAIN_MENU_OFF_TO_OFF;
-            }
-        } else if (mFromInfobar) {
-            if (mWasEnabledAtCreation) {
-                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_INFOBAR_ON_TO_ON
-                                          : DataReductionProxyUma.ACTION_INFOBAR_ON_TO_OFF;
-            } else {
-                statusChange = mIsEnabled ? DataReductionProxyUma.ACTION_INFOBAR_OFF_TO_ON
-                                          : DataReductionProxyUma.ACTION_INFOBAR_OFF_TO_OFF;
-            }
-        } else if (mWasEnabledAtCreation) {
-            statusChange = mIsEnabled
-                    ? DataReductionProxyUma.ACTION_ON_TO_ON
-                    : DataReductionProxyUma.ACTION_ON_TO_OFF;
-        } else {
-            statusChange = mIsEnabled
-                    ? DataReductionProxyUma.ACTION_OFF_TO_ON
-                    : DataReductionProxyUma.ACTION_OFF_TO_OFF;
-        }
-        DataReductionProxyUma.dataReductionProxyUIAction(statusChange);
-    }
-
-    @Override
-    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
-        menu.clear();
-        MenuItem help = menu.add(
-                Menu.NONE, R.id.menu_id_targeted_help, Menu.NONE, R.string.menu_help);
-        help.setIcon(VectorDrawableCompat.create(
-                getResources(), R.drawable.ic_help_and_feedback, getActivity().getTheme()));
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item.getItemId() == R.id.menu_id_targeted_help) {
-            HelpAndFeedback.getInstance(getActivity())
-                    .show(getActivity(), getString(R.string.help_context_data_reduction),
-                            Profile.getLastUsedProfile(), null);
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Switches preference screens depending on whether data reduction is enabled/disabled.
-     * @param isEnabled Indicates whether data reduction is enabled.
-     */
-    public void updatePreferences(boolean isEnabled) {
-        if (mIsEnabled == isEnabled) return;
-        getPreferenceScreen().removeAll();
-        createDataReductionSwitch(isEnabled);
-        if (isEnabled) {
-            PreferenceUtils.addPreferencesFromResource(this, R.xml.data_reduction_preferences);
-        } else {
-            PreferenceUtils.addPreferencesFromResource(this, R.xml.data_reduction_preferences_off);
-        }
-        mIsEnabled = isEnabled;
-    }
-
-    /**
-     * Returns summary string.
-     */
-    public static String generateSummary(Resources resources) {
-        if (DataReductionProxySettings.getInstance().isDataReductionProxyEnabled()) {
-            String percent = DataReductionProxySettings.getInstance()
-                    .getContentLengthPercentSavings();
-            return resources.getString(
-                    R.string.data_reduction_menu_item_summary, percent);
-        } else {
-            return (String) resources.getText(R.string.text_off);
-        }
-    }
-
-    private void createDataReductionSwitch(boolean isEnabled) {
-        final ChromeSwitchPreference dataReductionSwitch =
-                new ChromeSwitchPreference(getActivity(), null);
-        dataReductionSwitch.setKey(PREF_DATA_REDUCTION_SWITCH);
-        dataReductionSwitch.setSummaryOn(R.string.text_on);
-        dataReductionSwitch.setSummaryOff(R.string.text_off);
-        dataReductionSwitch.setDrawDivider(true);
-        dataReductionSwitch.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
-            @Override
-            public boolean onPreferenceChange(Preference preference, Object newValue) {
-                DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
-                        dataReductionSwitch.getContext(), (boolean) newValue);
-                DataReductionPreferences.this.updatePreferences((boolean) newValue);
-                return true;
-            }
-        });
-        dataReductionSwitch.setManagedPreferenceDelegate(preference -> {
-            return CommandLine.getInstance().hasSwitch(ENABLE_DATA_REDUCTION_PROXY)
-                    || DataReductionProxySettings.getInstance().isDataReductionProxyManaged();
-        });
-
-        getPreferenceScreen().addPreference(dataReductionSwitch);
-
-        // Note: setting the switch state before the preference is added to the screen results in
-        // some odd behavior where the switch state doesn't always match the internal enabled state
-        // (e.g. the switch will say "On" when data reduction is really turned off), so
-        // .setChecked() should be called after .addPreference()
-        dataReductionSwitch.setChecked(isEnabled);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java
index 06787015..f80768e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java
@@ -52,9 +52,15 @@
     protected DialogParams getDialogParams() {
         PromoDialog.DialogParams params = new PromoDialog.DialogParams();
         params.drawableResource = R.drawable.data_reduction_illustration;
-        params.headerStringResource = R.string.data_reduction_promo_title;
-        params.subheaderStringResource = R.string.data_reduction_promo_summary;
-        params.primaryButtonStringResource = R.string.data_reduction_enable_button;
+        params.headerStringResource =
+                DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                        R.string.data_reduction_promo_title);
+        params.subheaderStringResource =
+                DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                        R.string.data_reduction_promo_summary);
+        params.primaryButtonStringResource =
+                DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                        R.string.data_reduction_enable_button);
         params.secondaryButtonStringResource = R.string.no_thanks;
         return params;
     }
@@ -82,8 +88,12 @@
         DataReductionProxySettings.getInstance().setDataReductionProxyEnabled(
                 getContext(), true);
         dismiss();
-        Toast.makeText(getContext(), getContext().getString(R.string.data_reduction_enabled_toast),
-                Toast.LENGTH_LONG).show();
+        Toast.makeText(getContext(),
+                     getContext().getString(
+                             DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                                     R.string.data_reduction_enabled_toast)),
+                     Toast.LENGTH_LONG)
+                .show();
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
index 80fda5e7..a358e6d3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionStatsPreference.java
@@ -376,9 +376,10 @@
                 };
 
                 new AlertDialog.Builder(getContext(), R.style.AlertDialogTheme)
-                        .setTitle(R.string.data_reduction_usage_reset_statistics_confirmation_title)
-                        .setMessage(
-                                R.string.data_reduction_usage_reset_statistics_confirmation_dialog)
+                        .setTitle(DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                                R.string.data_reduction_usage_reset_statistics_confirmation_title))
+                        .setMessage(DataReductionBrandingResourceProvider.getDataSaverBrandedString(
+                                R.string.data_reduction_usage_reset_statistics_confirmation_dialog))
                         .setPositiveButton(
                                 R.string.data_reduction_usage_reset_statistics_confirmation_button,
                                 dialogListener)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
index 9d1865e4..591f72b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
@@ -12,11 +12,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.StrictMode;
+import android.support.annotation.Nullable;
 import android.support.v4.app.ActivityOptionsCompat;
 import android.text.TextUtils;
 import android.view.View;
@@ -56,18 +55,11 @@
     /** Wraps up all things that a {@link SearchWidgetProvider} can request things from. */
     static class SearchWidgetProviderDelegate {
         private final Context mContext;
-        private final AppWidgetManager mManager;
+        private final @Nullable AppWidgetManager mManager;
 
         public SearchWidgetProviderDelegate(Context context) {
             mContext = context == null ? ContextUtils.getApplicationContext() : context;
-
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
-                    && !mContext.getPackageManager().hasSystemFeature(
-                               PackageManager.FEATURE_APP_WIDGETS)) {
-                mManager = null;
-            } else {
-                mManager = AppWidgetManager.getInstance(mContext);
-            }
+            mManager = AppWidgetManager.getInstance(mContext);
         }
 
         /** Returns the Context to pull resources from. */
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 fbd80765..86aaef1 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
@@ -321,14 +321,12 @@
     }
 
     private void updateSigninDetailsDescription(boolean addSettingsLink) {
-        final @StringRes int description = mChildAccountStatus == ChildAccountStatus.REGULAR_CHILD
-                ? R.string.signin_details_description_child_account
-                : R.string.signin_details_description;
         final @Nullable Object settingsLinkSpan =
                 addSettingsLink ? new NoUnderlineClickableSpan(this::onSettingsLinkClicked) : null;
         final SpanApplier.SpanInfo spanInfo =
                 new SpanApplier.SpanInfo(SETTINGS_LINK_OPEN, SETTINGS_LINK_CLOSE, settingsLinkSpan);
-        mConsentTextTracker.setText(mView.getDetailsDescriptionView(), description,
+        mConsentTextTracker.setText(mView.getDetailsDescriptionView(),
+                R.string.signin_details_description,
                 input -> SpanApplier.applySpans(input.toString(), spanInfo));
     }
 
@@ -343,16 +341,6 @@
                 : R.string.signin_sync_description;
         mConsentTextTracker.setText(mView.getSyncDescriptionView(), syncDescription);
 
-        mConsentTextTracker.setText(
-                mView.getTapToSearchTitleView(), R.string.signin_tap_to_search_title);
-        mConsentTextTracker.setText(
-                mView.getTapToSearchDescriptionView(), R.string.signin_tap_to_search_description);
-
-        mConsentTextTracker.setText(
-                mView.getSafeBrowsingTitleView(), R.string.signin_safe_browsing_title);
-        mConsentTextTracker.setText(
-                mView.getSafeBrowsingDescriptionView(), R.string.signin_safe_browsing_description);
-
         mConsentTextTracker.setText(mView.getRefuseButton(), getNegativeButtonTextId());
         mConsentTextTracker.setText(mView.getMoreButton(), R.string.more);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninView.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninView.java
index 7eb4710a..22df21a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninView.java
@@ -62,10 +62,6 @@
         mAccountPickerEndImage = findViewById(R.id.account_picker_end_image);
         mSyncTitle = findViewById(R.id.signin_sync_title);
         mSyncDescription = findViewById(R.id.signin_sync_description);
-        mTapToSearchTitle = findViewById(R.id.signin_tap_to_search_title);
-        mTapToSearchDescription = findViewById(R.id.signin_tap_to_search_description);
-        mSafeBrowsingTitle = findViewById(R.id.signin_safe_browsing_title);
-        mSafeBrowsingDescription = findViewById(R.id.signin_safe_browsing_description);
         mDetailsDescription = findViewById(R.id.signin_details_description);
         mAcceptButton = findViewById(R.id.positive_button);
         mRefuseButton = findViewById(R.id.negative_button);
@@ -111,22 +107,6 @@
         return mSyncDescription;
     }
 
-    TextView getTapToSearchTitleView() {
-        return mTapToSearchTitle;
-    }
-
-    TextView getTapToSearchDescriptionView() {
-        return mTapToSearchDescription;
-    }
-
-    TextView getSafeBrowsingTitleView() {
-        return mSafeBrowsingTitle;
-    }
-
-    TextView getSafeBrowsingDescriptionView() {
-        return mSafeBrowsingDescription;
-    }
-
     TextView getDetailsDescriptionView() {
         return mDetailsDescription;
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/DataReductionPromoSnackbarController.java b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/DataReductionPromoSnackbarController.java
index b4fb8c3..4c0f4c48 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/snackbar/DataReductionPromoSnackbarController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/snackbar/DataReductionPromoSnackbarController.java
@@ -10,7 +10,7 @@
 import org.chromium.base.CommandLine;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.PreferencesLauncher;
-import org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferences;
+import org.chromium.chrome.browser.preferences.datareduction.DataReductionPreferenceFragment;
 import org.chromium.chrome.browser.preferences.datareduction.DataReductionPromoUtils;
 import org.chromium.chrome.browser.preferences.datareduction.DataReductionProxyUma;
 import org.chromium.chrome.browser.util.ConversionUtils;
@@ -133,7 +133,7 @@
         Bundle fragmentArgs = new Bundle();
         fragmentArgs.putBoolean(FROM_PROMO, true);
         PreferencesLauncher.launchSettingsPage(
-                mContext, DataReductionPreferences.class, fragmentArgs);
+                mContext, DataReductionPreferenceFragment.class, fragmentArgs);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
index 412d261..4b110118 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/suggestions/SuggestionsBinder.java
@@ -108,7 +108,7 @@
         }
 
         mSmallThumbnailCornerRadius = mCardContainerView.getResources().getDimensionPixelSize(
-                R.dimen.snippets_thumbnail_small_corner_radius);
+                R.dimen.default_card_corner_radius);
     }
 
     public void updateViewInformation(SnippetArticle suggestion) {
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 51c44b78..c92b991 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
@@ -2879,7 +2879,7 @@
         }
 
         mDownloadIPHBubble.setPreferredVerticalOrientation(
-                AnchoredPopupWindow.VERTICAL_ORIENTATION_BELOW);
+                AnchoredPopupWindow.VerticalOrientation.BELOW);
         mDownloadIPHBubble.show();
         createPulse(rect);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridContainerViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridContainerViewBinder.java
new file mode 100644
index 0000000..2987ca6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridContainerViewBinder.java
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_list_ui;
+
+import static org.chromium.chrome.browser.tasks.tab_list_ui.TabListContainerProperties.IS_INCOGNITO;
+import static org.chromium.chrome.browser.tasks.tab_list_ui.TabListContainerProperties.IS_VISIBLE;
+
+import org.chromium.chrome.browser.util.ColorUtils;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+class TabGridContainerViewBinder {
+    /**
+     * Bind 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, TabListRecyclerView view, PropertyKey propertyKey) {
+        if (IS_VISIBLE == propertyKey) {
+            if (model.get(IS_VISIBLE)) {
+                view.startShowing();
+            } else {
+                view.startHiding();
+            }
+        } else if (IS_INCOGNITO == propertyKey) {
+            view.setBackgroundColor(
+                    ColorUtils.getDefaultThemeColor(view.getResources(), model.get(IS_INCOGNITO)));
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridCoordinator.java
new file mode 100644
index 0000000..2639ca3
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridCoordinator.java
@@ -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.
+
+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.support.v7.widget.GridLayoutManager;
+import android.view.LayoutInflater;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.compositor.CompositorViewHolder;
+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.TabModelSelector;
+import org.chromium.chrome.browser.toolbar.ToolbarManager;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
+import org.chromium.ui.modelutil.RecyclerViewAdapter;
+import org.chromium.ui.modelutil.SimpleRecyclerViewMcpBase;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Coordinator for showing a grid of tabs.
+ */
+@ActivityScope
+public class TabGridCoordinator implements Destroyable {
+    private static final int GRID_LAYOUT_SPAN_COUNT = 2;
+
+    private final PropertyModelChangeProcessor mContainerViewChangeProcessor;
+    private final SimpleRecyclerViewMcpBase<PropertyModel, TabGridViewHolder, PropertyKey>
+            mModelChangeProcessor;
+    private final TabListMediator mMediator;
+    private final TabListRecyclerView mRecyclerView;
+    private final ActivityLifecycleDispatcher mLifecycleDispatcher;
+
+    @Inject
+    TabGridCoordinator(@Named(ACTIVITY_CONTEXT) Context context,
+            ActivityLifecycleDispatcher lifecycleDispatcher, ToolbarManager toolbarManager,
+            TabModelSelector tabModelSelector, TabContentManager tabContentManager,
+            CompositorViewHolder compositorViewHolder) {
+        TabListModel tabListModel = new TabListModel();
+        mModelChangeProcessor = new SimpleRecyclerViewMcpBase<>(
+                null, TabGridViewBinder::onBindViewHolder, tabListModel);
+        RecyclerViewAdapter<TabGridViewHolder, PropertyKey> adapter =
+                new RecyclerViewAdapter<>(mModelChangeProcessor, TabGridViewHolder::create);
+
+        LayoutInflater.from(context).inflate(
+                R.layout.tab_grid_recycler_view_layout, compositorViewHolder, true);
+        mRecyclerView = compositorViewHolder.findViewById(R.id.tab_grid_view);
+        mRecyclerView.setAdapter(adapter);
+        mRecyclerView.setLayoutManager(new GridLayoutManager(context, GRID_LAYOUT_SPAN_COUNT));
+        mRecyclerView.setHasFixedSize(true);
+
+        PropertyModel containerViewModel = new PropertyModel(TabListContainerProperties.ALL_KEYS);
+        mContainerViewChangeProcessor = PropertyModelChangeProcessor.create(
+                containerViewModel, mRecyclerView, TabGridContainerViewBinder::bind);
+
+        mMediator = new TabListMediator(containerViewModel, tabListModel, context, toolbarManager,
+                tabModelSelector, tabContentManager);
+
+        mLifecycleDispatcher = lifecycleDispatcher;
+        mLifecycleDispatcher.register(this);
+    }
+
+    /**
+     * Destroy any members that needs clean up.
+     */
+    @Override
+    public void destroy() {
+        mContainerViewChangeProcessor.destroy();
+        mMediator.destroy();
+        mLifecycleDispatcher.unregister(this);
+    }
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewBinder.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewBinder.java
new file mode 100644
index 0000000..aabc5431f
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewBinder.java
@@ -0,0 +1,68 @@
+// 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.support.annotation.Nullable;
+import android.support.v4.content.res.ResourcesCompat;
+import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.widget.FrameLayout;
+
+import org.chromium.chrome.R;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * {@link org.chromium.chrome.browser.modelutil.SimpleRecyclerViewMcpBase.ViewBinder} for tab grid.
+ * This class supports both full and partial updates to the {@link TabGridViewHolder}.
+ */
+class TabGridViewBinder {
+    /**
+     * Partially or fully update the given ViewHolder based on the given model over propertyKey.
+     * @param holder The {@link ViewHolder} to use.
+     * @param item The model to use.
+     * @param propertyKey If present, to be used as the key to partially update. If null, a full
+     *                    bind is done.
+     */
+    public static void onBindViewHolder(
+            TabGridViewHolder holder, PropertyModel item, @Nullable PropertyKey propertyKey) {
+        if (propertyKey == null) {
+            onBindViewHolder(holder, item);
+            return;
+        }
+        if (TabProperties.TITLE == propertyKey) {
+            String title = item.get(TabProperties.TITLE);
+            holder.title.setText(title);
+            holder.closeButton.setContentDescription(holder.itemView.getResources().getString(
+                    R.string.accessibility_tabstrip_btn_close_tab, title));
+        } else if (TabProperties.IS_SELECTED == propertyKey) {
+            ((FrameLayout) holder.itemView)
+                    .setForeground(item.get(TabProperties.IS_SELECTED)
+                                    ? ResourcesCompat.getDrawable(holder.itemView.getResources(),
+                                            R.drawable.selected_tab_background,
+                                            holder.itemView.getContext().getTheme())
+                                    : null);
+        } else if (TabProperties.TAB_SELECTED_LISTENER == propertyKey) {
+            holder.itemView.setOnClickListener(view -> {
+                item.get(TabProperties.TAB_SELECTED_LISTENER).run(holder.getTabId());
+            });
+        } else if (TabProperties.TAB_CLOSED_LISTENER == propertyKey) {
+            holder.closeButton.setOnClickListener(view -> {
+                item.get(TabProperties.TAB_CLOSED_LISTENER).run(holder.getTabId());
+            });
+        } else if (TabProperties.FAVICON == propertyKey) {
+            holder.favicon.setImageBitmap(item.get(TabProperties.FAVICON));
+        } else if (TabProperties.THUMBNAIL_KEY_URI == propertyKey) {
+            // TODO(yusufo) : Add logic to get this from a cache.
+        } else if (TabProperties.TAB_ID == propertyKey) {
+            holder.setTabId(item.get(TabProperties.TAB_ID));
+        }
+    }
+
+    private static void onBindViewHolder(TabGridViewHolder holder, PropertyModel item) {
+        for (PropertyKey propertyKey : TabProperties.ALL_KEYS) {
+            onBindViewHolder(holder, item, propertyKey);
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewHolder.java
new file mode 100644
index 0000000..3d6d2ff
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewHolder.java
@@ -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.
+
+package org.chromium.chrome.browser.tasks.tab_list_ui;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.chromium.chrome.R;
+
+/**
+ * {@link android.support.v7.widget.RecyclerView.ViewHolder} for tab grid. Owns the tab info card
+ * and the associated view hierarchy.
+ */
+class TabGridViewHolder extends RecyclerView.ViewHolder {
+    public final ImageView favicon;
+    public final TextView title;
+    public final ImageView thumbnail;
+    public final ImageView closeButton;
+    public int mTabId;
+
+    public TabGridViewHolder(View itemView) {
+        super(itemView);
+        this.thumbnail = itemView.findViewById(R.id.tab_thumbnail);
+        this.title = itemView.findViewById(R.id.tab_title);
+        this.favicon = itemView.findViewById(R.id.tab_favicon);
+        this.closeButton = itemView.findViewById(R.id.close_button);
+    }
+
+    public static TabGridViewHolder create(ViewGroup parent, int itemViewType) {
+        View view =
+                LayoutInflater.from(parent.getContext())
+                        .inflate(org.chromium.chrome.R.layout.tab_grid_card_item, parent, false);
+        return new TabGridViewHolder(view);
+    }
+
+    public void setTabId(int tabId) {
+        mTabId = tabId;
+    }
+
+    public int getTabId() {
+        return mTabId;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListContainerProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListContainerProperties.java
new file mode 100644
index 0000000..d57ac938
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListContainerProperties.java
@@ -0,0 +1,18 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_list_ui;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+
+class TabListContainerProperties {
+    public static final PropertyModel.WritableBooleanPropertyKey IS_VISIBLE =
+            new PropertyModel.WritableBooleanPropertyKey();
+
+    public static final PropertyModel.WritableBooleanPropertyKey IS_INCOGNITO =
+            new PropertyModel.WritableBooleanPropertyKey();
+
+    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {IS_VISIBLE, IS_INCOGNITO};
+}
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
new file mode 100644
index 0000000..5dc7ca6
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediator.java
@@ -0,0 +1,182 @@
+// 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.tasks.tab_list_ui.TabListContainerProperties.IS_INCOGNITO;
+import static org.chromium.chrome.browser.tasks.tab_list_ui.TabListContainerProperties.IS_VISIBLE;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.compositor.layouts.content.TabContentManager;
+import org.chromium.chrome.browser.favicon.FaviconHelper;
+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.TabObserver;
+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.chrome.browser.toolbar.ToolbarManager;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * Mediator for business logic for the tab grid. This class should be initialized with a list of
+ * tabs and a TabModel to observe for changes and should not have any logic around what the list
+ * signifies.
+ * TODO(yusufo): Move some of the logic here to a parent component to make the above true.
+ */
+class TabListMediator {
+    private final int mFaviconSize;
+    private final FaviconHelper mFaviconHelper = new FaviconHelper();
+    private final TabListModel mModel;
+    private final PropertyModel mContainerViewModel;
+    private final TabModelSelector mTabModelSelector;
+    private final TabContentManager mTabContentManager;
+
+    private final TabActionListener mTabSelectedListener = new TabActionListener() {
+        @Override
+        public void run(int tabId) {
+            mTabModelSelector.getCurrentModel().setIndex(
+                    TabModelUtils.getTabIndexById(mTabModelSelector.getCurrentModel(), tabId),
+                    TabSelectionType.FROM_USER);
+            if (mTabModelSelector.getCurrentTabId() == tabId) {
+                mContainerViewModel.set(IS_VISIBLE, false);
+            }
+        }
+    };
+
+    private final TabActionListener mTabClosedListener = new TabActionListener() {
+        @Override
+        public void run(int tabId) {
+            mTabModelSelector.getCurrentModel().closeTab(
+                    TabModelUtils.getTabById(mTabModelSelector.getCurrentModel(), tabId));
+        }
+    };
+
+    private final TabObserver mTabObserver = new EmptyTabObserver() {
+        @Override
+        public void onTitleUpdated(Tab updatedTab) {
+            int index = mModel.indexFromId(updatedTab.getId());
+            if (index == TabModel.INVALID_TAB_INDEX) return;
+            mModel.get(index).set(TabProperties.TITLE, updatedTab.getTitle());
+        }
+
+        @Override
+        public void onFaviconUpdated(Tab updatedTab, Bitmap icon) {
+            int index = mModel.indexFromId(updatedTab.getId());
+            if (index == TabModel.INVALID_TAB_INDEX) return;
+            mModel.get(index).set(TabProperties.FAVICON, icon);
+        }
+    };
+
+    private final TabModelSelectorTabModelObserver mTabModelObserver;
+
+    /**
+     * Interface for implementing a {@link Runnable} that takes a tabId for a generic action.
+     */
+    public interface TabActionListener { void run(int tabId); }
+
+    /**
+     * Construct the Mediator with the given Models and observing hooks from the given
+     * ChromeActivity.
+     * @param containerViewModel The {@link PropertyModel} to keep state about the container view.
+     * @param model The Model to keep state about a list of {@link Tab}s.
+     * @param context The context to use for accessing {@link android.content.res.Resources}
+     * @param toolbarManager {@link ToolbarManager} to send any signals about overriding behavior.
+     * @param tabModelSelector {@link TabModelSelector} that will provide and receive signals about
+     *                                                 the tabs concerned.
+     * @param tabContentManager {@link TabContentManager} to provide screenshot related details.
+     */
+    public TabListMediator(PropertyModel containerViewModel, TabListModel model, Context context,
+            ToolbarManager toolbarManager, TabModelSelector tabModelSelector,
+            TabContentManager tabContentManager) {
+        mFaviconSize = context.getResources().getDimensionPixelSize(R.dimen.tab_grid_favicon_size);
+
+        mTabModelSelector = tabModelSelector;
+        mTabContentManager = tabContentManager;
+        mModel = model;
+
+        mTabModelObserver = new TabModelSelectorTabModelObserver(mTabModelSelector) {
+            @Override
+            public void didSelectTab(Tab tab, int type, int lastId) {
+                if (tab.getId() == lastId) return;
+                if (mModel.indexFromId(lastId) != TabModel.INVALID_TAB_INDEX) {
+                    mModel.get(mModel.indexFromId(lastId)).set(TabProperties.IS_SELECTED, false);
+                }
+                mModel.get(mModel.indexFromId(tab.getId())).set(TabProperties.IS_SELECTED, true);
+                mContainerViewModel.set(IS_VISIBLE, false);
+            }
+
+            @Override
+            public void didAddTab(Tab tab, int type) {
+                addTabInfoToModel(tab, false);
+            }
+
+            @Override
+            public void didCloseTab(int tabId, boolean incognito) {
+                mModel.removeAt(mModel.indexFromId(tabId));
+            }
+        };
+
+        // TODO(yusufo): Move the calls below to a parent component, and change resetWithTabModel to
+        // take a List of Tabs, so that this class stays oblivious to switcher button related
+        // logistics. Also lazily initialize TabGridComponent on button click once there are two
+        // components.
+        mContainerViewModel = containerViewModel;
+        toolbarManager.overrideTabSwitcherBehavior(view
+                -> mContainerViewModel.set(IS_VISIBLE, !mContainerViewModel.get(IS_VISIBLE)),
+                null);
+
+        resetWithTabModel(mTabModelSelector.getCurrentModel());
+    }
+
+    /**
+     * Initialize the component with a list of tabs to show in a grid.
+     */
+    public void resetWithTabModel(TabModel tabModel) {
+        mContainerViewModel.set(IS_INCOGNITO, tabModel.isIncognito());
+
+        int selectedIndex = tabModel.index();
+        for (int i = 0; i < tabModel.getCount(); i++) {
+            addTabInfoToModel(tabModel.getTabAt(i), i == selectedIndex);
+        }
+    }
+
+    /**
+     * Destroy any members that needs clean up.
+     */
+    public void destroy() {
+        TabModel tabModel = mTabModelSelector.getCurrentModel();
+        if (tabModel != null) {
+            for (int i = 0; i < tabModel.getCount(); i++) {
+                tabModel.getTabAt(i).removeObserver(mTabObserver);
+            }
+        }
+        mTabModelObserver.destroy();
+    }
+
+    private void addTabInfoToModel(final Tab tab, boolean isSelected) {
+        PropertyModel tabInfo =
+                new PropertyModel.Builder(TabProperties.ALL_KEYS)
+                        .with(TabProperties.TAB_ID, tab.getId())
+                        .with(TabProperties.TITLE, tab.getTitle())
+                        .with(TabProperties.FAVICON, tab.getFavicon())
+                        .with(TabProperties.IS_SELECTED, isSelected)
+                        .with(TabProperties.TAB_SELECTED_LISTENER, mTabSelectedListener)
+                        .with(TabProperties.TAB_CLOSED_LISTENER, mTabClosedListener)
+                        .build();
+        mModel.add(tabInfo);
+        mFaviconHelper.getLocalFaviconImageForURL(Profile.getLastUsedProfile().getOriginalProfile(),
+                tab.getUrl(), mFaviconSize,
+                (image, iconUrl)
+                        -> mModel.get(mModel.indexFromId(tab.getId()))
+                                   .set(TabProperties.FAVICON, image));
+        tab.addObserver(mTabObserver);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListModel.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListModel.java
new file mode 100644
index 0000000..b75ebb76
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListModel.java
@@ -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.
+
+package org.chromium.chrome.browser.tasks.tab_list_ui;
+
+import static org.chromium.chrome.browser.tasks.tab_list_ui.TabProperties.TAB_ID;
+
+import org.chromium.chrome.browser.tabmodel.TabModel;
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyListModel;
+import org.chromium.ui.modelutil.PropertyModel;
+
+/**
+ * A {@link PropertyListModel} implementation to keep information about a list of
+ * {@link org.chromium.chrome.browser.tab.Tab}s.
+ */
+class TabListModel extends PropertyListModel<PropertyModel, PropertyKey> {
+    /**
+     * Convert the given tab ID to an index to match during partial updates.
+     * @param tabId The tab ID to search for.
+     * @return The index within the model {@link org.chromium.chrome.browser.modelutil.SimpleList}.
+     */
+    public int indexFromId(int tabId) {
+        for (int i = 0; i < size(); i++) {
+            if (get(i).get(TAB_ID) == tabId) return i;
+        }
+        return TabModel.INVALID_TAB_INDEX;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListRecyclerView.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListRecyclerView.java
new file mode 100644
index 0000000..f1149a51
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListRecyclerView.java
@@ -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.
+
+package org.chromium.chrome.browser.tasks.tab_list_ui;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.view.View;
+
+import org.chromium.ui.interpolators.BakedBezierInterpolator;
+
+/**
+ * A custom RecyclerView implementation for the tab grid, to handle show/hide logic in class.
+ */
+class TabListRecyclerView extends RecyclerView {
+    public static final long BASE_ANIMATION_DURATION_MS = 218;
+
+    private ValueAnimator mFadeInAnimator;
+    private ValueAnimator mFadeOutAnimator;
+
+    public TabListRecyclerView(Context context, AttributeSet attributeSet) {
+        super(context, attributeSet);
+    }
+
+    void startShowing() {
+        cancelAllAnimations();
+        setAlpha(0);
+        setVisibility(View.VISIBLE);
+        mFadeInAnimator = ObjectAnimator.ofFloat(this, View.ALPHA, 1);
+        mFadeInAnimator.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
+        mFadeInAnimator.setDuration(BASE_ANIMATION_DURATION_MS);
+        mFadeInAnimator.start();
+    }
+
+    void startHiding() {
+        cancelAllAnimations();
+        mFadeOutAnimator = ObjectAnimator.ofFloat(this, View.ALPHA, 0);
+        mFadeOutAnimator.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
+        mFadeOutAnimator.setDuration(BASE_ANIMATION_DURATION_MS);
+        mFadeOutAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                setVisibility(View.INVISIBLE);
+            }
+        });
+        mFadeOutAnimator.start();
+    }
+
+    private void cancelAllAnimations() {
+        if (mFadeInAnimator != null) {
+            mFadeInAnimator.cancel();
+        }
+        if (mFadeOutAnimator != null) {
+            mFadeOutAnimator.cancel();
+        }
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabProperties.java b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabProperties.java
new file mode 100644
index 0000000..2919511
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabProperties.java
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.tasks.tab_list_ui;
+
+import android.graphics.Bitmap;
+
+import org.chromium.ui.modelutil.PropertyKey;
+import org.chromium.ui.modelutil.PropertyModel;
+import org.chromium.ui.modelutil.PropertyModel.ReadableIntPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
+import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
+
+/**
+ * List of properties to designate information about a single tab.
+ */
+public class TabProperties {
+    public static final PropertyModel.ReadableIntPropertyKey TAB_ID = new ReadableIntPropertyKey();
+
+    public static final WritableObjectPropertyKey<TabListMediator.TabActionListener>
+            TAB_SELECTED_LISTENER = new WritableObjectPropertyKey<>();
+
+    public static final WritableObjectPropertyKey<TabListMediator.TabActionListener>
+            TAB_CLOSED_LISTENER = new WritableObjectPropertyKey<>();
+
+    public static final WritableObjectPropertyKey<Bitmap> FAVICON =
+            new WritableObjectPropertyKey<>();
+
+    public static final WritableObjectPropertyKey<String> THUMBNAIL_KEY_URI =
+            new WritableObjectPropertyKey<>();
+
+    public static final WritableObjectPropertyKey<String> TITLE = new WritableObjectPropertyKey<>();
+
+    public static final WritableBooleanPropertyKey IS_SELECTED = new WritableBooleanPropertyKey();
+
+    public static final PropertyKey[] ALL_KEYS = new PropertyKey[] {TAB_ID, TAB_SELECTED_LISTENER,
+            TAB_CLOSED_LISTENER, FAVICON, THUMBNAIL_KEY_URI, TITLE, IS_SELECTED};
+}
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 394f83d..914e950 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
@@ -94,6 +94,7 @@
 import org.chromium.chrome.browser.toolbar.top.TopToolbarCoordinator;
 import org.chromium.chrome.browser.toolbar.top.ViewShiftingActionBarDelegate;
 import org.chromium.chrome.browser.translate.TranslateBridge;
+import org.chromium.chrome.browser.util.ColorUtils;
 import org.chromium.chrome.browser.util.FeatureUtilities;
 import org.chromium.chrome.browser.widget.ScrimView;
 import org.chromium.chrome.browser.widget.ScrimView.ScrimObserver;
@@ -1039,6 +1040,22 @@
     }
 
     /**
+     * Overrides tab switcher launching behavior.
+     * @param newClickListener The new {@link OnClickListener} for tab switcher button clicks.
+     * @param overviewModeBehavior The OverviewModeBehavior to be used for tab switcher states.
+     */
+    public void overrideTabSwitcherBehavior(
+            OnClickListener newClickListener, OverviewModeBehavior overviewModeBehavior) {
+        assert mInitializedWithNative;
+
+        mToolbar.setTabSwitcherClickListener(newClickListener);
+        if (mBottomToolbarCoordinator != null) {
+            mBottomToolbarCoordinator.overrideTabSwitcherBehavior(
+                    newClickListener, overviewModeBehavior);
+        }
+    }
+
+    /**
      * @return The experimental toolbar button if it exists.
      */
     public @Nullable View getExperimentalButtonView() {
@@ -1665,6 +1682,12 @@
                 if (tab != null) tab.addObserver(mTabObserver);
             }
 
+            int defaultPrimaryColor =
+                    ColorUtils.getDefaultThemeColor(mActivity.getResources(), isIncognito);
+            int primaryColor =
+                    tab != null ? TabThemeColorHelper.getColor(tab) : defaultPrimaryColor;
+            onThemeColorChanged(primaryColor, false);
+
             mToolbar.onTabOrModelChanged();
 
             if (tab != null && tab.getWebContents() != null
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 f5f31ae..3f5b91c 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
@@ -145,6 +145,16 @@
     }
 
     /**
+     * Overrides tab switcher launching behavior.
+     * @param newClickListener The new {@link OnClickListener} for tab switcher button clicks.
+     * @param overviewModeBehavior The OverviewModeBehavior to be used for tab switcher states.
+     */
+    public void overrideTabSwitcherBehavior(
+            OnClickListener newClickListener, OverviewModeBehavior overviewModeBehavior) {
+        mBrowsingModeCoordinator.overrideTabSwitcherBehavior(newClickListener, null);
+    }
+
+    /**
      * Clean up any state when the bottom toolbar is destroyed.
      */
     public void destroy() {
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 9623cdc..54de0d12 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
@@ -191,6 +191,16 @@
     }
 
     /**
+     * Overrides tab switcher launching behavior.
+     * @param newClickListener The new {@link OnClickListener} for tab switcher button clicks.
+     * @param overviewModeBehavior The OverviewModeBehavior to be used for tab switcher states.
+     */
+    public void overrideTabSwitcherBehavior(
+            OnClickListener newClickListener, OverviewModeBehavior overviewModeBehavior) {
+        mTabSwitcherButtonCoordinator.setTabSwitcherListener(newClickListener);
+    }
+
+    /**
      * @return Whether the browsing mode toolbar is visible.
      */
     public boolean isVisible() {
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 184e4f2..2bf23ab 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
@@ -374,6 +374,18 @@
     }
 
     /**
+     * Overrides tab switcher launching behavior.
+     * @param newClickListener The new {@link OnClickListener} for tab switcher button clicks.
+     *
+     */
+    public void setTabSwitcherClickListener(OnClickListener newClickListener) {
+        if (mTabSwitcherModeCoordinatorPhone != null) {
+            mTabSwitcherModeCoordinatorPhone.setOnTabSwitcherClickHandler(newClickListener);
+        }
+        mToolbarLayout.setOnTabSwitcherClickHandler(newClickListener);
+    }
+
+    /**
      * Gives inheriting classes the chance to show or hide the TabSwitcher mode of this toolbar.
      * @param inTabSwitcherMode Whether or not TabSwitcher mode should be shown or hidden.
      * @param showToolbar    Whether or not to show the normal toolbar while animating.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsBridge.java b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsBridge.java
index a4812c79..1bdd6b5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsBridge.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/usage_stats/UsageStatsBridge.java
@@ -8,6 +8,7 @@
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.chrome.browser.profiles.Profile;
 
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
@@ -35,23 +36,17 @@
         mNativeUsageStatsBridge = 0;
     }
 
-    /* NOTE: The parameter types below are a best guess at what will correspond well with the native
-     * types, but they're likely to change once the native side of the bridge and type conversion is
-     * actually implemented.
-     */
-
-    public void getAllEvents(Callback<List<String>> callback) {
+    public void getAllEvents(Callback<List<WebsiteEvent>> callback) {
         assert mNativeUsageStatsBridge != 0;
         nativeGetAllEvents(mNativeUsageStatsBridge, callback);
     }
 
-    public void queryEventsInRange(long start, long end, Callback<List<String>> callback) {
+    public void queryEventsInRange(long start, long end, Callback<List<WebsiteEvent>> callback) {
         assert mNativeUsageStatsBridge != 0;
         nativeQueryEventsInRange(mNativeUsageStatsBridge, start, end, callback);
     }
 
-    // Each event is a serialized WebsiteEvent protocol buffer message.
-    public void addEvents(byte[][] events, Callback<Boolean> callback) {
+    public void addEvents(List<WebsiteEvent> events, Callback<Boolean> callback) {
         assert mNativeUsageStatsBridge != 0;
         nativeAddEvents(mNativeUsageStatsBridge, events, callback);
     }
@@ -73,7 +68,8 @@
 
     public void getAllSuspensions(Callback<List<String>> callback) {
         assert mNativeUsageStatsBridge != 0;
-        nativeGetAllSuspensions(mNativeUsageStatsBridge, callback);
+        nativeGetAllSuspensions(
+                mNativeUsageStatsBridge, arr -> { callback.onResult(Arrays.asList(arr)); });
     }
 
     public void setSuspensions(String[] domains, Callback<Boolean> callback) {
@@ -81,7 +77,7 @@
         nativeSetSuspensions(mNativeUsageStatsBridge, domains, callback);
     }
 
-    public void getAllTokenMappings(Callback<List<String>> callback) {
+    public void getAllTokenMappings(Callback<Map<String, String>> callback) {
         assert mNativeUsageStatsBridge != 0;
         nativeGetAllTokenMappings(mNativeUsageStatsBridge, callback);
     }
@@ -94,11 +90,11 @@
     private native long nativeInit(Profile profile);
     private native void nativeDestroy(long nativeUsageStatsBridge);
     private native void nativeGetAllEvents(
-            long nativeUsageStatsBridge, Callback<List<String>> callback);
-    private native void nativeQueryEventsInRange(
-            long nativeUsageStatsBridge, long start, long end, Callback<List<String>> callback);
+            long nativeUsageStatsBridge, Callback<List<WebsiteEvent>> callback);
+    private native void nativeQueryEventsInRange(long nativeUsageStatsBridge, long start, long end,
+            Callback<List<WebsiteEvent>> callback);
     private native void nativeAddEvents(
-            long nativeUsageStatsBridge, byte[][] events, Callback<Boolean> callback);
+            long nativeUsageStatsBridge, List<WebsiteEvent> events, Callback<Boolean> callback);
     private native void nativeDeleteAllEvents(
             long nativeUsageStatsBridge, Callback<Boolean> callback);
     private native void nativeDeleteEventsInRange(
@@ -106,11 +102,11 @@
     private native void nativeDeleteEventsWithMatchingDomains(
             long nativeUsageStatsBridge, String[] domains, Callback<Boolean> callback);
     private native void nativeGetAllSuspensions(
-            long nativeUsageStatsBridge, Callback<List<String>> callback);
+            long nativeUsageStatsBridge, Callback<String[]> callback);
     private native void nativeSetSuspensions(
             long nativeUsageStatsBridge, String[] domains, Callback<Boolean> callback);
     private native void nativeGetAllTokenMappings(
-            long nativeUsageStatsBridge, Callback<List<String>> callback);
+            long nativeUsageStatsBridge, Callback<Map<String, String>> callback);
     private native void nativeSetTokenMappings(
             long nativeUsageStatsBridge, Map<String, String> mappings, Callback<Boolean> callback);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
index 00b5a48..87961951 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/WebappActivity.java
@@ -769,7 +769,7 @@
         int statusBarColor = Color.BLACK;
         if (mBrandColor != null && mWebappInfo.displayMode() != WebDisplayMode.FULLSCREEN) {
             taskDescriptionColor = mBrandColor;
-            statusBarColor = ColorUtils.getDarkenedColorForStatusBar(mBrandColor);
+            statusBarColor = mBrandColor;
             if (getToolbarManager() != null) {
                 getToolbarManager().onThemeColorChanged(mBrandColor, false);
             }
@@ -777,17 +777,21 @@
 
         ApiCompatibilityUtils.setTaskDescription(this, title, icon,
                 ColorUtils.getOpaqueColor(taskDescriptionColor));
-        ApiCompatibilityUtils.setStatusBarColor(getWindow(), statusBarColor);
+        setStatusBarColor(statusBarColor, statusBarColor != Color.BLACK);
     }
 
     @Override
     protected void setStatusBarColor(Tab tab, int color) {
-        // Intentionally do nothing as WebappActivity explicitly sets status bar color.
+        // Ignore any color that is not the brand color.
+        super.setStatusBarColor(
+                mBrandColor == null ? Color.BLACK : mBrandColor, mBrandColor == null);
     }
 
     @Override
     protected void setStatusBarColor(int color, boolean isDefaultThemeColor) {
-        // Intentionally do nothing as WebappActivity explicitly sets status bar color.
+        // Ignore any color that is not the brand color.
+        super.setStatusBarColor(
+                mBrandColor == null ? Color.BLACK : mBrandColor, mBrandColor == null);
     }
 
     @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/TextBubble.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/TextBubble.java
index ec38e62..83ba6e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/TextBubble.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/TextBubble.java
@@ -178,7 +178,7 @@
         mPopupWindow.setMargin(
                 context.getResources().getDimensionPixelSize(R.dimen.text_bubble_margin));
         mPopupWindow.setPreferredHorizontalOrientation(
-                AnchoredPopupWindow.HORIZONTAL_ORIENTATION_CENTER);
+                AnchoredPopupWindow.HorizontalOrientation.CENTER);
         mPopupWindow.setLayoutObserver(this);
 
         mHandler = new Handler();
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index f040fc3b..c208f5b 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2704,37 +2704,22 @@
       </message>
 
       <!-- Strings for Streamlined Signin and Unified Consent. -->
-      <message name="IDS_SIGNIN_TITLE" desc="Title for the screen that asks users to sign-in and turn on Sync and personalization. [CHAR-LIMIT=27]">
-        Get Google smarts in Chrome
+      <message name="IDS_SIGNIN_TITLE" desc="Title for the screen that asks users to sign-in and turn on Sync. [CHAR-LIMIT=27]">
+        Turn on sync?
       </message>
-      <message name="IDS_SIGNIN_SYNC_TITLE" desc="Title of Sync feature for the screen that asks users to sign-in and turn on Sync and personalization.">
+      <message name="IDS_SIGNIN_SYNC_TITLE" desc="Title of Sync feature for the screen that asks users to sign-in and turn on Sync.">
         Sync your passwords, history &amp; more on all devices
       </message>
-      <message name="IDS_SIGNIN_SYNC_DESCRIPTION" desc="Description of Sync feature for the screen that asks users to sign-in and turn on Sync and personalization.">
+      <message name="IDS_SIGNIN_SYNC_DESCRIPTION" desc="Description of Sync feature for the screen that asks users to sign-in and turn on Sync.">
         Google may use your history to personalize Search, ads, and other Google services
       </message>
-      <message name="IDS_SIGNIN_SYNC_DESCRIPTION_CHILD_ACCOUNT" desc="Description of Sync feature for the screen that asks users to sign-in and turn on Sync and personalization.">
+      <message name="IDS_SIGNIN_SYNC_DESCRIPTION_CHILD_ACCOUNT" desc="Description of Sync feature for the screen that asks users to sign-in and turn on Sync.">
         Google may use your history to personalize Search and other Google services
       </message>
-      <message name="IDS_SIGNIN_TAP_TO_SEARCH_TITLE" desc="Title of TapToSearch feature for the screen that asks users to sign-in and turn on Sync and personalization.">
-        Search by tapping a word on a page (Tap to Search)
-      </message>
-      <message name="IDS_SIGNIN_TAP_TO_SEARCH_DESCRIPTION" desc="Description of TapToSearch feature for the screen that asks users to sign-in and turn on Sync and personalization.">
-        Google may use the words you tap on and surrounding text to return results
-      </message>
-      <message name="IDS_SIGNIN_SAFE_BROWSING_TITLE" desc="Title of Safe Browsing Extended Reporting feature for the screen that asks users to sign-in and turn on Sync and personalization.">
-        Help improve Chrome and its security
-      </message>
-      <message name="IDS_SIGNIN_SAFE_BROWSING_DESCRIPTION" desc="Description of Safe Browsing Extended Reporting feature for the screen that asks users to sign-in and turn on Sync and personalization.">
-        Sends some system information and page content to Google anonymously
-      </message>
-      <message name="IDS_SIGNIN_DETAILS_DESCRIPTION" desc="Message with a link to customize settings of the features enabled by 'Get Google smarts in Chrome' screen.">
+      <message name="IDS_SIGNIN_DETAILS_DESCRIPTION" desc="Message with a link to customize Sync settings. Shown on the screen that asks the user to turn on Sync.">
         You can <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>customize these settings<ph name="END_LINK1">&lt;/LINK1&gt;</ph> anytime
       </message>
-      <message name="IDS_SIGNIN_DETAILS_DESCRIPTION_CHILD_ACCOUNT" desc="Message explaining that user can customize Sync and personalization and explains what data is shared with Google. This message is shown in the screen that asks users to sign-in and turn on Sync and personalization. This version of the description is used with child accounts.">
-        <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Settings<ph name="END_LINK1">&lt;/LINK1&gt;</ph> can be customized by you or your parents anytime. Google may use content on sites you visit, browser interactions and activity to personalize Chrome and Google services like Translate, Search, and ads.
-      </message>
-      <message name="IDS_SIGNIN_ACCEPT_BUTTON" desc="Text for the confirmation button in the sign-in screen. By clicking this button users signs in and turns on Sync and personalization. [CHAR-LIMIT=20]">
+      <message name="IDS_SIGNIN_ACCEPT_BUTTON" desc="Text for the confirmation button in the sign-in screen. By clicking this button users signs in and turns on Sync. [CHAR-LIMIT=20]">
         Yes, I'm in
       </message>
       <message name="IDS_SIGNIN_ACCOUNT_PICKER_DIALOG_TITLE" desc="The title for the dialog that shows the list of accounts on the device and asks the user to select one of these accounts. [CHAR-LIMIT=27]">
@@ -3627,6 +3612,10 @@
       <message name="IDS_CONTACTS_PICKER_ALL_CONTACTS" desc="The label associated with the select all checkbox, indicating that all the contacts will be selected.">
         All contacts
       </message>
+      <message name="IDS_CONTACTS_PICKER_NO_CONTACTS_FOUND" desc="The label shown when no contacts are found (e.g. none exist on the device).">
+        No contacts found
+      </message>
+
       <message name="IDS_CONTACTS_PICKER_MORE_DETAILS" desc="Label describing that the user has one or more telephone/emails (used for either). The leading space is a word separator, for those languages that use space as a separator.">
         {DETAIL_COUNT, plural,
           =1 { (+ 1 more)}
diff --git a/chrome/android/java/strings/android_chrome_strings_grd/IDS_CONTACTS_PICKER_NO_CONTACTS_FOUND.png.sha1 b/chrome/android/java/strings/android_chrome_strings_grd/IDS_CONTACTS_PICKER_NO_CONTACTS_FOUND.png.sha1
new file mode 100644
index 0000000..d91c747
--- /dev/null
+++ b/chrome/android/java/strings/android_chrome_strings_grd/IDS_CONTACTS_PICKER_NO_CONTACTS_FOUND.png.sha1
@@ -0,0 +1 @@
+706c961acd1be5950110dad341bb3400efa9d6ed
\ No newline at end of file
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 3961578..b203d6f 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -110,6 +110,7 @@
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetTabViewBinder.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetView.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/AccessorySheetViewBinder.java",
+  "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/CreditCardAccessorySheetCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryData.java",
   "java/src/org/chromium/chrome/browser/autofill/keyboard_accessory/KeyboardAccessoryMediator.java",
@@ -162,7 +163,6 @@
   "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayDelegate.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayModel.java",
-  "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/AssistantOverlayState.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/overlay/TouchEventFilterView.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestCoordinator.java",
   "java/src/org/chromium/chrome/browser/autofill_assistant/payment/AssistantPaymentRequestDelegate.java",
@@ -1314,9 +1314,11 @@
   "java/src/org/chromium/chrome/browser/preferences/autofill/AutofillServerProfilePreferences.java",
   "java/src/org/chromium/chrome/browser/preferences/autofill/CreditCardNumberFormattingTextWatcher.java",
   "java/src/org/chromium/chrome/browser/preferences/autofill_assistant/AutofillAssistantPreferences.java",
+  "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionBrandingResourceProvider.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionDataUseItem.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionMainMenuItem.java",
-  "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferences.java",
+  "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreference.java",
+  "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPreferenceFragment.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoScreen.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionPromoUtils.java",
   "java/src/org/chromium/chrome/browser/preferences/datareduction/DataReductionProxyUma.java",
@@ -1591,6 +1593,15 @@
   "java/src/org/chromium/chrome/browser/tabmodel/document/StorageDelegate.java",
   "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_list_ui/TabGridCoordinator.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridContainerViewBinder.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewBinder.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabGridViewHolder.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListContainerProperties.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListMediator.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListModel.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabListRecyclerView.java",
+  "java/src/org/chromium/chrome/browser/tasks/tab_list_ui/TabProperties.java",
   "java/src/org/chromium/chrome/browser/toolbar/HomeButton.java",
   "java/src/org/chromium/chrome/browser/toolbar/IncognitoStateProvider.java",
   "java/src/org/chromium/chrome/browser/toolbar/IncognitoToggleTabLayout.java",
@@ -2205,6 +2216,7 @@
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShowTwiceTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTabTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestRule.java",
+  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUseStatsTest.java",
   "javatests/src/org/chromium/chrome/browser/permissions/GeolocationTest.java",
   "javatests/src/org/chromium/chrome/browser/permissions/MediaTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
index 7726e16..0dadb72 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/autofill_assistant/AutofillAssistantUiTest.java
@@ -141,7 +141,7 @@
         ThreadUtils.runOnUiThreadBlocking(
                 ()
                         -> assistantCoordinator.getModel().getOverlayModel().set(
-                                AssistantOverlayModel.STATE, AssistantOverlayState.full()));
+                                AssistantOverlayModel.STATE, AssistantOverlayState.FULL));
         View overlay = bottomSheet.findViewById(R.id.touch_event_filter);
         Assert.assertTrue(overlay.isShown());
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceTest.java
index ba4c9ce..8574f81 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/crash/MinidumpUploadServiceTest.java
@@ -31,6 +31,7 @@
 import org.chromium.components.minidump_uploader.CrashTestRule;
 import org.chromium.components.minidump_uploader.CrashTestRule.MockCrashReportingPermissionManager;
 import org.chromium.components.minidump_uploader.MinidumpUploadCallable;
+import org.chromium.components.minidump_uploader.MinidumpUploadCallable.MinidumpUploadStatus;
 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionManager;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
@@ -156,7 +157,7 @@
         List<CountedMinidumpUploadCallable> callables =
                 new ArrayList<CountedMinidumpUploadCallable>();
         callables.add(new CountedMinidumpUploadCallable(
-                "chromium_renderer-111.dmp1.try0", MinidumpUploadCallable.UPLOAD_SUCCESS, false));
+                "chromium_renderer-111.dmp1.try0", MinidumpUploadStatus.SUCCESS, false));
         runUploadCrashTest(callables);
     }
 
@@ -168,8 +169,8 @@
         final List<CountedMinidumpUploadCallable> callables =
                 new ArrayList<CountedMinidumpUploadCallable>();
         for (int i = 0; i < MinidumpUploadService.MAX_TRIES_ALLOWED; i++) {
-            callables.add(new CountedMinidumpUploadCallable("chromium_renderer-111.dmp1.try" + i,
-                    MinidumpUploadCallable.UPLOAD_FAILURE, true));
+            callables.add(new CountedMinidumpUploadCallable(
+                    "chromium_renderer-111.dmp1.try" + i, MinidumpUploadStatus.FAILURE, true));
         }
         runUploadCrashTest(callables);
     }
@@ -181,9 +182,9 @@
         List<CountedMinidumpUploadCallable> callables =
                 new ArrayList<CountedMinidumpUploadCallable>();
         callables.add(new CountedMinidumpUploadCallable(
-                "chromium_renderer-111.dmp1.try0", MinidumpUploadCallable.UPLOAD_FAILURE, true));
+                "chromium_renderer-111.dmp1.try0", MinidumpUploadStatus.FAILURE, true));
         callables.add(new CountedMinidumpUploadCallable(
-                "chromium_renderer-111.dmp1.try1", MinidumpUploadCallable.UPLOAD_SUCCESS, true));
+                "chromium_renderer-111.dmp1.try1", MinidumpUploadStatus.SUCCESS, true));
         runUploadCrashTest(callables);
     }
 
@@ -194,7 +195,7 @@
         List<CountedMinidumpUploadCallable> callables =
                 new ArrayList<CountedMinidumpUploadCallable>();
         callables.add(new CountedMinidumpUploadCallable(
-                "chromium_renderer-111.dmp1.try0", MinidumpUploadCallable.UPLOAD_FAILURE, false));
+                "chromium_renderer-111.dmp1.try0", MinidumpUploadStatus.FAILURE, false));
         runUploadCrashTest(callables);
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
index b6d445ff1..73336182 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
@@ -22,7 +22,6 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.omaha.MockRequestGenerator;
 import org.chromium.chrome.test.omaha.MockRequestGenerator.DeviceType;
-import org.chromium.chrome.test.omaha.MockRequestGenerator.SignedInStatus;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
@@ -88,8 +87,8 @@
 
         @Override
         protected RequestGenerator createRequestGenerator(Context context) {
-            mMockGenerator = new MockRequestGenerator(context,
-                    mIsOnTablet ? DeviceType.TABLET : DeviceType.HANDSET, SignedInStatus.FALSE);
+            mMockGenerator = new MockRequestGenerator(
+                    context, mIsOnTablet ? DeviceType.TABLET : DeviceType.HANDSET);
             return mMockGenerator;
         }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
index 1f8b308..08aa4cd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
@@ -22,7 +22,6 @@
 import org.chromium.chrome.test.omaha.AttributeFinder;
 import org.chromium.chrome.test.omaha.MockRequestGenerator;
 import org.chromium.chrome.test.omaha.MockRequestGenerator.DeviceType;
-import org.chromium.chrome.test.omaha.MockRequestGenerator.SignedInStatus;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.test.util.AccountHolder;
 import org.chromium.components.signin.test.util.FakeAccountManagerDelegate;
@@ -77,7 +76,7 @@
         UniqueIdentificationGeneratorFactory.clearGeneratorMapForTest();
 
         // Creating a RequestGenerator should register the identification generator.
-        new MockRequestGenerator(context, DeviceType.HANDSET, SignedInStatus.FALSE);
+        new MockRequestGenerator(context, DeviceType.HANDSET);
 
         // Verify the identification generator exists and is of the correct type.
         UniqueIdentificationGenerator instance = UniqueIdentificationGeneratorFactory.getInstance(
@@ -89,88 +88,49 @@
     @SmallTest
     @Feature({"Omaha"})
     public void testHandsetXMLCreationWithInstall() {
-        createAndCheckXML(DeviceType.HANDSET, SignedInStatus.FALSE, true);
+        createAndCheckXML(DeviceType.HANDSET, true);
     }
 
     @Test
     @SmallTest
     @Feature({"Omaha"})
     public void testHandsetXMLCreationWithoutInstall() {
-        createAndCheckXML(DeviceType.HANDSET, SignedInStatus.FALSE, false);
+        createAndCheckXML(DeviceType.HANDSET, false);
     }
 
     @Test
     @SmallTest
     @Feature({"Omaha"})
     public void testTabletXMLCreationWithInstall() {
-        createAndCheckXML(DeviceType.TABLET, SignedInStatus.FALSE, true);
+        createAndCheckXML(DeviceType.TABLET, true);
     }
 
     @Test
     @SmallTest
     @Feature({"Omaha"})
     public void testTabletXMLCreationWithoutInstall() {
-        createAndCheckXML(DeviceType.TABLET, SignedInStatus.FALSE, false);
+        createAndCheckXML(DeviceType.TABLET, false);
     }
 
     @Test
     @SmallTest
     @Feature({"Omaha"})
     public void testIsSignedIn() {
-        createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE, false);
+        createAndCheckXML(DeviceType.HANDSET, false);
     }
 
     @Test
     @SmallTest
     @Feature({"Omaha"})
     public void testIsNotSignedIn() {
-        createAndCheckXML(DeviceType.HANDSET, SignedInStatus.FALSE, false);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Omaha"})
-    public void testNoGoogleAccountsRetrieved() {
-        RequestGenerator generator =
-                createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE, false);
-        Assert.assertEquals(0, generator.getNumGoogleAccountsOnDevice());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Omaha"})
-    public void testOneGoogleAccountRetrieved() {
-        RequestGenerator generator = createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE,
-                false, new Account("clanktester@this.com", "com.google"));
-        Assert.assertEquals(1, generator.getNumGoogleAccountsOnDevice());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Omaha"})
-    public void testTwoGoogleAccountsRetrieved() {
-        RequestGenerator generator = createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE,
-                false, new Account("clanktester@gmail.com", "com.google"),
-                new Account("googleguy@elsewhere.com", "com.google"));
-        Assert.assertEquals(2, generator.getNumGoogleAccountsOnDevice());
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Omaha"})
-    public void testThreeGoogleAccountsExist() {
-        RequestGenerator generator = createAndCheckXML(DeviceType.HANDSET, SignedInStatus.TRUE,
-                false, new Account("clanktester@gmail.com", "com.google"),
-                new Account("googleguy@elsewhere.com", "com.google"),
-                new Account("ImInATest@gmail.com", "com.google"));
-        Assert.assertEquals(2, generator.getNumGoogleAccountsOnDevice());
+        createAndCheckXML(DeviceType.HANDSET, false);
     }
 
     /**
      * Checks that the XML is being created properly.
      */
-    private RequestGenerator createAndCheckXML(DeviceType deviceType, SignedInStatus signInStatus,
-            boolean sendInstallEvent, Account... accounts) {
+    private RequestGenerator createAndCheckXML(
+            DeviceType deviceType, boolean sendInstallEvent, Account... accounts) {
         Context targetContext = InstrumentationRegistry.getTargetContext();
         AdvancedMockContext context = new AdvancedMockContext(targetContext);
 
@@ -186,9 +146,7 @@
         String version = "1.2.3.4";
         long installAge = 42;
 
-        MockRequestGenerator generator =
-                new MockRequestGenerator(context, deviceType, signInStatus);
-
+        MockRequestGenerator generator = new MockRequestGenerator(context, deviceType);
         String xml = null;
         try {
             RequestData data = new RequestData(sendInstallEvent, 0, requestId, INSTALL_SOURCE);
@@ -226,12 +184,6 @@
 
         checkForAttributeAndValue(xml, "request", "userid", "{" + generator.getDeviceID() + "}");
 
-        checkForAttributeAndValue(xml, "app", "_numaccounts", "1");
-        checkForAttributeAndValue(xml, "app", "_numgoogleaccountsondevice",
-                String.valueOf(generator.getNumGoogleAccountsOnDevice()));
-        checkForAttributeAndValue(
-                xml, "app", "_numsignedin", String.valueOf(generator.getNumSignedIn()));
-
         return generator;
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryNoCardTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryNoCardTest.java
index 7c1949fe..0e61295 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryNoCardTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestCcCanMakePaymentQueryNoCardTest.java
@@ -48,10 +48,33 @@
     @Test
     @MediumTest
     @Feature({"Payments"})
-    public void testCannotMakePayment()
+    @CommandLineFlags.Add("disable-features=PaymentRequestHasEnrolledInstrument")
+    public void testLegacyCannotMakePayment()
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.openPageAndClickBuyAndWait(
                 mPaymentRequestTestRule.getCanMakePaymentQueryResponded());
         mPaymentRequestTestRule.expectResultContains(new String[] {"false"});
     }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    @CommandLineFlags.Add("enable-features=PaymentRequestHasEnrolledInstrument")
+    public void testCanMakePayment()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mPaymentRequestTestRule.openPageAndClickBuyAndWait(
+                mPaymentRequestTestRule.getCanMakePaymentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"true"});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    @CommandLineFlags.Add("enable-features=PaymentRequestHasEnrolledInstrument")
+    public void testHasEnrolledInstrument()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mPaymentRequestTestRule.openPageAndClickNodeAndWait("has-enrolled-instrument-visa",
+                mPaymentRequestTestRule.getHasEnrolledInstrumentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"false"});
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java
index e356f3891..5e5dc27 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppCanMakePaymentQueryTest.java
@@ -49,7 +49,8 @@
     @Test
     @MediumTest
     @Feature({"Payments"})
-    public void testNoBobPayInstalled()
+    @CommandLineFlags.Add("disable-features=PaymentRequestHasEnrolledInstrument")
+    public void testLegacyNoBobPayInstalled()
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.openPageAndClickBuyAndWait(
                 mPaymentRequestTestRule.getCanMakePaymentQueryResponded());
@@ -63,7 +64,8 @@
     @Test
     @MediumTest
     @Feature({"Payments"})
-    public void testBobPayInstalledLater()
+    @CommandLineFlags.Add("disable-features=PaymentRequestHasEnrolledInstrument")
+    public void testLegacyBobPayInstalledLater()
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.openPageAndClickBuyAndWait(
                 mPaymentRequestTestRule.getCanMakePaymentQueryResponded());
@@ -81,7 +83,28 @@
     @Test
     @MediumTest
     @Feature({"Payments"})
-    public void testNoInstrumentsInFastBobPay()
+    @CommandLineFlags.Add("enable-features=PaymentRequestHasEnrolledInstrument")
+    public void testBobPayInstalledLater()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // hasEnrolledInstrument returns false, since BobPay is not installed.
+        mPaymentRequestTestRule.openPageAndClickNodeAndWait("hasEnrolledInstrument",
+                mPaymentRequestTestRule.getHasEnrolledInstrumentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"false, false"});
+
+        mPaymentRequestTestRule.installPaymentApp(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
+        Thread.sleep(10000);
+
+        // hasEnrolledInstrument returns true now for BobPay, but still returns false for AlicePay.
+        mPaymentRequestTestRule.clickNodeAndWait("hasEnrolledInstrument",
+                mPaymentRequestTestRule.getHasEnrolledInstrumentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"true, false"});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    @CommandLineFlags.Add("disable-features=PaymentRequestHasEnrolledInstrument")
+    public void testLegacyNoInstrumentsInFastBobPay()
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.installPaymentApp(NO_INSTRUMENTS, IMMEDIATE_RESPONSE);
         mPaymentRequestTestRule.openPageAndClickBuyAndWait(
@@ -96,7 +119,28 @@
     @Test
     @MediumTest
     @Feature({"Payments"})
-    public void testNoInstrumentsInSlowBobPay()
+    @CommandLineFlags.Add("enable-features=PaymentRequestHasEnrolledInstrument")
+    public void testNoInstrumentsInFastBobPay()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        mPaymentRequestTestRule.installPaymentApp(NO_INSTRUMENTS, IMMEDIATE_RESPONSE);
+
+        // canMakePayment returns true for BobPay and false for AlicePay.
+        mPaymentRequestTestRule.openPageAndClickNodeAndWait(
+                "otherBuy", mPaymentRequestTestRule.getCanMakePaymentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"true, false"});
+
+        // hasEnrolledInstrument returns false for BobPay (installed but no instrument) and
+        // false for AlicePay (not installed).
+        mPaymentRequestTestRule.clickNodeAndWait("hasEnrolledInstrument",
+                mPaymentRequestTestRule.getHasEnrolledInstrumentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"false, false"});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    @CommandLineFlags.Add("disable-features=PaymentRequestHasEnrolledInstrument")
+    public void testLegacyNoInstrumentsInSlowBobPay()
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.installPaymentApp(NO_INSTRUMENTS, DELAYED_RESPONSE);
         mPaymentRequestTestRule.openPageAndClickBuyAndWait(
@@ -111,7 +155,29 @@
     @Test
     @MediumTest
     @Feature({"Payments"})
-    public void testPayViaFastBobPay()
+    @CommandLineFlags.Add("enable-features=PaymentRequestHasEnrolledInstrument")
+    public void testNoInstrumentsInSlowBobPay()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // Install BobPay.
+        mPaymentRequestTestRule.installPaymentApp(NO_INSTRUMENTS, DELAYED_RESPONSE);
+
+        // canMakePayment returns true for BobPay and false for AlicePay.
+        mPaymentRequestTestRule.openPageAndClickNodeAndWait(
+                "otherBuy", mPaymentRequestTestRule.getCanMakePaymentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"true, false"});
+
+        // hasEnrolledInstrument returns false for BobPay (installed but no instrument) and
+        // false for AlicePay (not installed).
+        mPaymentRequestTestRule.clickNodeAndWait("hasEnrolledInstrument",
+                mPaymentRequestTestRule.getHasEnrolledInstrumentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"false, false"});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    @CommandLineFlags.Add("disable-features=PaymentRequestHasEnrolledInstrument")
+    public void testLegacyPayViaFastBobPay()
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.installPaymentApp(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
         mPaymentRequestTestRule.openPageAndClickBuyAndWait(
@@ -126,7 +192,28 @@
     @Test
     @MediumTest
     @Feature({"Payments"})
-    public void testPayViaSlowBobPay()
+    @CommandLineFlags.Add("enable-features=PaymentRequestHasEnrolledInstrument")
+    public void testPayViaFastBobPay()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // Install BobPay.
+        mPaymentRequestTestRule.installPaymentApp(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
+
+        // canMakePayment returns true for BobPay and false for AlicePay.
+        mPaymentRequestTestRule.openPageAndClickNodeAndWait(
+                "otherBuy", mPaymentRequestTestRule.getCanMakePaymentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"true, false"});
+
+        // hasEnrolledInstrument returns true for BobPay and false for AlicePay.
+        mPaymentRequestTestRule.clickNodeAndWait("hasEnrolledInstrument",
+                mPaymentRequestTestRule.getHasEnrolledInstrumentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"true, false"});
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    @CommandLineFlags.Add("disable-features=PaymentRequestHasEnrolledInstrument")
+    public void testLegacyPayViaSlowBobPay()
             throws InterruptedException, ExecutionException, TimeoutException {
         mPaymentRequestTestRule.installPaymentApp(HAVE_INSTRUMENTS, DELAYED_RESPONSE);
         mPaymentRequestTestRule.openPageAndClickBuyAndWait(
@@ -137,4 +224,24 @@
                 "otherBuy", mPaymentRequestTestRule.getCanMakePaymentQueryResponded());
         mPaymentRequestTestRule.expectResultContains(new String[] {"true, false"});
     }
+
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    @CommandLineFlags.Add("enable-features=PaymentRequestHasEnrolledInstrument")
+    public void testPayViaSlowBobPay()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        // Install BobPay.
+        mPaymentRequestTestRule.installPaymentApp(HAVE_INSTRUMENTS, DELAYED_RESPONSE);
+
+        // canMakePayment returns true for BobPay and false for AlicePay.
+        mPaymentRequestTestRule.openPageAndClickNodeAndWait(
+                "otherBuy", mPaymentRequestTestRule.getCanMakePaymentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"true, false"});
+
+        // hasEnrolledInstrument returns true for BobPay and false for AlicePay.
+        mPaymentRequestTestRule.clickNodeAndWait("hasEnrolledInstrument",
+                mPaymentRequestTestRule.getHasEnrolledInstrumentQueryResponded());
+        mPaymentRequestTestRule.expectResultContains(new String[] {"true, false"});
+    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java
new file mode 100644
index 0000000..5551c7d
--- /dev/null
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestUpdateWithTest.java
@@ -0,0 +1,92 @@
+// 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.payments;
+
+import android.support.test.filters.MediumTest;
+
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.util.CommandLineFlags;
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.R;
+import org.chromium.chrome.browser.ChromeSwitches;
+import org.chromium.chrome.browser.autofill.AutofillTestHelper;
+import org.chromium.chrome.browser.autofill.CardType;
+import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
+import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
+import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
+import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.ui.modaldialog.ModalDialogProperties;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * A payment integration test for updateWith().
+ */
+@RunWith(ChromeJUnit4ClassRunner.class)
+@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
+public class PaymentRequestUpdateWithTest implements MainActivityStartCallback {
+    @Rule
+    public PaymentRequestTestRule mRule =
+            new PaymentRequestTestRule("payment_request_update_with_test.html", this);
+
+    @Override
+    public void onMainActivityStarted()
+            throws InterruptedException, ExecutionException, TimeoutException {
+        AutofillTestHelper helper = new AutofillTestHelper();
+        helper.setProfile(new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */,
+                "Lisa Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "", "90210",
+                "", "US", "555 123-4567", "lisa@simpson.com", ""));
+        String billingAddressId = helper.setProfile(
+                new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */,
+                        "Maggie Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "",
+                        "90210", "", "Uzbekistan", "555 123-4567", "maggie@simpson.com", ""));
+        helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe",
+                "4111111111111111", "1111", "12", "2050", "visa", R.drawable.visa_card,
+                CardType.UNKNOWN, billingAddressId, "" /* serverId */));
+    }
+
+    /**
+     * A merchant that calls updateWith() without shipping options will not cause timeouts in UI.
+     */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUpdateWithNoShippingOptions() throws Throwable {
+        mRule.triggerUIAndWait("updateWithNoShippingOptions", mRule.getReadyForInput());
+        Assert.assertEquals("USD $5.00", mRule.getOrderSummaryTotal());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
+        Assert.assertEquals("USD $10.00", mRule.getOrderSummaryTotal());
+        mRule.clickAndWait(R.id.button_primary, mRule.getReadyForUnmaskInput());
+        mRule.setTextInCardUnmaskDialogAndWait(
+                R.id.card_unmask_input, "123", mRule.getReadyToUnmask());
+        mRule.clickCardUnmaskButtonAndWait(
+                ModalDialogProperties.ButtonType.POSITIVE, mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"freeShipping"});
+    }
+
+    /** A merchant that calls updateWith() with shipping options will not cause timeouts in UI. */
+    @Test
+    @MediumTest
+    @Feature({"Payments"})
+    public void testUpdateWithShippingOptions() throws Throwable {
+        mRule.triggerUIAndWait("updateWithShippingOptions", mRule.getReadyForInput());
+        Assert.assertEquals("USD $5.00", mRule.getOrderSummaryTotal());
+        mRule.clickInShippingAddressAndWait(R.id.payments_section, mRule.getReadyForInput());
+        mRule.clickOnShippingAddressSuggestionOptionAndWait(1, mRule.getReadyForInput());
+        Assert.assertEquals("USD $10.00", mRule.getOrderSummaryTotal());
+        mRule.clickAndWait(R.id.button_primary, mRule.getReadyForUnmaskInput());
+        mRule.setTextInCardUnmaskDialogAndWait(
+                R.id.card_unmask_input, "123", mRule.getReadyToUnmask());
+        mRule.clickCardUnmaskButtonAndWait(
+                ModalDialogProperties.ButtonType.POSITIVE, mRule.getDismissed());
+        mRule.expectResultContains(new String[] {"updatedShipping"});
+    }
+}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java
index 305597c..8b80fa0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/webapps/WebappSplashScreenThemeColorTest.java
@@ -68,17 +68,27 @@
     public void testThemeColorNotUsedIfPagesHasOne() {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return;
 
+        // Depending on the Android version, the status bar color will either be the same as the
+        // theme color or darker.
+        final int baseColor = Color.GREEN;
+        final int finalColor;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            finalColor = Color.GREEN;
+        } else {
+            finalColor = ColorUtils.getDarkenedColorForStatusBar(Color.GREEN);
+        }
+
         ThreadUtils.runOnUiThread(new Runnable() {
             @Override
             public void run() {
                 TabTestUtils.simulateChangeThemeColor(
-                        mActivityTestRule.getActivity().getActivityTab(), Color.GREEN);
+                        mActivityTestRule.getActivity().getActivityTab(), baseColor);
             }
         });
 
         // Waits for theme-color to change so the test doesn't rely on system timing.
-        CriteriaHelper.pollInstrumentationThread(Criteria.equals(
-                ColorUtils.getDarkenedColorForStatusBar(Color.GREEN), new Callable<Integer>() {
+        CriteriaHelper.pollInstrumentationThread(
+                Criteria.equals(finalColor, new Callable<Integer>() {
                     @Override
                     public Integer call() {
                         return mActivityTestRule.getActivity().getWindow().getStatusBarColor();
diff --git a/chrome/android/modules/ar/ar_module_tmpl.gni b/chrome/android/modules/ar/ar_module_tmpl.gni
index 1dced61..0a2e3a21 100644
--- a/chrome/android/modules/ar/ar_module_tmpl.gni
+++ b/chrome/android/modules/ar/ar_module_tmpl.gni
@@ -41,21 +41,30 @@
       "//third_party/android_deps:com_google_ar_core_java",
     ]
 
-    # We only want to add the 32 bit arcore shim even for 64 bit monochrome
-    # builds. AR is never used in 64 bit mode. We store the arcore shim as a
-    # separate .so in the bundle module and only load as needed.
-    if (android_64bit_target_cpu && build_apk_secondary_abi) {
-      secondary_abi_loadable_modules = [ "$root_gen_dir/third_party/android_deps/com_google_ar_core_java/jni/armeabi-v7a/libarcore_sdk_c.so" ]
+    # We store the arcore shim as a separate .so in the bundle module and only
+    # load as needed.
+    if (android_64bit_target_cpu) {
+      if (defined(invoker.is_64_bit_browser) && invoker.is_64_bit_browser) {
+        loadable_modules = [ "$root_gen_dir/third_party/android_deps/com_google_ar_core_java/jni/arm64-v8a/libarcore_sdk_c.so" ]
 
-      # Disguise 32 bit library as 64 bit. This works around a Play Core bug where only 64 bit
-      # libraries are extracted on 64 bit devices.
-      # TODO(crbug.com/902859): Remove once bug is fixed.
-      loadable_modules = [ "$root_gen_dir/third_party/android_deps/com_google_ar_core_java/jni/armeabi-v7a/libarcore_sdk_c.so" ]
-    } else if (android_64bit_target_cpu && !build_apk_secondary_abi) {
-      loadable_modules = [ "$root_gen_dir/third_party/android_deps/com_google_ar_core_java/jni/armeabi-v7a/libarcore_sdk_c.so" ]
+        # TODO(cjgrant): Make this a dummy lib, if SplitCompat properly opens the 64-bit library.
+        secondary_abi_loadable_modules = [ "$root_gen_dir/third_party/android_deps/com_google_ar_core_java/jni/arm64-v8a/libarcore_sdk_c.so" ]
+      } else {
+        if (build_apk_secondary_abi) {
+          secondary_abi_loadable_modules = [ "$root_gen_dir/third_party/android_deps/com_google_ar_core_java/jni/armeabi-v7a/libarcore_sdk_c.so" ]
+
+          # Disguise 32 bit library as 64 bit. This works around a Play Core bug where only 64 bit
+          # libraries are extracted on 64 bit devices.
+          # TODO(crbug.com/902859): Remove once bug is fixed.
+          loadable_modules = [ "$root_gen_dir/third_party/android_deps/com_google_ar_core_java/jni/armeabi-v7a/libarcore_sdk_c.so" ]
+        } else {
+          loadable_modules = [ "$root_gen_dir/third_party/android_deps/com_google_ar_core_java/jni/armeabi-v7a/libarcore_sdk_c.so" ]
+        }
+      }
     } else {
       loadable_modules = [ "$root_gen_dir/third_party/android_deps/com_google_ar_core_java/jni/armeabi-v7a/libarcore_sdk_c.so" ]
     }
+
     uncompress_shared_libraries = true
 
     # Don't embed more localized strings than required (http://crbug.com/932017)
diff --git a/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkVerifySignatureTest.java b/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkVerifySignatureTest.java
index 3d19f55c..4c12f28 100644
--- a/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkVerifySignatureTest.java
+++ b/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkVerifySignatureTest.java
@@ -7,15 +7,6 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 
-import static org.chromium.webapk.lib.client.WebApkVerifySignature.ERROR_BAD_APK;
-import static org.chromium.webapk.lib.client.WebApkVerifySignature.ERROR_BAD_BLANK_SPACE;
-import static org.chromium.webapk.lib.client.WebApkVerifySignature.ERROR_BAD_V2_SIGNING_BLOCK;
-import static org.chromium.webapk.lib.client.WebApkVerifySignature.ERROR_EXTRA_FIELD_TOO_LARGE;
-import static org.chromium.webapk.lib.client.WebApkVerifySignature.ERROR_INCORRECT_SIGNATURE;
-import static org.chromium.webapk.lib.client.WebApkVerifySignature.ERROR_OK;
-import static org.chromium.webapk.lib.client.WebApkVerifySignature.ERROR_SIGNATURE_NOT_FOUND;
-import static org.chromium.webapk.lib.client.WebApkVerifySignature.ERROR_TOO_MANY_META_INF_FILES;
-
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -23,6 +14,7 @@
 
 import org.chromium.testing.local.LocalRobolectricTestRunner;
 import org.chromium.testing.local.TestDir;
+import org.chromium.webapk.lib.client.WebApkVerifySignature.Error;
 
 import java.io.RandomAccessFile;
 import java.nio.MappedByteBuffer;
@@ -93,31 +85,31 @@
     public void testBadVerifyFiles() throws Exception {
         PublicKey pub = readPublicKey(testFilePath("public.der"));
         FileResult[] tests = {
-                new FileResult("example.apk", ERROR_OK),
-                new FileResult("java-example.apk", ERROR_OK),
-                new FileResult("v2-signed-ok.apk", ERROR_OK),
-                new FileResult("bad-sig.apk", ERROR_INCORRECT_SIGNATURE),
-                new FileResult("bad-utf8-fname.apk", ERROR_INCORRECT_SIGNATURE),
-                new FileResult("empty.apk", ERROR_BAD_APK),
-                new FileResult("extra-field-too-large.apk", ERROR_EXTRA_FIELD_TOO_LARGE),
-                new FileResult("extra-len-too-large.apk", ERROR_BAD_APK),
-                new FileResult("no-cd.apk", ERROR_BAD_APK),
-                new FileResult("no-comment.apk", ERROR_SIGNATURE_NOT_FOUND),
-                new FileResult("no-eocd.apk", ERROR_BAD_APK),
-                new FileResult("no-lfh.apk", ERROR_BAD_APK),
-                new FileResult("not-an.apk", ERROR_BAD_APK),
-                new FileResult("too-many-metainf.apk", ERROR_TOO_MANY_META_INF_FILES),
-                new FileResult("truncated.apk", ERROR_BAD_APK),
-                new FileResult("zeros.apk", ERROR_BAD_APK),
-                new FileResult("zeros-at-end.apk", ERROR_BAD_APK),
-                new FileResult("block-before-first.apk", ERROR_BAD_BLANK_SPACE),
-                new FileResult("block-at-end.apk", ERROR_BAD_BLANK_SPACE),
-                new FileResult("block-before-eocd.apk", ERROR_BAD_BLANK_SPACE),
-                new FileResult("block-before-cd.apk", ERROR_BAD_BLANK_SPACE),
-                new FileResult("block-middle.apk", ERROR_BAD_BLANK_SPACE),
-                new FileResult("v2-signed-too-large.apk", ERROR_BAD_V2_SIGNING_BLOCK),
-                // This badly fuzzed file should return ERROR_FILE_COMMENT_TOO_LARGE.
-                new FileResult("fcomment-too-large.apk", ERROR_BAD_APK),
+                new FileResult("example.apk", Error.OK),
+                new FileResult("java-example.apk", Error.OK),
+                new FileResult("v2-signed-ok.apk", Error.OK),
+                new FileResult("bad-sig.apk", Error.INCORRECT_SIGNATURE),
+                new FileResult("bad-utf8-fname.apk", Error.INCORRECT_SIGNATURE),
+                new FileResult("empty.apk", Error.BAD_APK),
+                new FileResult("extra-field-too-large.apk", Error.EXTRA_FIELD_TOO_LARGE),
+                new FileResult("extra-len-too-large.apk", Error.BAD_APK),
+                new FileResult("no-cd.apk", Error.BAD_APK),
+                new FileResult("no-comment.apk", Error.SIGNATURE_NOT_FOUND),
+                new FileResult("no-eocd.apk", Error.BAD_APK),
+                new FileResult("no-lfh.apk", Error.BAD_APK),
+                new FileResult("not-an.apk", Error.BAD_APK),
+                new FileResult("too-many-metainf.apk", Error.TOO_MANY_META_INF_FILES),
+                new FileResult("truncated.apk", Error.BAD_APK),
+                new FileResult("zeros.apk", Error.BAD_APK),
+                new FileResult("zeros-at-end.apk", Error.BAD_APK),
+                new FileResult("block-before-first.apk", Error.BAD_BLANK_SPACE),
+                new FileResult("block-at-end.apk", Error.BAD_BLANK_SPACE),
+                new FileResult("block-before-eocd.apk", Error.BAD_BLANK_SPACE),
+                new FileResult("block-before-cd.apk", Error.BAD_BLANK_SPACE),
+                new FileResult("block-middle.apk", Error.BAD_BLANK_SPACE),
+                new FileResult("v2-signed-too-large.apk", Error.BAD_V2_SIGNING_BLOCK),
+                // This badly fuzzed file should return Error.FILE_COMMENT_TOO_LARGE.
+                new FileResult("fcomment-too-large.apk", Error.BAD_APK),
         };
         for (FileResult test : tests) {
             RandomAccessFile file = new RandomAccessFile(testFilePath(test.filename), "r");
@@ -127,8 +119,9 @@
             buf.load();
             WebApkVerifySignature v = new WebApkVerifySignature(buf);
             try {
+                @WebApkVerifySignature.Error
                 int readError = v.read();
-                if (readError == WebApkVerifySignature.ERROR_OK) {
+                if (readError == WebApkVerifySignature.Error.OK) {
                     assertEquals(test.filename, test.want, v.verifySignature(pub));
                 } else {
                     assertEquals(test.filename, test.want, readError);
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkValidator.java b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkValidator.java
index 64fbabe..00370144 100644
--- a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkValidator.java
+++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkValidator.java
@@ -306,8 +306,9 @@
             buf.load();
 
             WebApkVerifySignature v = new WebApkVerifySignature(buf);
+            @WebApkVerifySignature.Error
             int result = v.read();
-            if (result != WebApkVerifySignature.ERROR_OK) {
+            if (result != WebApkVerifySignature.Error.OK) {
                 Log.e(TAG, String.format("Failure reading %s: %s", packageFilename, result));
                 return false;
             }
@@ -317,7 +318,7 @@
             if (DEBUG) {
                 Log.d(TAG, "File " + packageFilename + ": " + result);
             }
-            return result == WebApkVerifySignature.ERROR_OK;
+            return result == WebApkVerifySignature.Error.OK;
         } catch (Exception e) {
             Log.e(TAG, "WebApk file error for file " + packageFilename, e);
             return false;
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVerifySignature.java b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVerifySignature.java
index ac27fac2c..62f2a23 100644
--- a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVerifySignature.java
+++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVerifySignature.java
@@ -9,6 +9,8 @@
 import android.support.annotation.IntDef;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.ByteBuffer;
 import java.security.PublicKey;
 import java.security.Signature;
@@ -24,22 +26,22 @@
  */
 public class WebApkVerifySignature {
     /** Errors codes. */
-    @IntDef({
-            ERROR_OK, ERROR_BAD_APK, ERROR_EXTRA_FIELD_TOO_LARGE, ERROR_FILE_COMMENT_TOO_LARGE,
-            ERROR_INCORRECT_SIGNATURE, ERROR_SIGNATURE_NOT_FOUND, ERROR_TOO_MANY_META_INF_FILES,
-            ERROR_BAD_BLANK_SPACE, ERROR_BAD_V2_SIGNING_BLOCK,
-    })
+    @IntDef({Error.OK, Error.BAD_APK, Error.EXTRA_FIELD_TOO_LARGE, Error.FILE_COMMENT_TOO_LARGE,
+            Error.INCORRECT_SIGNATURE, Error.SIGNATURE_NOT_FOUND, Error.TOO_MANY_META_INF_FILES,
+            Error.BAD_BLANK_SPACE, Error.BAD_V2_SIGNING_BLOCK})
     @SuppressWarnings("JavaLangClash")
-    public @interface Error {}
-    public static final int ERROR_OK = 0;
-    public static final int ERROR_BAD_APK = 1;
-    public static final int ERROR_EXTRA_FIELD_TOO_LARGE = 2;
-    public static final int ERROR_FILE_COMMENT_TOO_LARGE = 3;
-    public static final int ERROR_INCORRECT_SIGNATURE = 4;
-    public static final int ERROR_SIGNATURE_NOT_FOUND = 5;
-    public static final int ERROR_TOO_MANY_META_INF_FILES = 6;
-    public static final int ERROR_BAD_BLANK_SPACE = 7;
-    public static final int ERROR_BAD_V2_SIGNING_BLOCK = 8;
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Error {
+        int OK = 0;
+        int BAD_APK = 1;
+        int EXTRA_FIELD_TOO_LARGE = 2;
+        int FILE_COMMENT_TOO_LARGE = 3;
+        int INCORRECT_SIGNATURE = 4;
+        int SIGNATURE_NOT_FOUND = 5;
+        int TOO_MANY_META_INF_FILES = 6;
+        int BAD_BLANK_SPACE = 7;
+        int BAD_V2_SIGNING_BLOCK = 8;
+    }
 
     private static final String TAG = "WebApkVerifySignature";
 
@@ -162,47 +164,48 @@
      * set.
      * @return OK on success.
      */
-    public int read() {
+    public @Error int read() {
         try {
+            @Error
             int err = readEOCD();
-            if (err != ERROR_OK) {
+            if (err != Error.OK) {
                 return err;
             }
             // Short circuit if no comment found.
             if (parseCommentSignature(mComment) == null) {
-                return ERROR_SIGNATURE_NOT_FOUND;
+                return Error.SIGNATURE_NOT_FOUND;
             }
             err = readDirectory();
-            if (err != ERROR_OK) {
+            if (err != Error.OK) {
                 return err;
             }
         } catch (Exception e) {
-            return ERROR_BAD_APK;
+            return Error.BAD_APK;
         }
-        return ERROR_OK;
+        return Error.OK;
     }
 
     /**
      * verifySignature hashes all the files and then verifies the signature.
      * @param pub The public key that it should be verified against.
-     * @return ERROR_OK if the public key signature verifies.
+     * @return Error.OK if the public key signature verifies.
      */
-    public int verifySignature(PublicKey pub) {
+    public @Error int verifySignature(PublicKey pub) {
         byte[] sig = parseCommentSignature(mComment);
         if (sig == null || sig.length == 0) {
-            return ERROR_SIGNATURE_NOT_FOUND;
+            return Error.SIGNATURE_NOT_FOUND;
         }
         try {
             Signature signature = Signature.getInstance(SIGNING_ALGORITHM);
             signature.initVerify(pub);
             int err = calculateHash(signature);
-            if (err != ERROR_OK) {
+            if (err != Error.OK) {
                 return err;
             }
-            return signature.verify(sig) ? ERROR_OK : ERROR_INCORRECT_SIGNATURE;
+            return signature.verify(sig) ? Error.OK : Error.INCORRECT_SIGNATURE;
         } catch (Exception e) {
             Log.e(TAG, "Exception calculating signature", e);
-            return ERROR_INCORRECT_SIGNATURE;
+            return Error.INCORRECT_SIGNATURE;
         }
     }
 
@@ -211,7 +214,7 @@
      * cryptographic hash.
      * @param sig Signature object you can call update on.
      */
-    public int calculateHash(Signature sig) throws Exception {
+    public @Error int calculateHash(Signature sig) throws Exception {
         Collections.sort(mBlocks);
         int metaInfCount = 0;
         for (Block block : mBlocks) {
@@ -219,7 +222,7 @@
                 metaInfCount++;
                 if (metaInfCount > MAX_META_INF_FILES) {
                     // TODO(scottkirkwood): Add whitelist of files.
-                    return ERROR_TOO_MANY_META_INF_FILES;
+                    return Error.TOO_MANY_META_INF_FILES;
                 }
 
                 // Files that begin with META-INF/ are not part of the hash.
@@ -241,7 +244,7 @@
             slice.limit(block.mCompressedSize);
             sig.update(slice);
         }
-        return ERROR_OK;
+        return Error.OK;
     }
 
     /**
@@ -273,12 +276,12 @@
 
     /**
      * Reads the End of Central Directory Record.
-     * @return ERROR_OK on success.
+     * @return Error.OK on success.
      */
-    private int readEOCD() {
+    private @Error int readEOCD() {
         int start = findEOCDStart();
         if (start < 0) {
-            return ERROR_BAD_APK;
+            return Error.BAD_APK;
         }
         mEndOfCentralDirOffset = start;
 
@@ -291,14 +294,14 @@
         mComment = readString(commentLength);
         if (mBuffer.position() < mBuffer.limit()) {
             // We should have read every byte to the end of the file by this time.
-            return ERROR_BAD_BLANK_SPACE;
+            return Error.BAD_BLANK_SPACE;
         }
-        return ERROR_OK;
+        return Error.OK;
     }
 
     /**
      * Reads the central directory and populates {@link mBlocks} with data about each entry.
-     * @return ERROR_OK on success.
+     * @return Error.OK on success.
      */
     @Error
     int readDirectory() {
@@ -308,7 +311,7 @@
             int signature = read4();
             if (signature != CD_SIG) {
                 Log.d(TAG, "Missing Central Directory Signature");
-                return ERROR_BAD_APK;
+                return Error.BAD_APK;
             }
             // CreatorVersion(2), ReaderVersion(2), Flags(2), CompressionMethod(2)
             // ModifiedTime(2), ModifiedDate(2), CRC32(4) = 16 bytes
@@ -323,17 +326,17 @@
             String filename = readString(fileNameLength);
             seekDelta(extraLen + fileCommentLength);
             if (extraLen > MAX_EXTRA_LENGTH) {
-                return ERROR_EXTRA_FIELD_TOO_LARGE;
+                return Error.EXTRA_FIELD_TOO_LARGE;
             }
             if (fileCommentLength > MAX_FILE_COMMENT_LENGTH) {
-                return ERROR_FILE_COMMENT_TOO_LARGE;
+                return Error.FILE_COMMENT_TOO_LARGE;
             }
             mBlocks.add(new Block(filename, offset, compressedSize));
         }
 
         if (mBuffer.position() != mEndOfCentralDirOffset) {
             // At this point we should be exactly at the EOCD start.
-            return ERROR_BAD_BLANK_SPACE;
+            return Error.BAD_BLANK_SPACE;
         }
 
         // We need blocks to be sorted by position at this point.
@@ -343,14 +346,14 @@
         // Read the 'local file header' block to the size of the header in bytes.
         for (Block block : mBlocks) {
             if (block.mPosition != lastByte) {
-                return ERROR_BAD_BLANK_SPACE;
+                return Error.BAD_BLANK_SPACE;
             }
 
             seek(block.mPosition);
             int signature = read4();
             if (signature != LFH_SIG) {
                 Log.d(TAG, "LFH Signature missing");
-                return ERROR_BAD_APK;
+                return Error.BAD_APK;
             }
             // ReaderVersion(2)
             seekDelta(2);
@@ -361,7 +364,7 @@
             int fileNameLength = read2();
             int extraFieldLength = read2();
             if (extraFieldLength > MAX_EXTRA_LENGTH) {
-                return ERROR_EXTRA_FIELD_TOO_LARGE;
+                return Error.EXTRA_FIELD_TOO_LARGE;
             }
 
             block.mHeaderSize =
@@ -388,13 +391,13 @@
                 // Only if we have a v2 signature do we allow medium sized gap between the last
                 // block and the start of the central directory.
                 if (mCentralDirOffset - lastByte > MAX_V2_SIGNING_BLOCK_SIZE) {
-                    return ERROR_BAD_V2_SIGNING_BLOCK;
+                    return Error.BAD_V2_SIGNING_BLOCK;
                 }
             } else {
-                return ERROR_BAD_BLANK_SPACE;
+                return Error.BAD_BLANK_SPACE;
             }
         }
-        return ERROR_OK;
+        return Error.OK;
     }
 
     /**
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 1597b97..39cfe2b 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -439,6 +439,10 @@
 
   if (is_chromeos) {
     deps += [
+      "//ash/components/shortcut_viewer/public/cpp:manifest",
+      "//ash/components/shortcut_viewer/public/mojom",
+      "//ash/components/tap_visualizer/public/cpp:manifest",
+      "//ash/components/tap_visualizer/public/mojom",
       "//chromeos/assistant:buildflags",
       "//chromeos/services/device_sync:manifest",
       "//chromeos/services/ime/public/mojom",
@@ -589,9 +593,9 @@
 
   if (is_chromeos) {
     deps += [
-      "//ash/components/quick_launch:manifest",
-      "//ash/components/shortcut_viewer:manifest",
-      "//ash/components/tap_visualizer:manifest",
+      "//ash/components/quick_launch/public/cpp:manifest",
+      "//ash/components/shortcut_viewer/public/cpp:manifest",
+      "//ash/components/tap_visualizer/public/cpp:manifest",
       "//ash/public/cpp:manifest",
       "//chrome/browser/chromeos:ash_pref_connector_manifest",
       "//chrome/services/cups_ipp_parser:manifest",
diff --git a/chrome/app/DEPS b/chrome/app/DEPS
index deb5eee..a5833e3d 100644
--- a/chrome/app/DEPS
+++ b/chrome/app/DEPS
@@ -44,6 +44,8 @@
 
 specific_include_rules = {
   "chrome_content_browser_overlay_manifest\.cc": [
+    "+ash/components/shortcut_viewer/public",
+    "+ash/components/tap_visualizer/public",
     "+chrome/services/app_service",
     "+chromeos/assistant",
     "+chromeos/services/assistant",
@@ -78,9 +80,9 @@
     "+third_party/blink/public/mojom",
   ],
   "chrome_packaged_service_manifests\.cc": [
-    "+ash/components/quick_launch",
-    "+ash/components/shortcut_viewer",
-    "+ash/components/tap_visualizer",
+    "+ash/components/quick_launch/public",
+    "+ash/components/shortcut_viewer/public",
+    "+ash/components/tap_visualizer/public",
     "+chrome/services/cups_ipp_parser",
     "+chrome/services/file_util",
     "+chrome/services/isolated_xr_device",
diff --git a/chrome/app/chrome_command_ids.h b/chrome/app/chrome_command_ids.h
index a39edee..950f9fe 100644
--- a/chrome/app/chrome_command_ids.h
+++ b/chrome/app/chrome_command_ids.h
@@ -244,7 +244,7 @@
 #define IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB 50113
 #define IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE 50114
 #define IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB 50115
-#define IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE 50116
+#define IDC_CONTENT_CONTEXT_LOAD_IMAGE 50116
 // Audio/video items.
 #define IDC_CONTENT_CONTEXT_SAVEAVAS 50120
 #define IDC_CONTENT_CONTEXT_COPYAVLOCATION 50121
diff --git a/chrome/app/chrome_content_browser_overlay_manifest.cc b/chrome/app/chrome_content_browser_overlay_manifest.cc
index f4e6f24..6e506e4 100644
--- a/chrome/app/chrome_content_browser_overlay_manifest.cc
+++ b/chrome/app/chrome_content_browser_overlay_manifest.cc
@@ -50,6 +50,10 @@
 #include "third_party/blink/public/platform/modules/webshare/webshare.mojom.h"
 
 #if defined(OS_CHROMEOS)
+#include "ash/components/shortcut_viewer/public/cpp/manifest.h"  // nogncheck
+#include "ash/components/shortcut_viewer/public/mojom/shortcut_viewer.mojom.h"  // nogncheck
+#include "ash/components/tap_visualizer/public/cpp/manifest.h"  // nogncheck
+#include "ash/components/tap_visualizer/public/mojom/tap_visualizer.mojom.h"  // nogncheck
 #include "chromeos/assistant/buildflags.h"  // nogncheck
 #include "chromeos/services/device_sync/manifest.h"
 #include "chromeos/services/ime/public/mojom/input_engine.mojom.h"
@@ -170,8 +174,6 @@
                                "removable_storage_writer")
             .RequireCapability("resource_coordinator", "webui_graph_dump")
             .RequireCapability("secure_channel", "secure_channel")
-            .RequireCapability("shortcut_viewer_app", "shortcut_viewer")
-            .RequireCapability("tap_visualizer_app", "tap_visualizer")
             .RequireCapability("ui", "ime_registrar")
             .RequireCapability("ui", "input_device_controller")
             .RequireCapability("ui", "window_manager")
@@ -181,6 +183,10 @@
             .RequireCapability("xr_device_service", "xr_device_provider")
             .RequireCapability("xr_device_service", "xr_device_test_hook")
 #if defined(OS_CHROMEOS)
+            .RequireCapability(shortcut_viewer::mojom::kServiceName,
+                               shortcut_viewer::mojom::kToggleUiCapability)
+            .RequireCapability(tap_visualizer::mojom::kServiceName,
+                               tap_visualizer::mojom::kShowUiCapability)
             .ExposeInterfaceFilterCapability_Deprecated(
                 "navigation:frame", "multidevice_setup",
                 service_manager::Manifest::InterfaceList<
@@ -224,8 +230,8 @@
                     // WebUI-only interfaces go below this line. These should be
                     // brokered through a dedicated interface, but they're here
                     // for for now.
+                    downloads::mojom::PageHandlerFactory,
                     feed_internals::mojom::PageHandler,
-                    md_downloads::mojom::PageHandlerFactory,
 #if defined(OS_ANDROID)
                     eoc_internals::mojom::PageHandler,
 #else
diff --git a/chrome/app/chrome_main_delegate.cc b/chrome/app/chrome_main_delegate.cc
index ea426fa..dd3df67d 100644
--- a/chrome/app/chrome_main_delegate.cc
+++ b/chrome/app/chrome_main_delegate.cc
@@ -133,6 +133,7 @@
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
 #include "components/crash/content/app/breakpad_linux.h"
+#include "v8/include/v8-wasm-trap-handler-posix.h"
 #include "v8/include/v8.h"
 #endif
 
@@ -592,7 +593,7 @@
   v8_crashpad_support::SetUp();
 #endif
 #if defined(OS_LINUX)
-  breakpad::SetFirstChanceExceptionHandler(v8::V8::TryHandleSignal);
+  breakpad::SetFirstChanceExceptionHandler(v8::TryHandleWebAssemblyTrapPosix);
 #endif
 
 #if defined(OS_POSIX)
diff --git a/chrome/app/chrome_packaged_service_manifests.cc b/chrome/app/chrome_packaged_service_manifests.cc
index 114bced..4b5b6c2 100644
--- a/chrome/app/chrome_packaged_service_manifests.cc
+++ b/chrome/app/chrome_packaged_service_manifests.cc
@@ -22,9 +22,9 @@
 #include "services/service_manager/public/cpp/manifest_builder.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/components/quick_launch/manifest.h"
-#include "ash/components/shortcut_viewer/manifest.h"
-#include "ash/components/tap_visualizer/manifest.h"
+#include "ash/components/quick_launch/public/cpp/manifest.h"
+#include "ash/components/shortcut_viewer/public/cpp/manifest.h"
+#include "ash/components/tap_visualizer/public/cpp/manifest.h"
 #include "ash/public/cpp/manifest.h"
 #include "chrome/browser/chromeos/ash_pref_connector_manifest.h"
 #include "chrome/services/cups_ipp_parser/manifest.h"
@@ -169,9 +169,9 @@
       profile_import::GetManifest(),
 #endif
 #if defined(OS_CHROMEOS)
-      quick_launch_app::GetManifest(),
-      shortcut_viewer_app::GetManifest(),
-      tap_visualizer_app::GetManifest(),
+      quick_launch::GetManifest(),
+      shortcut_viewer::GetManifest(),
+      tap_visualizer::GetManifest(),
       ash::GetManifest(),
       ash_pref_connector::GetManifest(),
       cups_ipp_parser::GetManifest(),
diff --git a/chrome/app/chrome_watcher_client_unittest_win.cc b/chrome/app/chrome_watcher_client_unittest_win.cc
index b6d5442..f9fca18 100644
--- a/chrome/app/chrome_watcher_client_unittest_win.cc
+++ b/chrome/app/chrome_watcher_client_unittest_win.cc
@@ -154,7 +154,7 @@
   ChromeWatcherClient& client() { return client_; }
 
   base::string16 NamedEventSuffix() {
-    return base::UintToString16(base::GetCurrentProcId());
+    return base::NumberToString16(base::GetCurrentProcId());
   }
 
   // base::SimpleThread implementation.
@@ -175,10 +175,10 @@
                           "ChromeWatcherClientTestProcess");
     ret.AppendSwitchASCII(
         kEventHandle,
-        base::UintToString(base::win::HandleToUint32(on_initialized_event)));
+        base::NumberToString(base::win::HandleToUint32(on_initialized_event)));
     ret.AppendSwitchASCII(
         kParentHandle,
-        base::UintToString(base::win::HandleToUint32(parent_handle)));
+        base::NumberToString(base::win::HandleToUint32(parent_handle)));
 
     // Our child does not actually need the main thread ID, but we verify here
     // that the correct ID is being passed from the client.
diff --git a/chrome/app/chrome_watcher_command_line_win.cc b/chrome/app/chrome_watcher_command_line_win.cc
index 050b4b1..12f17d8 100644
--- a/chrome/app/chrome_watcher_command_line_win.cc
+++ b/chrome/app/chrome_watcher_command_line_win.cc
@@ -28,7 +28,7 @@
                         HANDLE handle,
                         base::CommandLine* command_line) {
   command_line->AppendSwitchASCII(
-      switch_name, base::UintToString(base::win::HandleToUint32(handle)));
+      switch_name, base::NumberToString(base::win::HandleToUint32(handle)));
 }
 
 uint32_t ReadUintSwitch(const base::CommandLine& command_line,
@@ -151,7 +151,7 @@
   command_line.AppendSwitchASCII(switches::kProcessType,
                                  switches::kWatcherProcess);
   command_line.AppendSwitchASCII(kMainThreadIdSwitch,
-                                 base::UintToString(main_thread_id));
+                                 base::NumberToString(main_thread_id));
   AppendHandleSwitch(kOnIninitializedEventHandleSwitch, on_initialized_event,
                      &command_line);
   AppendHandleSwitch(kParentHandleSwitch, parent_process, &command_line);
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 7796250..ccc463e4 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -3507,9 +3507,13 @@
     An error occurred during uninstallation. Please uninstall through the Terminal.
   </message>
 
-  <message name="IDS_CROSTINI_REPOSITORY_SEARCH_RESULT_PLACEHOLDER_TEXT" translateable="false" desc="Placeholder text for surfaced Crostini Repository Search results in the app launcher">
+  <!-- Crostini Repository Search -->
+  <message name="IDS_CROSTINI_REPOSITORY_SEARCH_RESULT_TITLE_PLACEHOLDER_TEXT" translateable="false" desc="Placeholder title text for surfaced Crostini Repository Search results in the app launcher">
     Install <ph name="LINUX_APP_NAME">$1<ex>GIMP</ex></ph>
   </message>
+  <message name="IDS_CROSTINI_REPOSITORY_SEARCH_RESULT_DETAILS_PLACEHOLDER_TEXT" translateable="false" desc="Placeholder description details for surfaced Crostini Repository Search results in the app launcher">
+    Crostini Application
+  </message>
 
   <!-- Time limit notification -->
   <message name="IDS_SCREEN_TIME_NOTIFICATION_TITLE" desc="The title of the notification when screen usage limit reaches before locking the device.">
diff --git a/chrome/app/chromium_strings.grd b/chrome/app/chromium_strings.grd
index dc34de7..47864b4e 100644
--- a/chrome/app/chromium_strings.grd
+++ b/chrome/app/chromium_strings.grd
@@ -992,9 +992,6 @@
       </message>
 
       <!-- User menu errors -->
-      <message name="IDS_SYNC_ERROR_USER_MENU_SUPERVISED_SIGNIN_MESSAGE" desc="Message of the out-of-date signin token error for supervised users in the header of desktop user menu.">
-        Your manager must remove and add you back to Chromium.
-      </message>
       <message name="IDS_SYNC_ERROR_USER_MENU_UPGRADE_MESSAGE" desc="Message of the out-of-date Chrome client error in the header of desktop user menu.">
         Chromium is out of date
       </message>
diff --git a/chrome/app/file_manager_strings.grdp b/chrome/app/file_manager_strings.grdp
index e6b9400..8fb74f4 100644
--- a/chrome/app/file_manager_strings.grdp
+++ b/chrome/app/file_manager_strings.grdp
@@ -68,6 +68,9 @@
   <message name="IDS_FILE_BROWSER_MY_FILES_ROOT_LABEL" desc="A label for the 'My files' root which is parent of Downloads, Linux and Android files. Use sentence case.">
     My files
   </message>
+  <message name="IDS_FILE_BROWSER_EXTERNAL_STORAGE_ROOT_LABEL" desc="A label for external USB storage. Use sentence case.">
+    External storage
+  </message>
   <message name="IDS_FILE_BROWSER_MEDIA_VIEW_IMAGES_ROOT_LABEL" desc="A label for the 'Images' root of media views.">
     Images
   </message>
diff --git a/chrome/app/file_manager_strings_grdp/IDS_FILE_BROWSER_EXTERNAL_STORAGE_ROOT_LABEL.png.sha1 b/chrome/app/file_manager_strings_grdp/IDS_FILE_BROWSER_EXTERNAL_STORAGE_ROOT_LABEL.png.sha1
new file mode 100644
index 0000000..f993fad
--- /dev/null
+++ b/chrome/app/file_manager_strings_grdp/IDS_FILE_BROWSER_EXTERNAL_STORAGE_ROOT_LABEL.png.sha1
@@ -0,0 +1 @@
+cea3cc362c13aa56e1333c89c4705072575267b1
\ No newline at end of file
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 762c196..74ec739 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -465,7 +465,7 @@
           <message name="IDS_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB" desc="The name of the Open Original Image in New Tab command in the content area context menu">
             Open original &amp;image in new tab
           </message>
-          <message name="IDS_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE" desc="The name of the Load Image command in the content area context menu">
+          <message name="IDS_CONTENT_CONTEXT_LOAD_IMAGE" desc="The name of the Load Image command in the content area context menu">
             Load image
           </message>
 
@@ -686,7 +686,7 @@
           <message name="IDS_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB" desc="In Title Case: The name of the Open Original Image in New Tab command in the content area context menu">
             Open Original &amp;Image in New Tab
           </message>
-          <message name="IDS_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE" desc="In Title Case: The name of the Load Image command in the content area context menu">
+          <message name="IDS_CONTENT_CONTEXT_LOAD_IMAGE" desc="In Title Case: The name of the Load Image command in the content area context menu">
             Load Image
           </message>
 
@@ -1609,7 +1609,7 @@
                desc="In the download view, 'Pause' link text">
         Pause
       </message>
-      <message name="IDS_MD_DOWNLOAD_SEARCH"
+      <message name="IDS_DOWNLOAD_SEARCH"
                desc="Placeholder text/label for the search input in the download page">
         Search downloads
       </message>
@@ -1617,7 +1617,7 @@
                desc="Tooltip text for the button that clears the search term on the downloads page.">
         Clear search
       </message>
-      <message name="IDS_MD_DOWNLOAD_NO_DOWNLOADS" desc="A message shown when the user has no downloads to show on chrome://downloads.">
+      <message name="IDS_DOWNLOAD_NO_DOWNLOADS" desc="A message shown when the user has no downloads to show on chrome://downloads.">
         Files you download appear here
       </message>
       <message name="IDS_DOWNLOAD_ITEM_DROPDOWN_BUTTON_ACCESSIBLE_TEXT" desc="Text for the dropdown button that opens the download item options menu">
@@ -1639,7 +1639,7 @@
                desc="In the download view, 'Cancel' link text">
         Cancel
       </message>
-      <message name="IDS_MD_DOWNLOAD_LINK_RETRY"
+      <message name="IDS_DOWNLOAD_LINK_RETRY"
                desc="In the download view, 'Retry' button text to retry downloading a file.">
         Retry
       </message>
@@ -3240,9 +3240,6 @@
         </message>
       </if>
 
-      <message name="IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME" desc="The name of the utility process used for decoding images.">
-        Image Decoder
-      </message>
       <message name="IDS_UTILITY_PROCESS_PROXY_RESOLVER_NAME" desc="The name of the utility process used for out-of-process V8 proxy resolution.">
         V8 Proxy Resolver
       </message>
@@ -6356,12 +6353,6 @@
       <message name="IDS_COOKIES_COOKIE_NONESELECTED" desc="Field value when no cookie is selected">
         no cookie selected
       </message>
-      <message name="IDS_COOKIES_WEB_DATABASE_DESCRIPTION_LABEL" desc="The Database Description label">
-        Description:
-      </message>
-      <message name="IDS_COOKIES_WEB_DATABASE_UNNAMED_NAME" desc="The the name displayed for a Database with an empty name attribute.">
-        &lt;unnamed&gt;
-      </message>
       <if expr="is_macosx">
         <message name="IDS_COOKIES_LOCAL_STORAGE_KEY_LABEL" desc="The label of the Local Storage key being set">
           Key:
@@ -6399,20 +6390,12 @@
       <message name="IDS_COOKIES_WEB_DATABASES" desc="Label for the folder under which a list of web databases (name of an HTML standard) are displayed">
         Web databases
       </message>
-      <if expr="is_macosx">
-        <message name="IDS_COOKIES_WEB_DATABASE_NAME" desc="Label for the name of an individual web databases (name of an HTML standard) are displayed">
-          Database name:
-        </message>
-      </if>
       <message name="IDS_COOKIES_LOCAL_STORAGE" desc="Label for local storage (name of an HTML standard)">
         Local storage
       </message>
       <message name="IDS_COOKIES_SESSION_STORAGE" desc="Label for session storage (name of an HTML standard)">
         Session storage
       </message>
-      <message name="IDS_COOKIES_INDEXED_DB" desc="The text shown when there is an Indexed Database (name of an HTML standard) in the Cookies table">
-        Indexed database
-      </message>
       <message name="IDS_COOKIES_INDEXED_DBS" desc="Label for Indexed Databases (name of an HTML standard)">
         Indexed databases
       </message>
diff --git a/chrome/app/google_chrome_strings.grd b/chrome/app/google_chrome_strings.grd
index 6fd110c6..1a615ae 100644
--- a/chrome/app/google_chrome_strings.grd
+++ b/chrome/app/google_chrome_strings.grd
@@ -1010,9 +1010,6 @@
       </message>
 
       <!-- User menu errors -->
-      <message name="IDS_SYNC_ERROR_USER_MENU_SUPERVISED_SIGNIN_MESSAGE" desc="Message of the out-of-date signin token error for supervised users in the header of desktop user menu.">
-        Your manager must remove and add you back to Chrome.
-      </message>
       <message name="IDS_SYNC_ERROR_USER_MENU_UPGRADE_MESSAGE" desc="Message of the out-of-date Chrome client error in the header of desktop user menu.">
         Chrome is out of date
       </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 1d1207c..426f7836 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -494,12 +494,21 @@
     <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS" desc="Label for managing shared folders in Crostini.">
       Manage shared files &amp; folders
     </message>
+    <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING" desc="Label for list of shared folders.">
+      Shared folders
+    </message>
     <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD" desc="Instructions for how to add shared folders in Crostini.">
       To share, right-click on a folder in Files app, then select "Share with Linux".
     </message>
+    <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE" desc="Instructions for how to locate shared folders in Crostini.">
+      Shared folders are available in Linux at <ph name="BASE_DIR">$1<ex>/mnt/chromeos</ex></ph>.
+    </message>
     <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_REMOVE" desc="Instructions for removing shared folders in Crostini.">
       Removing folders from here will stop sharing but will not delete files.
     </message>
+    <message name="IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING" desc="Tooltip to show when hovering on the remove icon for a crostini shared folder.">
+      Remove sharing
+    </message>
     <message name="IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LABEL" desc="Label for managing shared USB devices.">
       USB Device preferences
     </message>
@@ -3290,9 +3299,6 @@
         =1 {1 cookie}
         other {# cookies}}
   </message>
-  <message name="IDS_SETTINGS_COOKIES_WEB_DATABASE_DESCRIPTION_LABEL" desc="The Database Description label">
-    Description
-  </message>
   <message name="IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL" desc="The Local Storage Origin label">
     Origin
   </message>
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE.png.sha1
new file mode 100644
index 0000000..456acee
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE.png.sha1
@@ -0,0 +1 @@
+4dcc02b4b67dadb033eadd0f51e34946d4b582b4
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING.png.sha1
new file mode 100644
index 0000000..456acee
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING.png.sha1
@@ -0,0 +1 @@
+4dcc02b4b67dadb033eadd0f51e34946d4b582b4
\ No newline at end of file
diff --git a/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING.png.sha1 b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING.png.sha1
new file mode 100644
index 0000000..456acee
--- /dev/null
+++ b/chrome/app/settings_strings_grdp/IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING.png.sha1
@@ -0,0 +1 @@
+4dcc02b4b67dadb033eadd0f51e34946d4b582b4
\ No newline at end of file
diff --git a/chrome/app/vector_icons/BUILD.gn b/chrome/app/vector_icons/BUILD.gn
index 7d3fae21..46462167 100644
--- a/chrome/app/vector_icons/BUILD.gn
+++ b/chrome/app/vector_icons/BUILD.gn
@@ -58,7 +58,6 @@
     "google_pay_logo.icon",
     "horizontal_menu.icon",
     "incognito.icon",
-    "incognito_circle.icon",
     "input.icon",
     "key.icon",
     "laptop.icon",
diff --git a/chrome/app/vector_icons/incognito_circle.icon b/chrome/app/vector_icons/incognito_circle.icon
deleted file mode 100644
index 29ebc4a..0000000
--- a/chrome/app/vector_icons/incognito_circle.icon
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-CANVAS_DIMENSIONS, 40,
-PATH_COLOR_ARGB, 0xFF, 0x32, 0x36, 0x39,
-CIRCLE, 20, 20, 20,
-CLOSE,
-MOVE_TO, 16.51f, 11.29f,
-R_CUBIC_TO, 0.26f, 0.13f, 2.39f, 0.49f, 2.39f, 0.49f,
-R_CUBIC_TO, 0, 0, 4.68f, -0.68f, 4.85f, -0.72f,
-R_CUBIC_TO, 0.11f, -0.03f, 0.16f, 0.03f, 0.19f, 0.11f,
-R_CUBIC_TO, 0.01f, 0.05f, 0.81f, 2.65f, 1.48f, 4.83f,
-R_H_LINE_TO, -10.82f,
-R_ARC_TO, 951.85f, 951.85f, 0, 0, 0, 1.54f, -4.61f,
-R_CUBIC_TO, 0.04f, -0.14f, 0.18f, -0.19f, 0.38f, -0.1f,
-CLOSE,
-MOVE_TO, 23.84f, 27.05f,
-R_CUBIC_TO, -1.7f, 0, -3.08f, -1.37f, -3.08f, -3.05f,
-R_CUBIC_TO, 0, -0.09f, 0, -0.18f, 0.01f, -0.27f,
-R_ARC_TO, 2.63f, 2.63f, 0, 0, 0, -1.64f, 0.01f,
-R_CUBIC_TO, 0.01f, 0.08f, 0.01f, 0.17f, 0.01f, 0.25f,
-R_CUBIC_TO, 0, 1.69f, -1.38f, 3.05f, -3.08f, 3.05f,
-R_CUBIC_TO, -1.7f, 0, -3.08f, -1.37f, -3.08f, -3.05f,
-R_CUBIC_TO, 0, -1.68f, 1.38f, -3.05f, 3.08f, -3.05f,
-R_CUBIC_TO, 1.29f, 0, 2.39f, 0.78f, 2.85f, 1.89f,
-R_ARC_TO, 3.58f, 3.58f, 0, 0, 1, 2.08f, -0.01f,
-R_LINE_TO, 0, 0,
-R_ARC_TO, 3.08f, 3.08f, 0, 0, 1, 2.84f, -1.88f,
-R_CUBIC_TO, 1.7f, 0, 3.08f, 1.37f, 3.08f, 3.05f,
-CUBIC_TO_SHORTHAND, 25.54f, 27.05f, 23.84f, 27.05f,
-CLOSE,
-R_MOVE_TO, 0, -5.22f,
-R_ARC_TO, 2.18f, 2.18f, 0, 0, 0, -2.19f, 2.17f,
-R_CUBIC_TO, 0, 1.2f, 0.98f, 2.17f, 2.19f, 2.17f,
-R_ARC_TO, 2.18f, 2.18f, 0, 0, 0, 2.19f, -2.17f,
-R_ARC_TO, 2.18f, 2.18f, 0, 0, 0, -2.19f, -2.17f,
-CLOSE,
-R_MOVE_TO, -7.77f, 0,
-R_ARC_TO, 2.18f, 2.18f, 0, 0, 0, -2.19f, 2.17f,
-R_CUBIC_TO, 0, 1.2f, 0.98f, 2.17f, 2.19f, 2.17f,
-R_ARC_TO, 2.18f, 2.18f, 0, 0, 0, 2.19f, -2.17f,
-R_ARC_TO, 2.18f, 2.18f, 0, 0, 0, -2.19f, -2.17f,
-CLOSE,
-R_MOVE_TO, 3.92f, -3.98f,
-CUBIC_TO, 26.03f, 17.85f, 30, 20, 30, 20,
-H_LINE_TO, 10,
-R_CUBIC_TO, 0, 0, 3.95f, -2.15f, 9.99f, -2.15f,
-CLOSE
\ No newline at end of file
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 5d7dfa8..9b717fb 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -759,6 +759,8 @@
     "memory_details_win.cc",
     "metrics/antivirus_metrics_provider_win.cc",
     "metrics/antivirus_metrics_provider_win.h",
+    "metrics/bluetooth_available_utility.cc",
+    "metrics/bluetooth_available_utility.h",
     "metrics/browser_window_histogram_helper.cc",
     "metrics/browser_window_histogram_helper.h",
     "metrics/chrome_browser_main_extra_parts_metrics.cc",
@@ -938,8 +940,10 @@
     "page_load_metrics/metrics_web_contents_observer.h",
     "page_load_metrics/observers/aborts_page_load_metrics_observer.cc",
     "page_load_metrics/observers/aborts_page_load_metrics_observer.h",
-    "page_load_metrics/observers/ads_page_load_metrics_observer.cc",
-    "page_load_metrics/observers/ads_page_load_metrics_observer.h",
+    "page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc",
+    "page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h",
+    "page_load_metrics/observers/ad_metrics/frame_data.cc",
+    "page_load_metrics/observers/ad_metrics/frame_data.h",
     "page_load_metrics/observers/amp_page_load_metrics_observer.cc",
     "page_load_metrics/observers/amp_page_load_metrics_observer.h",
     "page_load_metrics/observers/core_page_load_metrics_observer.cc",
@@ -1878,6 +1882,7 @@
     "//components/security_state/content",
     "//components/security_state/core",
     "//components/send_tab_to_self",
+    "//components/services/heap_profiling",
     "//components/services/patch/public/interfaces",
     "//components/services/unzip/public/interfaces",
     "//components/sessions",
@@ -4760,10 +4765,10 @@
     }
   }
 
-  if (is_desktop_linux || is_mac) {
+  if (trial_comparison_cert_verifier_supported) {
     sources += [
-      "net/trial_comparison_cert_verifier.cc",
-      "net/trial_comparison_cert_verifier.h",
+      "net/trial_comparison_cert_verifier_controller.cc",
+      "net/trial_comparison_cert_verifier_controller.h",
     ]
   }
 
@@ -5341,12 +5346,12 @@
     "search_engines/template_url_service_test_util.h",
     "signin/fake_account_fetcher_service_builder.cc",
     "signin/fake_account_fetcher_service_builder.h",
-    "signin/fake_gaia_cookie_manager_service_builder.cc",
-    "signin/fake_gaia_cookie_manager_service_builder.h",
     "signin/fake_profile_oauth2_token_service_builder.cc",
     "signin/fake_profile_oauth2_token_service_builder.h",
     "signin/fake_signin_manager_builder.cc",
     "signin/fake_signin_manager_builder.h",
+    "signin/gaia_cookie_manager_service_test_util.cc",
+    "signin/gaia_cookie_manager_service_test_util.h",
     "signin/identity_test_environment_profile_adaptor.cc",
     "signin/identity_test_environment_profile_adaptor.h",
     "signin/scoped_account_consistency.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index d1938c14..bd19ab1 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -81,6 +81,7 @@
   "+services/network/network_service.h",
   "+services/network/public",
   "+services/network/test",
+  "+services/network/trial_comparison_cert_verifier_mojo.h",
   "+services/network/url_request_context_owner.h",
   "+services/network/url_request_context_builder_mojo.h",
   "+services/preferences/public/cpp",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 1793b8a..c1e6961 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1487,10 +1487,6 @@
      SINGLE_VALUE_TYPE_AND_VALUE(switches::kAllowNaClSocketAPI, "*")},
 #endif  // ENABLE_PLUGINS
 #if defined(OS_CHROMEOS)
-    {"ash-enable-notification-scroll-bar",
-     flag_descriptions::kEnableNotificationScrollBarName,
-     flag_descriptions::kEnableNotificationScrollBarDescription, kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kNotificationScrollBar)},
     {"allow-touchpad-three-finger-click",
      flag_descriptions::kAllowTouchpadThreeFingerClickName,
      flag_descriptions::kAllowTouchpadThreeFingerClickDescription, kOsCrOS,
@@ -1950,10 +1946,6 @@
      flag_descriptions::kWakeOnPacketsDescription, kOsCrOSOwnerOnly,
      SINGLE_VALUE_TYPE(chromeos::switches::kWakeOnWifiPacket)},
 #endif  // OS_CHROMEOS
-    {"enable-memory-coordinator", flag_descriptions::kMemoryCoordinatorName,
-     flag_descriptions::kMemoryCoordinatorDescription,
-     kOsAndroid | kOsCrOS | kOsLinux | kOsWin,
-     FEATURE_VALUE_TYPE(features::kMemoryCoordinator)},
     {"reduced-referrer-granularity",
      flag_descriptions::kReducedReferrerGranularityName,
      flag_descriptions::kReducedReferrerGranularityDescription, kOsAll,
@@ -2699,6 +2691,10 @@
     {"fill-on-account-select", flag_descriptions::kFillOnAccountSelectName,
      flag_descriptions::kFillOnAccountSelectDescription, kOsAll,
      FEATURE_VALUE_TYPE(password_manager::features::kFillOnAccountSelect)},
+    {"fill-on-account-select-http",
+     flag_descriptions::kFillOnAccountSelectHttpName,
+     flag_descriptions::kFillOnAccountSelectHttpDescription, kOsAll,
+     FEATURE_VALUE_TYPE(password_manager::features::kFillOnAccountSelectHttp)},
     {"enable-surfaces-for-videos",
      flag_descriptions::kUseSurfaceLayerForVideoName,
      flag_descriptions::kUseSurfaceLayerForVideoDescription, kOsAll,
@@ -4136,15 +4132,6 @@
      flag_descriptions::kSendTabToSelfDescription, kOsAll,
      FEATURE_VALUE_TYPE(switches::kSyncSendTabToSelf)},
 
-#if defined(OS_CHROMEOS)
-    {"ash-enable-notification-expansion-animation",
-     flag_descriptions::kEnableNotificationExpansionAnimationName,
-     flag_descriptions::kEnableNotificationExpansionAnimationDescription,
-     kOsCrOS,
-     FEATURE_VALUE_TYPE(ash::features::kNotificationExpansionAnimation)},
-
-#endif  // defined(OS_CHROMEOS)
-
     {"enable-data-reduction-proxy-with-network-service",
      flag_descriptions::kEnableDataReductionProxyNetworkServiceName,
      flag_descriptions::kEnableDataReductionProxyNetworkServiceDescription,
diff --git a/chrome/browser/accessibility/interstitial_accessibility_browsertest.cc b/chrome/browser/accessibility/interstitial_accessibility_browsertest.cc
index c8ef662..d51d8eb 100644
--- a/chrome/browser/accessibility/interstitial_accessibility_browsertest.cc
+++ b/chrome/browser/accessibility/interstitial_accessibility_browsertest.cc
@@ -72,7 +72,7 @@
     SSLBlockingPage* ssl_interstitial = static_cast<SSLBlockingPage*>(
         interstitial_page->GetDelegateForTesting());
     ssl_interstitial->CommandReceived(
-        base::IntToString(security_interstitials::CMD_PROCEED));
+        base::NumberToString(security_interstitials::CMD_PROCEED));
   }
 };
 
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.cc b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
index f0b76ea..e879bd2f 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.cc
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.cc
@@ -103,19 +103,28 @@
 void UiControllerAndroid::OnStateChanged(AutofillAssistantState new_state) {
   switch (new_state) {
     case AutofillAssistantState::STARTING:
-      ShowOverlay();
+      SetOverlayState(OverlayState::FULL);
       AllowShowingSoftKeyboard(false);
       SetProgressPulsingEnabled(false);
       return;
 
     case AutofillAssistantState::RUNNING:
-      ShowOverlay();
+      SetOverlayState(OverlayState::FULL);
       AllowShowingSoftKeyboard(false);
       SetProgressPulsingEnabled(false);
       return;
 
+    case AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT:
+      SetOverlayState(OverlayState::HIDDEN);
+      AllowShowingSoftKeyboard(true);
+      SetProgressPulsingEnabled(true);
+
+      // user interaction is needed.
+      ExpandBottomSheet();
+      return;
+
     case AutofillAssistantState::PROMPT:
-      ShowOverlay();  // partial overlay
+      SetOverlayState(OverlayState::PARTIAL);
       AllowShowingSoftKeyboard(true);
       SetProgressPulsingEnabled(true);
 
@@ -124,13 +133,13 @@
       return;
 
     case AutofillAssistantState::MODAL_DIALOG:
-      ShowOverlay();
+      SetOverlayState(OverlayState::FULL);
       AllowShowingSoftKeyboard(true);
       SetProgressPulsingEnabled(false);
       return;
 
     case AutofillAssistantState::STOPPED:
-      HideOverlay();
+      SetOverlayState(OverlayState::HIDDEN);
       AllowShowingSoftKeyboard(true);
       SetProgressPulsingEnabled(false);
 
@@ -158,18 +167,11 @@
   }
 }
 
-void UiControllerAndroid::ShowProgressBar(int progress) {
-  // TODO(crbug.com/806868): Get progress first and call setProgress only if
-  // progress > current_progress, and remove that logic from
-  // AnimatedProgressBar.
+void UiControllerAndroid::OnProgressChanged(int progress) {
   Java_AssistantHeaderModel_setProgress(AttachCurrentThread(), GetHeaderModel(),
                                         progress);
 }
 
-void UiControllerAndroid::HideProgressBar() {
-  // TODO(crbug.com/806868): Remove calls to this function.
-}
-
 void UiControllerAndroid::AllowShowingSoftKeyboard(bool enabled) {
   Java_AssistantModel_setAllowSoftKeyboard(AttachCurrentThread(), GetModel(),
                                            enabled);
@@ -222,40 +224,28 @@
                                               GetModel());
 }
 
-void UiControllerAndroid::SetChips(std::unique_ptr<std::vector<Chip>> chips) {
-  DCHECK(chips);
-  current_chips_ = std::move(chips);
+void UiControllerAndroid::OnChipsChanged(const std::vector<Chip>& chips) {
+  JNIEnv* env = AttachCurrentThread();
+  auto jmodel = GetCarouselModel();
+  if (chips.empty()) {
+    Java_AssistantCarouselModel_clearChips(env, jmodel);
+    return;
+  }
 
-  int types[current_chips_->size()];
+  std::vector<int> types;
   std::vector<std::string> texts;
-  int i = 0;
-  for (const auto& chip : *current_chips_) {
-    types[i++] = chip.type;
+  for (const auto& chip : chips) {
+    types.emplace_back(chip.type);
     texts.emplace_back(chip.text);
   }
-  SetProgressPulsingEnabled(true);
-  JNIEnv* env = AttachCurrentThread();
   Java_AssistantCarouselModel_setChips(
-      env, GetCarouselModel(),
-      base::android::ToJavaIntArray(env, types, current_chips_->size()),
+      env, jmodel, base::android::ToJavaIntArray(env, types),
       base::android::ToJavaArrayOfStrings(env, texts),
       carousel_delegate_.GetJavaObject());
 }
 
-void UiControllerAndroid::ClearChips() {
-  current_chips_.reset();
-  SetProgressPulsingEnabled(false);
-  Java_AssistantCarouselModel_clearChips(AttachCurrentThread(),
-                                         GetCarouselModel());
-}
-
 void UiControllerAndroid::OnChipSelected(int index) {
-  if (current_chips_ && index >= 0 && index < (int)current_chips_->size()) {
-    auto callback = std::move((*current_chips_)[index].callback);
-    current_chips_.reset();
-    SetProgressPulsingEnabled(false);
-    std::move(callback).Run();
-  }
+  ui_delegate_->SelectChip(index);
 }
 
 // Overlay related methods.
@@ -265,17 +255,13 @@
   return Java_AssistantModel_getOverlayModel(AttachCurrentThread(), GetModel());
 }
 
-void UiControllerAndroid::ShowOverlay() {
-  Java_AssistantOverlayModel_setFull(AttachCurrentThread(), GetOverlayModel());
+void UiControllerAndroid::SetOverlayState(OverlayState state) {
+  Java_AssistantOverlayModel_setState(AttachCurrentThread(), GetOverlayModel(),
+                                      state);
 }
 
-void UiControllerAndroid::HideOverlay() {
-  Java_AssistantOverlayModel_setHidden(AttachCurrentThread(),
-                                       GetOverlayModel());
-}
-
-void UiControllerAndroid::UpdateTouchableArea(bool enabled,
-                                              const std::vector<RectF>& areas) {
+void UiControllerAndroid::OnTouchableAreaChanged(
+    const std::vector<RectF>& areas) {
   JNIEnv* env = AttachCurrentThread();
   std::vector<float> flattened;
   for (const auto& rect : areas) {
@@ -284,7 +270,7 @@
     flattened.emplace_back(rect.right);
     flattened.emplace_back(rect.bottom);
   }
-  Java_AssistantOverlayModel_setPartial(
+  Java_AssistantOverlayModel_setTouchableArea(
       env, GetOverlayModel(), base::android::ToJavaFloatArray(env, flattened));
 }
 
diff --git a/chrome/browser/android/autofill_assistant/ui_controller_android.h b/chrome/browser/android/autofill_assistant/ui_controller_android.h
index ed12ff1..3fbf46f 100644
--- a/chrome/browser/android/autofill_assistant/ui_controller_android.h
+++ b/chrome/browser/android/autofill_assistant/ui_controller_android.h
@@ -15,9 +15,11 @@
 #include "chrome/browser/android/autofill_assistant/assistant_header_delegate.h"
 #include "chrome/browser/android/autofill_assistant/assistant_overlay_delegate.h"
 #include "chrome/browser/android/autofill_assistant/assistant_payment_request_delegate.h"
+#include "components/autofill_assistant/browser/chip.h"
 #include "components/autofill_assistant/browser/client.h"
 #include "components/autofill_assistant/browser/details.h"
 #include "components/autofill_assistant/browser/metrics.h"
+#include "components/autofill_assistant/browser/overlay_state.h"
 #include "components/autofill_assistant/browser/ui_controller.h"
 
 namespace autofill_assistant {
@@ -43,17 +45,14 @@
   void OnStatusMessageChanged(const std::string& message) override;
   void Shutdown(Metrics::DropOutReason reason) override;
   void Close() override;
-  void SetChips(std::unique_ptr<std::vector<Chip>> chips) override;
-  void ClearChips() override;
+  void OnChipsChanged(const std::vector<Chip>& chips) override;
   void GetPaymentInformation(
       payments::mojom::PaymentOptionsPtr payment_options,
       base::OnceCallback<void(std::unique_ptr<PaymentInformation>)> callback,
       const std::vector<std::string>& supported_basic_card_networks) override;
   void OnDetailsChanged(const Details* details) override;
-  void ShowProgressBar(int progress) override;
-  void HideProgressBar() override;
-  void UpdateTouchableArea(bool enabled,
-                           const std::vector<RectF>& areas) override;
+  void OnProgressChanged(int progress) override;
+  void OnTouchableAreaChanged(const std::vector<RectF>& areas) override;
 
   // Called by AssistantOverlayDelegate:
   void OnUnexpectedTaps();
@@ -92,8 +91,7 @@
   base::android::ScopedJavaLocalRef<jobject> GetPaymentRequestModel();
   base::android::ScopedJavaLocalRef<jobject> GetCarouselModel();
 
-  void ShowOverlay();
-  void HideOverlay();
+  void SetOverlayState(OverlayState state);
   void AllowShowingSoftKeyboard(bool enabled);
   void ExpandBottomSheet();
   void ShutdownGracefully();
@@ -104,7 +102,6 @@
   base::android::ScopedJavaGlobalRef<jobject>
       java_autofill_assistant_ui_controller_;
 
-  std::unique_ptr<std::vector<Chip>> current_chips_;
   base::OnceCallback<void(std::unique_ptr<PaymentInformation>)>
       get_payment_information_callback_;
 
diff --git a/chrome/browser/android/chrome_feature_list.cc b/chrome/browser/android/chrome_feature_list.cc
index 812c7ec..722011e6 100644
--- a/chrome/browser/android/chrome_feature_list.cc
+++ b/chrome/browser/android/chrome_feature_list.cc
@@ -27,6 +27,7 @@
 #include "components/omnibox/browser/omnibox_field_trial.h"
 #include "components/password_manager/core/common/password_manager_features.h"
 #include "components/payments/core/features.h"
+#include "components/previews/core/previews_features.h"
 #include "components/safe_browsing/features.h"
 #include "components/subresource_filter/core/browser/subresource_filter_features.h"
 #include "components/sync/driver/sync_driver_switches.h"
@@ -152,6 +153,7 @@
     &kSoleIntegration,
     &kSpannableInlineAutocomplete,
     &kSpecialLocaleWrapper,
+    &kTabGridLayoutAndroid,
     &kTabReparenting,
     &kTabSwitcherOnReturn,
     &kTrustedWebActivity,
@@ -184,6 +186,7 @@
     &omnibox::kQueryInOmnibox,
     &password_manager::features::kGooglePasswordManager,
     &password_manager::features::kPasswordsKeyboardAccessory,
+    &previews::features::kDataSaverLiteModeRebranding,
     &switches::kSyncSendTabToSelf,
     &translate::kTranslateAndroidManualTrigger,
     &unified_consent::kUnifiedConsent,
@@ -420,6 +423,9 @@
 const base::Feature kSpecialLocaleWrapper{"SpecialLocaleWrapper",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
+const base::Feature kTabGridLayoutAndroid{"TabGridLayoutAndroid",
+                                          base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kTabReparenting{"TabReparenting",
                                     base::FEATURE_ENABLED_BY_DEFAULT};
 
diff --git a/chrome/browser/android/chrome_feature_list.h b/chrome/browser/android/chrome_feature_list.h
index 997c16c0..c7997f1 100644
--- a/chrome/browser/android/chrome_feature_list.h
+++ b/chrome/browser/android/chrome_feature_list.h
@@ -83,6 +83,7 @@
 extern const base::Feature kSoleIntegration;
 extern const base::Feature kSpannableInlineAutocomplete;
 extern const base::Feature kSpecialLocaleWrapper;
+extern const base::Feature kTabGridLayoutAndroid;
 extern const base::Feature kTabReparenting;
 extern const base::Feature kTabSwitcherOnReturn;
 extern const base::Feature kTrustedWebActivity;
diff --git a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
index ff820415..03be34cb 100644
--- a/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
+++ b/chrome/browser/android/contextualsearch/contextual_search_delegate.cc
@@ -164,7 +164,7 @@
   // Add Chrome experiment state to the request headers.
   // Reset will delete any previous loader, and we won't get any callback.
   url_loader_ =
-      variations::CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn(
+      variations::CreateSimpleURLLoaderWithVariationsHeaderUnknownSignedIn(
           std::move(resource_request),
           variations::InIncognito::kNo,  // Impossible to be incognito at this
                                          // point.
diff --git a/chrome/browser/android/devtools_manager_delegate_android.cc b/chrome/browser/android/devtools_manager_delegate_android.cc
index 6fe04d8..f805a467 100644
--- a/chrome/browser/android/devtools_manager_delegate_android.cc
+++ b/chrome/browser/android/devtools_manager_delegate_android.cc
@@ -181,7 +181,7 @@
   if (result)
     return result;
 
-  result = DevToolsAgentHost::Forward(base::IntToString(tab->GetAndroidId()),
+  result = DevToolsAgentHost::Forward(base::NumberToString(tab->GetAndroidId()),
                                       std::make_unique<TabProxyDelegate>(tab));
   tab->SetDevToolsAgentHost(result);
   return result;
diff --git a/chrome/browser/android/history_report/delta_file_backend_leveldb.cc b/chrome/browser/android/history_report/delta_file_backend_leveldb.cc
index 0c5337b5..98dfa0e 100644
--- a/chrome/browser/android/history_report/delta_file_backend_leveldb.cc
+++ b/chrome/browser/android/history_report/delta_file_backend_leveldb.cc
@@ -248,7 +248,7 @@
   std::unique_ptr<leveldb::Iterator> db_it(db_->NewIterator(options));
   int num_entries = 0;
   for (db_it->SeekToFirst(); db_it->Valid(); db_it->Next()) num_entries++;
-  dump.append(base::IntToString(num_entries));
+  dump.append(base::NumberToString(num_entries));
   dump.append("]");
   return dump;
 }
diff --git a/chrome/browser/android/history_report/usage_reports_buffer_backend.cc b/chrome/browser/android/history_report/usage_reports_buffer_backend.cc
index a101f56..5965cbd 100644
--- a/chrome/browser/android/history_report/usage_reports_buffer_backend.cc
+++ b/chrome/browser/android/history_report/usage_reports_buffer_backend.cc
@@ -143,7 +143,8 @@
     return dump;
   }
   dump.append("num pending entries=");
-  dump.append(base::IntToString(usage_report_util::DatabaseEntries(db_.get())));
+  dump.append(
+      base::NumberToString(usage_report_util::DatabaseEntries(db_.get())));
   dump.append("]");
   return dump;
 }
diff --git a/chrome/browser/android/metrics/uma_session_stats.cc b/chrome/browser/android/metrics/uma_session_stats.cc
index f8bec8e3..93fd733 100644
--- a/chrome/browser/android/metrics/uma_session_stats.cc
+++ b/chrome/browser/android/metrics/uma_session_stats.cc
@@ -215,7 +215,8 @@
   variations::ActiveGroupId active_group;
   active_group.name = variations::HashName(trial_name_utf8);
   for (int experiment_id : experiment_ids) {
-    active_group.group = variations::HashName(base::IntToString(experiment_id));
+    active_group.group =
+        variations::HashName(base::NumberToString(experiment_id));
     // Since external experiments are not based on Chrome's low entropy source,
     // they are only sent to Google web properties for signed in users to make
     // sure that this couldn't be used to identify a user that's not signed in.
diff --git a/chrome/browser/android/ntp/content_suggestions_notifier_service.cc b/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
index fd2437f..d21e6a6 100644
--- a/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
+++ b/chrome/browser/android/ntp/content_suggestions_notifier_service.cc
@@ -153,7 +153,7 @@
           service_->GetSuggestionsForCategory(category).size() - 1;
       text = l10n_util::GetStringFUTF16(
           IDS_NTP_NOTIFICATIONS_READ_THIS_STORY_AND_MORE,
-          suggestion->publisher_name(), base::IntToString16(extra_count));
+          suggestion->publisher_name(), base::NumberToString16(extra_count));
     } else {
       text = suggestion->publisher_name();
     }
diff --git a/chrome/browser/android/thumbnail/thumbnail_cache.cc b/chrome/browser/android/thumbnail/thumbnail_cache.cc
index 205563c4..8fc2816a 100644
--- a/chrome/browser/android/thumbnail/thumbnail_cache.cc
+++ b/chrome/browser/android/thumbnail/thumbnail_cache.cc
@@ -261,7 +261,7 @@
 
 base::FilePath ThumbnailCache::GetFilePath(TabId tab_id) {
   base::FilePath path = GetCacheDirectory();
-  return path.Append(base::IntToString(tab_id));
+  return path.Append(base::NumberToString(tab_id));
 }
 
 bool ThumbnailCache::CheckAndUpdateThumbnailMetaData(TabId tab_id,
diff --git a/chrome/browser/android/usage_stats/usage_stats_bridge.cc b/chrome/browser/android/usage_stats/usage_stats_bridge.cc
index 8f916eba..03b000c 100644
--- a/chrome/browser/android/usage_stats/usage_stats_bridge.cc
+++ b/chrome/browser/android/usage_stats/usage_stats_bridge.cc
@@ -6,6 +6,9 @@
 
 #include <utility>
 
+#include "base/android/callback_android.h"
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
 #include "chrome/browser/android/usage_stats/usage_stats_database.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_android.h"
@@ -13,11 +16,22 @@
 #include "components/pref_registry/pref_registry_syncable.h"
 #include "jni/UsageStatsBridge_jni.h"
 
+using base::android::AttachCurrentThread;
 using base::android::JavaParamRef;
 using base::android::JavaRef;
+using base::android::ScopedJavaLocalRef;
+using base::android::ToJavaArrayOfStrings;
 
 namespace usage_stats {
 
+namespace {
+
+bool isSuccess(UsageStatsDatabase::Error error) {
+  return error == UsageStatsDatabase::Error::kNoError;
+}
+
+}  // namespace
+
 static jlong JNI_UsageStatsBridge_Init(JNIEnv* env,
                                        const JavaParamRef<jobject>& j_this,
                                        const JavaParamRef<jobject>& j_profile) {
@@ -55,7 +69,7 @@
 
 void UsageStatsBridge::AddEvents(JNIEnv* j_env,
                                  const JavaRef<jobject>& j_this,
-                                 const JavaRef<jobjectArray>& j_events,
+                                 const JavaRef<jobject>& j_events,
                                  const JavaRef<jobject>& j_callback) {}
 
 void UsageStatsBridge::DeleteAllEvents(JNIEnv* j_env,
@@ -77,12 +91,27 @@
 
 void UsageStatsBridge::GetAllSuspensions(JNIEnv* j_env,
                                          const JavaRef<jobject>& j_this,
-                                         const JavaRef<jobject>& j_callback) {}
+                                         const JavaRef<jobject>& j_callback) {
+  ScopedJavaGlobalRef<jobject> callback(j_callback);
+
+  usage_stats_database_->GetAllSuspensions(
+      base::BindOnce(&UsageStatsBridge::OnGetAllSuspensionsDone,
+                     weak_ptr_factory_.GetWeakPtr(), callback));
+}
 
 void UsageStatsBridge::SetSuspensions(JNIEnv* j_env,
                                       const JavaRef<jobject>& j_this,
                                       const JavaRef<jobjectArray>& j_domains,
-                                      const JavaRef<jobject>& j_callback) {}
+                                      const JavaRef<jobject>& j_callback) {
+  std::vector<std::string> domains;
+  AppendJavaStringArrayToStringVector(j_env, j_domains, &domains);
+
+  ScopedJavaGlobalRef<jobject> callback(j_callback);
+
+  usage_stats_database_->SetSuspensions(
+      domains, base::BindOnce(&UsageStatsBridge::OnSetSuspensionsDone,
+                              weak_ptr_factory_.GetWeakPtr(), callback));
+}
 
 void UsageStatsBridge::GetAllTokenMappings(JNIEnv* j_env,
                                            const JavaRef<jobject>& j_this,
@@ -94,6 +123,25 @@
                                         const JavaRef<jobject>& j_mappings,
                                         const JavaRef<jobject>& j_callback) {}
 
+void UsageStatsBridge::OnGetAllSuspensionsDone(
+    ScopedJavaGlobalRef<jobject> callback,
+    UsageStatsDatabase::Error error,
+    std::vector<std::string> suspensions) {
+  JNIEnv* env = AttachCurrentThread();
+
+  ScopedJavaLocalRef<jobjectArray> j_suspensions =
+      isSuccess(error) ? ToJavaArrayOfStrings(env, suspensions)
+                       : ToJavaArrayOfStrings(env, std::vector<std::string>());
+
+  RunObjectCallbackAndroid(callback, j_suspensions);
+}
+
+void UsageStatsBridge::OnSetSuspensionsDone(
+    ScopedJavaGlobalRef<jobject> callback,
+    UsageStatsDatabase::Error error) {
+  RunBooleanCallbackAndroid(callback, isSuccess(error));
+}
+
 // static
 void UsageStatsBridge::RegisterProfilePrefs(
     user_prefs::PrefRegistrySyncable* registry) {
diff --git a/chrome/browser/android/usage_stats/usage_stats_bridge.h b/chrome/browser/android/usage_stats/usage_stats_bridge.h
index 70a12ac7..9e237e2 100644
--- a/chrome/browser/android/usage_stats/usage_stats_bridge.h
+++ b/chrome/browser/android/usage_stats/usage_stats_bridge.h
@@ -6,9 +6,12 @@
 #define CHROME_BROWSER_ANDROID_USAGE_STATS_USAGE_STATS_BRIDGE_H_
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/android/scoped_java_ref.h"
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/android/usage_stats/usage_stats_database.h"
 
 namespace user_prefs {
 class PrefRegistrySyncable;
@@ -18,8 +21,7 @@
 
 using base::android::JavaParamRef;
 using base::android::JavaRef;
-
-class UsageStatsDatabase;
+using base::android::ScopedJavaGlobalRef;
 
 /* Native counterpart of UsageStatsBridge.java. Holds non-owning pointers to
  * native implementation to which operations are delegated. This bridge is
@@ -45,7 +47,7 @@
 
   void AddEvents(JNIEnv* j_env,
                  const JavaRef<jobject>& j_this,
-                 const JavaRef<jobjectArray>& j_events,
+                 const JavaRef<jobject>& j_events,
                  const JavaRef<jobject>& j_callback);
 
   void DeleteAllEvents(JNIEnv* j_env,
@@ -84,6 +86,13 @@
   static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
 
  private:
+  void OnGetAllSuspensionsDone(ScopedJavaGlobalRef<jobject> callback,
+                               UsageStatsDatabase::Error error,
+                               std::vector<std::string> suspensions);
+
+  void OnSetSuspensionsDone(ScopedJavaGlobalRef<jobject> callback,
+                            UsageStatsDatabase::Error error);
+
   std::unique_ptr<UsageStatsDatabase> usage_stats_database_;
 
   base::WeakPtrFactory<UsageStatsBridge> weak_ptr_factory_;
diff --git a/chrome/browser/apps/app_service/arc_apps.cc b/chrome/browser/apps/app_service/arc_apps.cc
index ed4ddbf..f74f668 100644
--- a/chrome/browser/apps/app_service/arc_apps.cc
+++ b/chrome/browser/apps/app_service/arc_apps.cc
@@ -281,23 +281,19 @@
 
 void ArcApps::OnAppRegistered(const std::string& app_id,
                               const ArcAppListPrefs::AppInfo& app_info) {
-  OnAppStatesChanged(app_id, app_info);
+  Publish(Convert(app_id, app_info));
 }
 
 void ArcApps::OnAppStatesChanged(const std::string& app_id,
                                  const ArcAppListPrefs::AppInfo& app_info) {
-  apps::mojom::AppPtr app = apps::mojom::App::New();
-  app->app_type = apps::mojom::AppType::kArc;
-  app->app_id = app_id;
-  app->readiness = NewReadiness(app_info.ready);
-  Publish(std::move(app));
+  Publish(Convert(app_id, app_info));
 }
 
 void ArcApps::OnAppRemoved(const std::string& app_id) {
   apps::mojom::AppPtr app = apps::mojom::App::New();
   app->app_type = apps::mojom::AppType::kArc;
   app->app_id = app_id;
-  app->readiness = NewReadiness(false);
+  app->readiness = apps::mojom::Readiness::kUninstalledByUser;
   Publish(std::move(app));
 }
 
@@ -386,7 +382,9 @@
 
   app->app_type = apps::mojom::AppType::kArc;
   app->app_id = app_id;
-  app->readiness = NewReadiness(app_info.ready);
+  // TODO(crbug.com/826982): examine app_info.suspended, and possibly have a
+  // corresponding 'suspended' apps::mojom::Readiness enum value??
+  app->readiness = apps::mojom::Readiness::kReady;
   app->name = app_info.name;
 
   app->icon_key = NewIconKey(app_id);
@@ -417,15 +415,6 @@
   return icon_key;
 }
 
-// static
-apps::mojom::Readiness ArcApps::NewReadiness(bool ready) {
-  // TODO(crbug.com/826982): examine ArcAppListPrefs::AppInfo::suspended, and
-  // possibly have a corresponding 'suspended' apps::mojom::Readiness enum
-  // value.
-  return ready ? apps::mojom::Readiness::kReady
-               : apps::mojom::Readiness::kUninstalledByUser;
-}
-
 void ArcApps::Publish(apps::mojom::AppPtr app) {
   subscribers_.ForAllPtrs([&app](apps::mojom::Subscriber* subscriber) {
     std::vector<apps::mojom::AppPtr> apps;
diff --git a/chrome/browser/apps/app_service/arc_apps.h b/chrome/browser/apps/app_service/arc_apps.h
index f6f31b8..2a473341 100644
--- a/chrome/browser/apps/app_service/arc_apps.h
+++ b/chrome/browser/apps/app_service/arc_apps.h
@@ -86,7 +86,6 @@
   apps::mojom::AppPtr Convert(const std::string& app_id,
                               const ArcAppListPrefs::AppInfo& app_info);
   apps::mojom::IconKeyPtr NewIconKey(const std::string& app_id);
-  static apps::mojom::Readiness NewReadiness(bool ready);
   void Publish(apps::mojom::AppPtr app);
 
   mojo::Binding<apps::mojom::Publisher> binding_;
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index 316215d..d2386e6 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -3075,7 +3075,7 @@
   // number.
   for (auto* download : saved_downloads) {
     const std::string port_string =
-        base::UintToString(embedded_test_server()->port());
+        base::NumberToString(embedded_test_server()->port());
     url::Replacements<char> replacements;
     replacements.SetPort(port_string.c_str(),
                          url::Component(0, port_string.size()));
@@ -3533,7 +3533,11 @@
 }
 
 // Tests that webviews do get garbage collected.
-IN_PROC_BROWSER_TEST_F(WebViewTest, Shim_TestGarbageCollect) {
+// This test is disabled because it relies on garbage collections triggered from
+// window.gc() to run precisely. This is not the case with unified heap where
+// they need to conservatively scan the stack, potentially keeping objects
+// alive. https://crbug.com/843903
+IN_PROC_BROWSER_TEST_F(WebViewTest, DISABLED_Shim_TestGarbageCollect) {
   TestHelper("testGarbageCollect", "web_view/shim", NO_TEST_SERVER);
   GetGuestViewManager()->WaitForSingleViewGarbageCollected();
 }
diff --git a/chrome/browser/apps/platform_apps/api/sync_file_system/extension_sync_event_observer.cc b/chrome/browser/apps/platform_apps/api/sync_file_system/extension_sync_event_observer.cc
index 87db521..ee5e474 100644
--- a/chrome/browser/apps/platform_apps/api/sync_file_system/extension_sync_event_observer.cc
+++ b/chrome/browser/apps/platform_apps/api/sync_file_system/extension_sync_event_observer.cc
@@ -112,7 +112,8 @@
   params->AppendString(sync_file_system::ToString(direction_enum));
 
   BroadcastOrDispatchEvent(
-      url.origin(), extensions::events::SYNC_FILE_SYSTEM_ON_FILE_STATUS_CHANGED,
+      url.origin().GetURL(),
+      extensions::events::SYNC_FILE_SYSTEM_ON_FILE_STATUS_CHANGED,
       sync_file_system::OnFileStatusChanged::kEventName, std::move(params));
 }
 
diff --git a/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_api_helpers.cc b/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_api_helpers.cc
index 7cd3cb49..4a6918d 100644
--- a/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_api_helpers.cc
+++ b/chrome/browser/apps/platform_apps/api/sync_file_system/sync_file_system_api_helpers.cc
@@ -119,7 +119,8 @@
           .AsUTF8Unsafe();
 
   std::string root_url =
-      storage::GetFileSystemRootURI(url.origin(), url.mount_type()).spec();
+      storage::GetFileSystemRootURI(url.origin().GetURL(), url.mount_type())
+          .spec();
   if (!url.filesystem_id().empty()) {
     root_url.append(url.filesystem_id());
     root_url.append("/");
@@ -128,8 +129,8 @@
   auto dict = std::make_unique<base::DictionaryValue>();
   dict->SetString("fileSystemType",
                   storage::GetFileSystemTypeString(url.mount_type()));
-  dict->SetString("fileSystemName",
-                  storage::GetFileSystemName(url.origin(), url.type()));
+  dict->SetString("fileSystemName", storage::GetFileSystemName(
+                                        url.origin().GetURL(), url.type()));
   dict->SetString("rootUrl", root_url);
   dict->SetString("filePath", file_path);
   dict->SetBoolean("isDirectory",
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 72ae575f..b85825d 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -2150,14 +2150,14 @@
   std::vector<AutofillProfile> profiles;
   for (int i = 0; i < kNumProfiles; i++) {
     AutofillProfile profile;
-    base::string16 name(base::IntToString16(i));
+    base::string16 name(base::NumberToString16(i));
     base::string16 email(name + ASCIIToUTF16("@example.com"));
-    base::string16 street = ASCIIToUTF16(
-        base::IntToString(base::RandInt(0, 10000)) + " " +
-        streets[base::RandInt(0, streets.size() - 1)]);
+    base::string16 street =
+        ASCIIToUTF16(base::NumberToString(base::RandInt(0, 10000)) + " " +
+                     streets[base::RandInt(0, streets.size() - 1)]);
     base::string16 city =
         ASCIIToUTF16(cities[base::RandInt(0, cities.size() - 1)]);
-    base::string16 zip(base::IntToString16(base::RandInt(0, 10000)));
+    base::string16 zip(base::NumberToString16(base::RandInt(0, 10000)));
     profile.SetRawInfo(NAME_FIRST, name);
     profile.SetRawInfo(EMAIL_ADDRESS, email);
     profile.SetRawInfo(ADDRESS_HOME_LINE1, street);
diff --git a/chrome/browser/bookmarks/bookmark_html_writer.cc b/chrome/browser/bookmarks/bookmark_html_writer.cc
index 01455bd..910d451 100644
--- a/chrome/browser/bookmarks/bookmark_html_writer.cc
+++ b/chrome/browser/bookmarks/bookmark_html_writer.cc
@@ -245,7 +245,7 @@
   bool WriteTime(const std::string& time_string) {
     int64_t internal_value;
     base::StringToInt64(time_string, &internal_value);
-    return Write(base::Int64ToString(
+    return Write(base::NumberToString(
         base::Time::FromInternalValue(internal_value).ToTimeT()));
   }
 
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 1dfec9ea..dc6f1b0c 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -17,8 +17,6 @@
         <structure name="IDR_GUEST_TAB_HTML" file="resources\ntp4\guest_tab.html" flattenhtml="true" type="chrome_html" />
       </if>
       <if expr="chromeos">
-        <structure name="IDR_LOGIN_HTML" file="resources\chromeos\login\login.html" flattenhtml="true" type="chrome_html" variables="OOBE=login" expand_variables="true"/>
-        <structure name="IDR_LOGIN_JS" file="resources\chromeos\login\login.js" flattenhtml="true" type="chrome_html" />
         <structure name="IDR_MD_LOGIN_HTML" file="resources\chromeos\login\md_login.html" flattenhtml="true" type="chrome_html" variables="OOBE=md_login" expand_variables="true"/>
         <structure name="IDR_MD_LOGIN_JS" file="resources\chromeos\login\md_login.js" flattenhtml="true" type="chrome_html" />
       </if>
@@ -152,36 +150,36 @@
       <include name="IDR_UKM_INTERNALS_JS" file="../../components/ukm/debug/ukm_internals.js" flattenhtml="true" compress="gzip" type="BINDATA" />
       <include name="IDR_UKM_INTERNALS_CSS" file="../../components/ukm/debug/ukm_internals.css" flattenhtml="true" compress="gzip" type="BINDATA" />
       <if expr="not is_android">
-        <include name="IDR_MD_DOWNLOADS_1X_INCOGNITO_MARKER_PNG" file="resources\downloads\1x\incognito_marker.png" type="BINDATA" />
-        <include name="IDR_MD_DOWNLOADS_2X_INCOGNITO_MARKER_PNG" file="resources\downloads\2x\incognito_marker.png" type="BINDATA" />
-        <include name="IDR_MD_DOWNLOADS_1X_NO_DOWNLOADS_PNG" file="resources\downloads\1x\no_downloads.png" type="BINDATA" />
-        <include name="IDR_MD_DOWNLOADS_2X_NO_DOWNLOADS_PNG" file="resources\downloads\2x\no_downloads.png" type="BINDATA" />
-        <include name="IDR_MD_DOWNLOADS_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\downloads\downloads.mojom-lite.js" use_base_dir="false" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_1X_INCOGNITO_MARKER_PNG" file="resources\downloads\1x\incognito_marker.png" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_2X_INCOGNITO_MARKER_PNG" file="resources\downloads\2x\incognito_marker.png" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_1X_NO_DOWNLOADS_PNG" file="resources\downloads\1x\no_downloads.png" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_2X_NO_DOWNLOADS_PNG" file="resources\downloads\2x\no_downloads.png" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_MOJO_LITE_JS" file="${root_gen_dir}\chrome\browser\ui\webui\downloads\downloads.mojom-lite.js" use_base_dir="false" type="BINDATA" />
         <if expr="optimize_webui">
           <then>
-            <include name="IDR_MD_DOWNLOADS_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\downloads\vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
-            <include name="IDR_MD_DOWNLOADS_VULCANIZED_P2_HTML" file="${root_gen_dir}\chrome\browser\resources\downloads\vulcanized.p2.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
-            <include name="IDR_MD_DOWNLOADS_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\downloads\crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" />
+            <include name="IDR_DOWNLOADS_VULCANIZED_HTML" file="${root_gen_dir}\chrome\browser\resources\downloads\vulcanized.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
+            <include name="IDR_DOWNLOADS_VULCANIZED_P2_HTML" file="${root_gen_dir}\chrome\browser\resources\downloads\vulcanized.p2.html" use_base_dir="false" flattenhtml="true" allowexternalscript="true" type="BINDATA" compress="gzip" />
+            <include name="IDR_DOWNLOADS_CRISPER_JS" file="${root_gen_dir}\chrome\browser\resources\downloads\crisper.js" use_base_dir="false" flattenhtml="true" type="BINDATA" compress="gzip" />
           </then>
           <else>
-            <include name="IDR_MD_DOWNLOADS_DOWNLOADS_HTML" file="resources\downloads\downloads.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_BROWSER_PROXY_HTML" file="resources\downloads\browser_proxy.html" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_BROWSER_PROXY_JS" file="resources\downloads\browser_proxy.js" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_CONSTANTS_HTML" file="resources\downloads\constants.html" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_CONSTANTS_JS" file="resources\downloads\constants.js" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_DOWNLOADS_JS" file="resources\downloads\downloads.js" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_I18N_SETUP_HTML" file="resources\downloads\i18n_setup.html" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_ICON_LOADER_HTML" file="resources\downloads\icon_loader.html" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_ICON_LOADER_JS" file="resources\downloads\icon_loader.js" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_ICONS_HTML" file="resources\downloads\icons.html" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_ITEM_HTML" file="resources\downloads\item.html" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_ITEM_JS" file="resources\downloads\item.js" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_MANAGER_HTML" file="resources\downloads\manager.html" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_MANAGER_JS" file="resources\downloads\manager.js" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_SEARCH_SERVICE_HTML" file="resources\downloads\search_service.html" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_SEARCH_SERVICE_JS" file="resources\downloads\search_service.js" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_TOOLBAR_HTML" file="resources\downloads\toolbar.html" type="BINDATA" />
-            <include name="IDR_MD_DOWNLOADS_TOOLBAR_JS" file="resources\downloads\toolbar.js" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_DOWNLOADS_HTML" file="resources\downloads\downloads.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_BROWSER_PROXY_HTML" file="resources\downloads\browser_proxy.html" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_BROWSER_PROXY_JS" file="resources\downloads\browser_proxy.js" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_CONSTANTS_HTML" file="resources\downloads\constants.html" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_CONSTANTS_JS" file="resources\downloads\constants.js" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_DOWNLOADS_JS" file="resources\downloads\downloads.js" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_I18N_SETUP_HTML" file="resources\downloads\i18n_setup.html" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_ICON_LOADER_HTML" file="resources\downloads\icon_loader.html" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_ICON_LOADER_JS" file="resources\downloads\icon_loader.js" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_ICONS_HTML" file="resources\downloads\icons.html" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_ITEM_HTML" file="resources\downloads\item.html" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_ITEM_JS" file="resources\downloads\item.js" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_MANAGER_HTML" file="resources\downloads\manager.html" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_MANAGER_JS" file="resources\downloads\manager.js" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_SEARCH_SERVICE_HTML" file="resources\downloads\search_service.html" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_SEARCH_SERVICE_JS" file="resources\downloads\search_service.js" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_TOOLBAR_HTML" file="resources\downloads\toolbar.html" type="BINDATA" />
+            <include name="IDR_DOWNLOADS_TOOLBAR_JS" file="resources\downloads\toolbar.js" type="BINDATA" />
           </else>
         </if>
       </if>
diff --git a/chrome/browser/browsing_data/browsing_data_database_helper.cc b/chrome/browser/browsing_data/browsing_data_database_helper.cc
index c40ca88..48715b8d 100644
--- a/chrome/browser/browsing_data/browsing_data_database_helper.cc
+++ b/chrome/browser/browsing_data/browsing_data_database_helper.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/browsing_data/browsing_data_database_helper.h"
 
 #include <tuple>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -12,6 +13,7 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/location.h"
+#include "base/stl_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/browsing_data/browsing_data_helper.h"
@@ -19,31 +21,16 @@
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/browser/storage_usage_info.h"
 #include "net/base/completion_callback.h"
 #include "net/base/net_errors.h"
 #include "storage/common/database/database_identifier.h"
 
 using content::BrowserContext;
 using content::BrowserThread;
+using content::StorageUsageInfo;
 using storage::DatabaseIdentifier;
 
-BrowsingDataDatabaseHelper::DatabaseInfo::DatabaseInfo(
-    const DatabaseIdentifier& identifier,
-    const std::string& database_name,
-    const std::string& description,
-    int64_t size,
-    base::Time last_modified)
-    : identifier(identifier),
-      database_name(database_name),
-      description(description),
-      size(size),
-      last_modified(last_modified) {}
-
-BrowsingDataDatabaseHelper::DatabaseInfo::DatabaseInfo(
-    const DatabaseInfo& other) = default;
-
-BrowsingDataDatabaseHelper::DatabaseInfo::~DatabaseInfo() {}
-
 BrowsingDataDatabaseHelper::BrowsingDataDatabaseHelper(Profile* profile)
     : tracker_(BrowserContext::GetDefaultStoragePartition(profile)
                    ->GetDatabaseTracker()) {}
@@ -59,37 +46,16 @@
       tracker_->task_runner(), FROM_HERE,
       base::BindOnce(
           [](storage::DatabaseTracker* tracker) {
-            std::list<DatabaseInfo> result;
+            std::list<StorageUsageInfo> result;
             std::vector<storage::OriginInfo> origins_info;
             if (tracker->GetAllOriginsInfo(&origins_info)) {
-              for (const storage::OriginInfo& origin : origins_info) {
-                DatabaseIdentifier identifier =
-                    DatabaseIdentifier::Parse(origin.GetOriginIdentifier());
-                // Non-websafe state is not considered browsing data.
-                if (!BrowsingDataHelper::HasWebScheme(identifier.ToOrigin()))
+              for (const storage::OriginInfo& info : origins_info) {
+                url::Origin origin = storage::GetOriginFromIdentifier(
+                    info.GetOriginIdentifier());
+                if (!BrowsingDataHelper::HasWebScheme(origin.GetURL()))
                   continue;
-                std::vector<base::string16> databases;
-                origin.GetAllDatabaseNames(&databases);
-                for (const base::string16& db : databases) {
-                  base::FilePath file_path = tracker->GetFullDBFilePath(
-                      origin.GetOriginIdentifier(), db);
-                  base::File::Info file_info;
-                  if (base::GetFileInfo(file_path, &file_info)) {
-                    result.push_back(DatabaseInfo(
-                        identifier, base::UTF16ToUTF8(db),
-                        base::UTF16ToUTF8(origin.GetDatabaseDescription(db)),
-                        file_info.size, file_info.last_modified));
-                  } else {
-                    // This is an incognito database, so the file is not
-                    // accessible. This browsing data record will not be
-                    // user-visible, but is enumerated by test code, so produce
-                    // a dummy record for testing.
-                    result.push_back(DatabaseInfo(
-                        identifier, base::UTF16ToUTF8(db),
-                        base::UTF16ToUTF8(origin.GetDatabaseDescription(db)), 0,
-                        base::Time()));
-                  }
-                }
+                result.emplace_back(origin, info.TotalSize(),
+                                    info.LastModified());
               }
             }
             return result;
@@ -98,30 +64,24 @@
       std::move(callback));
 }
 
-void BrowsingDataDatabaseHelper::DeleteDatabase(const std::string& origin,
-                                                const std::string& name) {
+void BrowsingDataDatabaseHelper::DeleteDatabase(const url::Origin& origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   tracker_->task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(base::IgnoreResult(
-                                    &storage::DatabaseTracker::DeleteDatabase),
-                                tracker_, origin, base::UTF8ToUTF16(name),
-                                net::CompletionCallback()));
+      FROM_HERE,
+      base::BindOnce(
+          base::IgnoreResult(&storage::DatabaseTracker::DeleteDataForOrigin),
+          tracker_, origin, net::CompletionCallback()));
 }
 
 CannedBrowsingDataDatabaseHelper::PendingDatabaseInfo::PendingDatabaseInfo(
-    const GURL& origin,
-    const std::string& name,
-    const std::string& description)
-    : origin(origin),
-      name(name),
-      description(description) {
-}
+    const GURL& origin)
+    : origin(origin) {}
 
 CannedBrowsingDataDatabaseHelper::PendingDatabaseInfo::~PendingDatabaseInfo() {}
 
 bool CannedBrowsingDataDatabaseHelper::PendingDatabaseInfo::operator<(
     const PendingDatabaseInfo& other) const {
-  return std::tie(origin, name) < std::tie(other.origin, other.name);
+  return origin < other.origin;
 }
 
 CannedBrowsingDataDatabaseHelper::CannedBrowsingDataDatabaseHelper(
@@ -129,14 +89,11 @@
     : BrowsingDataDatabaseHelper(profile) {
 }
 
-void CannedBrowsingDataDatabaseHelper::AddDatabase(
-    const GURL& origin,
-    const std::string& name,
-    const std::string& description) {
+void CannedBrowsingDataDatabaseHelper::AddDatabase(const GURL& origin) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   if (!BrowsingDataHelper::HasWebScheme(origin))
     return;  // Non-websafe state is not considered browsing data.
-  pending_database_info_.insert(PendingDatabaseInfo(origin, name, description));
+  pending_database_info_.insert(PendingDatabaseInfo(origin));
 }
 
 void CannedBrowsingDataDatabaseHelper::Reset() {
@@ -163,13 +120,10 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(!callback.is_null());
 
-  std::list<DatabaseInfo> result;
+  std::list<StorageUsageInfo> result;
   for (const PendingDatabaseInfo& info : pending_database_info_) {
-    DatabaseIdentifier identifier =
-        DatabaseIdentifier::CreateFromOrigin(info.origin);
-
-    result.push_back(
-        DatabaseInfo(identifier, info.name, info.description, 0, base::Time()));
+    result.push_back(content::StorageUsageInfo(url::Origin::Create(info.origin),
+                                               0, base::Time()));
   }
 
   base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
@@ -177,18 +131,13 @@
 }
 
 void CannedBrowsingDataDatabaseHelper::DeleteDatabase(
-    const std::string& origin_identifier,
-    const std::string& name) {
-  GURL origin =
-      storage::DatabaseIdentifier::Parse(origin_identifier).ToOrigin();
-  for (auto it = pending_database_info_.begin();
-       it != pending_database_info_.end(); ++it) {
-    if (it->origin == origin && it->name == name) {
-      pending_database_info_.erase(it);
-      break;
-    }
-  }
-  BrowsingDataDatabaseHelper::DeleteDatabase(origin_identifier, name);
+    const url::Origin& origin) {
+  GURL origin_url = origin.GetURL();
+  base::EraseIf(pending_database_info_,
+                [&origin_url](const PendingDatabaseInfo& info) {
+                  return info.origin == origin_url;
+                });
+  BrowsingDataDatabaseHelper::DeleteDatabase(origin);
 }
 
 CannedBrowsingDataDatabaseHelper::~CannedBrowsingDataDatabaseHelper() {}
diff --git a/chrome/browser/browsing_data/browsing_data_database_helper.h b/chrome/browser/browsing_data/browsing_data_database_helper.h
index ae4fa32..b700c15 100644
--- a/chrome/browser/browsing_data/browsing_data_database_helper.h
+++ b/chrome/browser/browsing_data/browsing_data_database_helper.h
@@ -20,6 +20,10 @@
 #include "storage/common/database/database_identifier.h"
 #include "url/gurl.h"
 
+namespace content {
+struct StorageUsageInfo;
+}
+
 class Profile;
 
 // This class fetches database information in the FILE thread, and notifies
@@ -30,25 +34,8 @@
 class BrowsingDataDatabaseHelper
     : public base::RefCountedThreadSafe<BrowsingDataDatabaseHelper> {
  public:
-  // Contains detailed information about a web database.
-  struct DatabaseInfo {
-    DatabaseInfo(const storage::DatabaseIdentifier& identifier,
-                 const std::string& database_name,
-                 const std::string& description,
-                 int64_t size,
-                 base::Time last_modified);
-    DatabaseInfo(const DatabaseInfo& other);
-    ~DatabaseInfo();
-
-    storage::DatabaseIdentifier identifier;
-    std::string database_name;
-    std::string description;
-    int64_t size;
-    base::Time last_modified;
-  };
-
   using FetchCallback =
-      base::OnceCallback<void(const std::list<DatabaseInfo>&)>;
+      base::OnceCallback<void(const std::list<content::StorageUsageInfo>&)>;
 
   explicit BrowsingDataDatabaseHelper(Profile* profile);
 
@@ -57,10 +44,9 @@
   // This must be called only in the UI thread.
   virtual void StartFetching(FetchCallback callback);
 
-  // Requests a single database to be deleted. This must be called in the UI
-  // thread.
-  virtual void DeleteDatabase(const std::string& origin_identifier,
-                              const std::string& name);
+  // Deletes all databases associated with an origin. This must be called in the
+  // UI thread.
+  virtual void DeleteDatabase(const url::Origin& origin);
 
  protected:
   friend class base::RefCountedThreadSafe<BrowsingDataDatabaseHelper>;
@@ -77,27 +63,22 @@
 // a parameter during construction.
 class CannedBrowsingDataDatabaseHelper : public BrowsingDataDatabaseHelper {
  public:
+  // TODO(jsbell): Replace this struct with just url::Origin.
   struct PendingDatabaseInfo {
-    PendingDatabaseInfo(const GURL& origin,
-                        const std::string& name,
-                        const std::string& description);
+    explicit PendingDatabaseInfo(const GURL& origin);
     ~PendingDatabaseInfo();
 
     // The operator is needed to store |PendingDatabaseInfo| objects in a set.
     bool operator<(const PendingDatabaseInfo& other) const;
 
     GURL origin;
-    std::string name;
-    std::string description;
   };
 
   explicit CannedBrowsingDataDatabaseHelper(Profile* profile);
 
   // Add a database to the set of canned databases that is returned by this
   // helper.
-  void AddDatabase(const GURL& origin,
-                   const std::string& name,
-                   const std::string& description);
+  void AddDatabase(const GURL& origin);
 
   // Clear the list of canned databases.
   void Reset();
@@ -113,8 +94,7 @@
 
   // BrowsingDataDatabaseHelper implementation.
   void StartFetching(FetchCallback callback) override;
-  void DeleteDatabase(const std::string& origin_identifier,
-                      const std::string& name) override;
+  void DeleteDatabase(const url::Origin& origin) override;
 
  private:
   ~CannedBrowsingDataDatabaseHelper() override;
diff --git a/chrome/browser/browsing_data/browsing_data_database_helper_browsertest.cc b/chrome/browser/browsing_data/browsing_data_database_helper_browsertest.cc
index 1eac320..43c18586 100644
--- a/chrome/browser/browsing_data/browsing_data_database_helper_browsertest.cc
+++ b/chrome/browser/browsing_data/browsing_data_database_helper_browsertest.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "content/public/browser/storage_partition.h"
+#include "content/public/browser/storage_usage_info.h"
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_utils.h"
 
@@ -23,10 +24,12 @@
 using content::BrowserThread;
 
 namespace {
-typedef BrowsingDataHelperCallback<BrowsingDataDatabaseHelper::DatabaseInfo>
-    TestCompletionCallback;
+using TestCompletionCallback =
+    BrowsingDataHelperCallback<content::StorageUsageInfo>;
 
-const char kTestIdentifier1[] = "http_www.google.com_0";
+const char kTestIdentifier1[] = "http_www.example.com_0";
+const url::Origin kTestOrigin =
+    url::Origin::Create(GURL("http://www.example.com"));
 
 const char kTestIdentifierExtension[] =
   "chrome-extension_behllobkkfkfnphdnhnkndlbkcpglgmj_0";
@@ -70,74 +73,58 @@
   CreateDatabases();
   scoped_refptr<BrowsingDataDatabaseHelper> database_helper(
       new BrowsingDataDatabaseHelper(browser()->profile()));
-  std::list<BrowsingDataDatabaseHelper::DatabaseInfo> database_info_list;
+  std::list<content::StorageUsageInfo> database_info_list;
   base::RunLoop run_loop;
   database_helper->StartFetching(base::BindLambdaForTesting(
-      [&](const std::list<BrowsingDataDatabaseHelper::DatabaseInfo>& list) {
+      [&](const std::list<content::StorageUsageInfo>& list) {
         database_info_list = list;
         run_loop.Quit();
       }));
   run_loop.Run();
   ASSERT_EQ(1UL, database_info_list.size());
-  EXPECT_EQ(std::string(kTestIdentifier1),
-            database_info_list.begin()->identifier.ToString());
+  EXPECT_EQ(kTestOrigin, database_info_list.begin()->origin);
 }
 
 IN_PROC_BROWSER_TEST_F(BrowsingDataDatabaseHelperTest, CannedAddDatabase) {
-  const GURL origin1("http://host1:1/");
-  const GURL origin2("http://host2:1/");
-  const char origin_str1[] = "http_host1_1";
-  const char origin_str2[] = "http_host2_1";
-  const char db1[] = "db1";
-  const char db2[] = "db2";
-  const char db3[] = "db3";
+  const url::Origin origin1 = url::Origin::Create(GURL("http://host1:1/"));
+  const url::Origin origin2 = url::Origin::Create(GURL("http://host2:1/"));
 
   scoped_refptr<CannedBrowsingDataDatabaseHelper> helper(
       new CannedBrowsingDataDatabaseHelper(browser()->profile()));
-  helper->AddDatabase(origin1, db1, std::string());
-  helper->AddDatabase(origin1, db2, std::string());
-  helper->AddDatabase(origin2, db3, std::string());
+  helper->AddDatabase(origin1.GetURL());
+  helper->AddDatabase(origin1.GetURL());
+  helper->AddDatabase(origin2.GetURL());
 
   TestCompletionCallback callback;
   helper->StartFetching(
       base::Bind(&TestCompletionCallback::callback,
                  base::Unretained(&callback)));
 
-  std::list<BrowsingDataDatabaseHelper::DatabaseInfo> result =
-      callback.result();
+  std::list<content::StorageUsageInfo> result = callback.result();
 
-  ASSERT_EQ(3u, result.size());
+  ASSERT_EQ(2u, result.size());
   auto info = result.begin();
-  EXPECT_EQ(origin_str1, info->identifier.ToString());
-  EXPECT_STREQ(db1, info->database_name.c_str());
+  EXPECT_EQ(origin1, info->origin);
   info++;
-  EXPECT_EQ(origin_str1, info->identifier.ToString());
-  EXPECT_STREQ(db2, info->database_name.c_str());
-  info++;
-  EXPECT_EQ(origin_str2, info->identifier.ToString());
-  EXPECT_STREQ(db3, info->database_name.c_str());
+  EXPECT_EQ(origin2, info->origin);
 }
 
 IN_PROC_BROWSER_TEST_F(BrowsingDataDatabaseHelperTest, CannedUnique) {
-  const GURL origin("http://host1:1/");
-  const char origin_str[] = "http_host1_1";
-  const char db[] = "db1";
+  const url::Origin origin = url::Origin::Create(GURL("http://host1:1/"));
 
   scoped_refptr<CannedBrowsingDataDatabaseHelper> helper(
       new CannedBrowsingDataDatabaseHelper(browser()->profile()));
-  helper->AddDatabase(origin, db, std::string());
-  helper->AddDatabase(origin, db, std::string());
+  helper->AddDatabase(origin.GetURL());
+  helper->AddDatabase(origin.GetURL());
 
   TestCompletionCallback callback;
   helper->StartFetching(
       base::Bind(&TestCompletionCallback::callback,
                  base::Unretained(&callback)));
 
-  std::list<BrowsingDataDatabaseHelper::DatabaseInfo> result =
-      callback.result();
+  std::list<content::StorageUsageInfo> result = callback.result();
 
   ASSERT_EQ(1u, result.size());
-  EXPECT_EQ(origin_str, result.begin()->identifier.ToString());
-  EXPECT_STREQ(db, result.begin()->database_name.c_str());
+  EXPECT_EQ(origin, result.begin()->origin);
 }
 }  // namespace
diff --git a/chrome/browser/browsing_data/browsing_data_database_helper_unittest.cc b/chrome/browser/browsing_data/browsing_data_database_helper_unittest.cc
index 833f9d7..55090ff4 100644
--- a/chrome/browser/browsing_data/browsing_data_database_helper_unittest.cc
+++ b/chrome/browser/browsing_data/browsing_data_database_helper_unittest.cc
@@ -21,13 +21,12 @@
   TestingProfile profile;
 
   const GURL origin("http://host1:1/");
-  const char db[] = "db1";
 
   scoped_refptr<CannedBrowsingDataDatabaseHelper> helper(
       new CannedBrowsingDataDatabaseHelper(&profile));
 
   ASSERT_TRUE(helper->empty());
-  helper->AddDatabase(origin, db, std::string());
+  helper->AddDatabase(origin);
   ASSERT_FALSE(helper->empty());
   helper->Reset();
   ASSERT_TRUE(helper->empty());
@@ -37,27 +36,20 @@
   TestingProfile profile;
 
   const GURL origin1("http://host1:9000");
-  const char db1[] = "db1";
-
   const GURL origin2("http://example.com");
-  const char db2[] = "db2";
-
   const GURL origin3("http://foo.example.com");
-  const char db3[] = "db3";
 
   scoped_refptr<CannedBrowsingDataDatabaseHelper> helper(
       new CannedBrowsingDataDatabaseHelper(&profile));
 
   EXPECT_TRUE(helper->empty());
-  helper->AddDatabase(origin1, db1, std::string());
-  helper->AddDatabase(origin2, db2, std::string());
-  helper->AddDatabase(origin3, db3, std::string());
+  helper->AddDatabase(origin1);
+  helper->AddDatabase(origin2);
+  helper->AddDatabase(origin3);
   EXPECT_EQ(3u, helper->GetDatabaseCount());
-  helper->DeleteDatabase(
-      DatabaseIdentifier::CreateFromOrigin(origin2).ToString(), db1);
-  EXPECT_EQ(3u, helper->GetDatabaseCount());
-  helper->DeleteDatabase(
-      DatabaseIdentifier::CreateFromOrigin(origin2).ToString(), db2);
+  helper->DeleteDatabase(url::Origin::Create(origin2));
+  EXPECT_EQ(2u, helper->GetDatabaseCount());
+  helper->DeleteDatabase(url::Origin::Create(origin2));
   EXPECT_EQ(2u, helper->GetDatabaseCount());
 }
 
@@ -66,15 +58,14 @@
 
   const GURL origin1("chrome-extension://abcdefghijklmnopqrstuvwxyz/");
   const GURL origin2("chrome-devtools://abcdefghijklmnopqrstuvwxyz/");
-  const char db[] = "db1";
 
   scoped_refptr<CannedBrowsingDataDatabaseHelper> helper(
       new CannedBrowsingDataDatabaseHelper(&profile));
 
   ASSERT_TRUE(helper->empty());
-  helper->AddDatabase(origin1, db, std::string());
+  helper->AddDatabase(origin1);
   ASSERT_TRUE(helper->empty());
-  helper->AddDatabase(origin2, db, std::string());
+  helper->AddDatabase(origin2);
   ASSERT_TRUE(helper->empty());
   helper->Reset();
   ASSERT_TRUE(helper->empty());
diff --git a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
index be047438..0c40ff35 100644
--- a/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
+++ b/chrome/browser/browsing_data/chrome_browsing_data_remover_delegate_unittest.cc
@@ -2633,8 +2633,8 @@
   // Add a bookmark with a visited timestamp before the deletion interval.
   bookmarks::BookmarkNode::MetaInfoMap meta_info = {
       {"last_visited",
-       base::Int64ToString((delete_begin - base::TimeDelta::FromSeconds(1))
-                               .ToInternalValue())}};
+       base::NumberToString((delete_begin - base::TimeDelta::FromSeconds(1))
+                                .ToInternalValue())}};
   bookmark_model->AddURLWithCreationTimeAndMetaInfo(
       bookmark_model->mobile_node(), 0, base::ASCIIToUTF16("my title"),
       GURL("http://foo-2.org/"), delete_begin - base::TimeDelta::FromDays(1),
diff --git a/chrome/browser/browsing_data/cookies_tree_model.cc b/chrome/browser/browsing_data/cookies_tree_model.cc
index dd13749b..1256508 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <functional>
 #include <map>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -203,10 +204,10 @@
 }
 
 CookieTreeNode::DetailedInfo& CookieTreeNode::DetailedInfo::InitDatabase(
-    const BrowsingDataDatabaseHelper::DatabaseInfo* database_info) {
+    const content::StorageUsageInfo* usage_info) {
   Init(TYPE_DATABASE);
-  this->database_info = database_info;
-  origin = url::Origin::Create(database_info->identifier.ToOrigin());
+  this->usage_info = usage_info;
+  origin = usage_info->origin;
   return *this;
 }
 
@@ -414,16 +415,12 @@
  public:
   friend class CookieTreeDatabasesNode;
 
-  // |database_info| should remain valid at least as long as the
+  // |usage_info| should remain valid at least as long as the
   // CookieTreeDatabaseNode is valid.
   explicit CookieTreeDatabaseNode(
-      std::list<BrowsingDataDatabaseHelper::DatabaseInfo>::iterator
-          database_info)
-      : CookieTreeNode(database_info->database_name.empty()
-                           ? l10n_util::GetStringUTF16(
-                                 IDS_COOKIES_WEB_DATABASE_UNNAMED_NAME)
-                           : base::UTF8ToUTF16(database_info->database_name)),
-        database_info_(database_info) {}
+      std::list<content::StorageUsageInfo>::iterator usage_info)
+      : CookieTreeNode(base::UTF8ToUTF16(usage_info->origin.Serialize())),
+        usage_info_(usage_info) {}
 
   ~CookieTreeDatabaseNode() override {}
 
@@ -431,22 +428,23 @@
     LocalDataContainer* container = GetLocalDataContainerForNode(this);
 
     if (container) {
-      container->database_helper_->DeleteDatabase(
-          database_info_->identifier.ToString(), database_info_->database_name);
-      container->database_info_list_.erase(database_info_);
+      container->database_helper_->DeleteDatabase(usage_info_->origin);
+      container->database_info_list_.erase(usage_info_);
     }
   }
 
   DetailedInfo GetDetailedInfo() const override {
-    return DetailedInfo().InitDatabase(&*database_info_);
+    return DetailedInfo().InitDatabase(&*usage_info_);
   }
 
-  int64_t InclusiveSize() const override { return database_info_->size; }
+  int64_t InclusiveSize() const override {
+    return usage_info_->total_size_bytes;
+  }
 
  private:
   // |database_info_| is expected to remain valid as long as the
   // CookieTreeDatabaseNode is valid.
-  std::list<BrowsingDataDatabaseHelper::DatabaseInfo>::iterator database_info_;
+  std::list<content::StorageUsageInfo>::iterator usage_info_;
 
   DISALLOW_COPY_AND_ASSIGN(CookieTreeDatabaseNode);
 };
@@ -1648,11 +1646,11 @@
   notifier->StartBatchUpdate();
   for (auto database_info = container->database_info_list_.begin();
        database_info != container->database_info_list_.end(); ++database_info) {
-    GURL origin(database_info->identifier.ToOrigin());
-
-    if (filter.empty() || (CookieTreeHostNode::TitleForUrl(origin)
-                               .find(filter) != base::string16::npos)) {
-      CookieTreeHostNode* host_node = root->GetOrCreateHostNode(origin);
+    if (filter.empty() ||
+        (CookieTreeHostNode::TitleForUrl(database_info->origin.GetURL())
+             .find(filter) != base::string16::npos)) {
+      CookieTreeHostNode* host_node =
+          root->GetOrCreateHostNode(database_info->origin.GetURL());
       CookieTreeDatabasesNode* databases_node =
           host_node->GetOrCreateDatabasesNode();
       databases_node->AddDatabaseNode(
diff --git a/chrome/browser/browsing_data/cookies_tree_model.h b/chrome/browser/browsing_data/cookies_tree_model.h
index 7d03855..d085479 100644
--- a/chrome/browser/browsing_data/cookies_tree_model.h
+++ b/chrome/browser/browsing_data/cookies_tree_model.h
@@ -108,8 +108,7 @@
     DetailedInfo& Init(NodeType type);
     DetailedInfo& InitHost(const GURL& origin);
     DetailedInfo& InitCookie(const net::CanonicalCookie* cookie);
-    DetailedInfo& InitDatabase(
-        const BrowsingDataDatabaseHelper::DatabaseInfo* database_info);
+    DetailedInfo& InitDatabase(const content::StorageUsageInfo* usage_info);
     DetailedInfo& InitLocalStorage(
         const content::StorageUsageInfo* local_storage_info);
     DetailedInfo& InitSessionStorage(
@@ -135,9 +134,9 @@
     NodeType node_type;
     url::Origin origin;
     const net::CanonicalCookie* cookie = nullptr;
-    const BrowsingDataDatabaseHelper::DatabaseInfo* database_info = nullptr;
     const blink::mojom::AppCacheInfo* appcache_info = nullptr;
-    // Used for IndexedDB, Service Worker, and Cache Storage node types.
+    // Used for Database (WebSQL), IndexedDB, Service Worker, and Cache
+    // Storage node types.
     const content::StorageUsageInfo* usage_info = nullptr;
     const BrowsingDataFileSystemHelper::FileSystemInfo* file_system_info =
         nullptr;
diff --git a/chrome/browser/browsing_data/cookies_tree_model_unittest.cc b/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
index e3dca69e..8ab24d0 100644
--- a/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
+++ b/chrome/browser/browsing_data/cookies_tree_model_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/browsing_data/cookies_tree_model.h"
 
 #include <string>
+#include <utility>
 
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
@@ -173,8 +174,8 @@
       // foo1 -> cookies -> a,
       // foo2 -> cookies -> b,
       // foo3 -> cookies -> c,
-      // gdbhost1 -> database -> db1,
-      // gdbhost2 -> database -> db2,
+      // gdbhost1 -> database -> http://gdbhost1:1/,
+      // gdbhost2 -> database -> http://gdbhost2:2/,
       // host1 -> localstorage -> http://host1:1/,
       //       -> sessionstorage -> http://host1:1/,
       // host2 -> localstorage -> http://host2:2/.
@@ -195,7 +196,8 @@
       // xyz.com -> flash_lsos
       EXPECT_EQ(71, cookies_model->GetRoot()->GetTotalNodeCount());
       EXPECT_EQ("A,B,C", GetDisplayedCookies(cookies_model.get()));
-      EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+      EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+                GetDisplayedDatabases(cookies_model.get()));
       EXPECT_EQ("http://host1:1/,http://host2:2/",
                 GetDisplayedLocalStorages(cookies_model.get()));
       EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -274,13 +276,12 @@
     // compatibility with tests. The tests should be updated once all
     // appropriate parts have been migrated to url::Origin.
     switch (node_type) {
-      case CookieTreeNode::DetailedInfo::TYPE_DATABASE:
-        return node->GetDetailedInfo().database_info->database_name + ",";
       case CookieTreeNode::DetailedInfo::TYPE_COOKIE:
         return node->GetDetailedInfo().cookie->Name() + ",";
       case CookieTreeNode::DetailedInfo::TYPE_APPCACHE:
         return node->GetDetailedInfo().appcache_info->manifest_url.spec() + ",";
       case CookieTreeNode::DetailedInfo::TYPE_CACHE_STORAGE:
+      case CookieTreeNode::DetailedInfo::TYPE_DATABASE:
       case CookieTreeNode::DetailedInfo::TYPE_INDEXED_DB:
       case CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE:
       case CookieTreeNode::DetailedInfo::TYPE_SERVICE_WORKER:
@@ -442,7 +443,7 @@
     SCOPED_TRACE("Before removing");
     EXPECT_EQ("A,B,C",
               GetDisplayedCookies(cookies_model.get()));
-    EXPECT_EQ("db1,db2",
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
               GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
@@ -542,7 +543,8 @@
   {
     SCOPED_TRACE("`xyz.com` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -572,7 +574,8 @@
   {
     SCOPED_TRACE("`swhost2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -601,7 +604,8 @@
   {
     SCOPED_TRACE("`swhost1` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -630,7 +634,8 @@
   {
     SCOPED_TRACE("`sharedworkerhost2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -656,7 +661,8 @@
   {
     SCOPED_TRACE("`sharedworkerhost1` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -681,7 +687,8 @@
   {
     SCOPED_TRACE("`quotahost2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -707,7 +714,8 @@
   {
     SCOPED_TRACE("`quotahost1` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -731,7 +739,8 @@
   {
     SCOPED_TRACE("`media2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -755,7 +764,8 @@
   {
     SCOPED_TRACE("`media1` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -778,7 +788,8 @@
   {
     SCOPED_TRACE("`idbhost2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -801,7 +812,8 @@
   {
     SCOPED_TRACE("`idbhost1` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -824,7 +836,8 @@
   {
     SCOPED_TRACE("`host2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/",
@@ -847,7 +860,8 @@
   {
     SCOPED_TRACE("`host1` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
     EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
@@ -862,12 +876,12 @@
     EXPECT_EQ(31, cookies_model->GetRoot()->GetTotalNodeCount());
   }
 
-  // gdbhost2 -> database -> db2 (3 objects)
+  // gdbhost2 -> database -> http://gdbhost2:2/ (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(9));
   {
     SCOPED_TRACE("`gdbhost2` removed.");
     EXPECT_STREQ("A,B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("", GetDisplayedSessionStorages(cookies_model.get()));
     EXPECT_EQ("http://fshost1:1/,http://fshost2:2/,http://fshost3:3/",
@@ -881,7 +895,7 @@
     EXPECT_EQ("", GetDisplayedMediaLicenses(cookies_model.get()));
     EXPECT_EQ(28, cookies_model->GetRoot()->GetTotalNodeCount());
   }
-  // gdbhost1 -> database -> db1 (3 objects)
+  // gdbhost1 -> database -> http://gdbhost1:1/ (3 objects)
   DeleteStoredObjects(cookies_model->GetRoot()->GetChild(8));
   {
     SCOPED_TRACE("`gdbhost1` removed.");
@@ -1066,7 +1080,8 @@
     // 69 because in this case, the origin remains, although the COOKIES
     // node beneath it has been deleted.
     EXPECT_EQ(69, cookies_model->GetRoot()->GetTotalNodeCount());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1093,7 +1108,7 @@
   {
     SCOPED_TRACE("First database origin removed");
     EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost2:2/", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1121,7 +1136,7 @@
   {
     SCOPED_TRACE("First local storage origin removed");
     EXPECT_STREQ("B,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost2:2/", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1154,7 +1169,8 @@
   {
     SCOPED_TRACE("Second origin COOKIES node removed");
     EXPECT_STREQ("A,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1184,7 +1200,7 @@
   {
     SCOPED_TRACE("First database origin removed");
     EXPECT_STREQ("A,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost2:2/", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1212,7 +1228,7 @@
   {
     SCOPED_TRACE("First local storage origin removed");
     EXPECT_STREQ("A,C", GetDisplayedCookies(cookies_model.get()).c_str());
-    EXPECT_EQ("db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost2:2/", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1292,8 +1308,8 @@
     // foo1 -> cookies -> a,
     // foo2 -> cookies -> b,
     // foo3 -> cookies -> c,d
-    // dbhost1 -> database -> db1,
-    // dbhost2 -> database -> db2,
+    // dbhost1 -> database -> http://gdbhost1:1/,
+    // dbhost2 -> database -> http://gdbhost2:2/,
     // host1 -> localstorage -> http://host1:1/,
     //       -> sessionstorage -> http://host1:1/,
     // host2 -> localstorage -> http://host2:2/,
@@ -1311,7 +1327,8 @@
     // sharedworkerhost2 -> shared worker -> https://sharedworkerhost2:2
     EXPECT_EQ(64, cookies_model.GetRoot()->GetTotalNodeCount());
     EXPECT_STREQ("A,B,C,D", GetDisplayedCookies(&cookies_model).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(&cookies_model));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(&cookies_model));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(&cookies_model));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1334,7 +1351,8 @@
   {
     SCOPED_TRACE("Third cookie origin removed");
     EXPECT_STREQ("A,B", GetDisplayedCookies(&cookies_model).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(&cookies_model));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(&cookies_model));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(&cookies_model));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1413,8 +1431,8 @@
     // foo1 -> cookies -> a,
     // foo2 -> cookies -> b,
     // foo3 -> cookies -> c,d,e
-    // dbhost1 -> database -> db1,
-    // dbhost2 -> database -> db2,
+    // dbhost1 -> database -> http://gdbhost1:1/,
+    // dbhost2 -> database -> http://gdbhost2:2/,
     // host1 -> localstorage -> http://host1:1/,
     //       -> sessionstorage -> http://host1:1/,
     // host2 -> localstorage -> http://host2:2/,
@@ -1432,7 +1450,8 @@
     // sharedworkerhost2 -> shared worker -> https://sharedworkerhost2:2
     EXPECT_EQ(65, cookies_model.GetRoot()->GetTotalNodeCount());
     EXPECT_STREQ("A,B,C,D,E", GetDisplayedCookies(&cookies_model).c_str());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(&cookies_model));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(&cookies_model));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(&cookies_model));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1457,7 +1476,8 @@
     SCOPED_TRACE("Middle cookie in third cookie origin removed");
     EXPECT_STREQ("A,B,C,E", GetDisplayedCookies(&cookies_model).c_str());
     EXPECT_EQ(64, cookies_model.GetRoot()->GetTotalNodeCount());
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(&cookies_model));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(&cookies_model));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(&cookies_model));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1950,7 +1970,8 @@
   {
     SCOPED_TRACE("Search for everything");
     EXPECT_EQ("A,B,C", GetDisplayedCookies(cookies_model.get()));
-    EXPECT_EQ("db1,db2", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/,http://gdbhost2:2/",
+              GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/,http://host2:2/",
@@ -1978,7 +1999,7 @@
   {
     SCOPED_TRACE("Search for '1'");
     EXPECT_EQ("A", GetDisplayedCookies(cookies_model.get()));
-    EXPECT_EQ("db1", GetDisplayedDatabases(cookies_model.get()));
+    EXPECT_EQ("http://gdbhost1:1/", GetDisplayedDatabases(cookies_model.get()));
     EXPECT_EQ("http://host1:1/",
               GetDisplayedLocalStorages(cookies_model.get()));
     EXPECT_EQ("http://host1:1/",
diff --git a/chrome/browser/browsing_data/local_data_container.h b/chrome/browser/browsing_data/local_data_container.h
index 74524e98..966292c 100644
--- a/chrome/browser/browsing_data/local_data_container.h
+++ b/chrome/browser/browsing_data/local_data_container.h
@@ -43,7 +43,7 @@
 namespace {
 
 typedef std::list<net::CanonicalCookie> CookieList;
-typedef std::list<BrowsingDataDatabaseHelper::DatabaseInfo> DatabaseInfoList;
+typedef std::list<content::StorageUsageInfo> DatabaseInfoList;
 typedef std::list<content::StorageUsageInfo> LocalStorageInfoList;
 typedef std::list<content::StorageUsageInfo> SessionStorageInfoList;
 typedef std::list<content::StorageUsageInfo> IndexedDBInfoList;
diff --git a/chrome/browser/browsing_data/mock_browsing_data_database_helper.cc b/chrome/browser/browsing_data/mock_browsing_data_database_helper.cc
index 56eb7b9..a3d3fd51 100644
--- a/chrome/browser/browsing_data/mock_browsing_data_database_helper.cc
+++ b/chrome/browser/browsing_data/mock_browsing_data_database_helper.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/browsing_data/mock_browsing_data_database_helper.h"
 
+#include <utility>
+
 #include "base/callback.h"
 #include "base/stl_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -20,27 +22,20 @@
   callback_ = std::move(callback);
 }
 
-void MockBrowsingDataDatabaseHelper::DeleteDatabase(
-    const std::string& origin,
-    const std::string& name) {
-  std::string key = origin + ":" + name;
-  ASSERT_TRUE(base::ContainsKey(databases_, key));
-  last_deleted_origin_ = origin;
-  last_deleted_db_ = name;
-  databases_[key] = false;
+void MockBrowsingDataDatabaseHelper::DeleteDatabase(const url::Origin& origin) {
+  const std::string identifier = storage::GetIdentifierFromOrigin(origin);
+  ASSERT_TRUE(base::ContainsKey(databases_, identifier));
+  last_deleted_origin_ = identifier;
+  databases_[identifier] = false;
 }
 
 void MockBrowsingDataDatabaseHelper::AddDatabaseSamples() {
-  storage::DatabaseIdentifier id1 =
-      storage::DatabaseIdentifier::Parse("http_gdbhost1_1");
-  response_.push_back(BrowsingDataDatabaseHelper::DatabaseInfo(
-      id1, "db1", "description 1", 1, base::Time()));
-  databases_["http_gdbhost1_1:db1"] = true;
-  storage::DatabaseIdentifier id2 =
-      storage::DatabaseIdentifier::Parse("http_gdbhost2_2");
-  response_.push_back(BrowsingDataDatabaseHelper::DatabaseInfo(
-      id2, "db2", "description 2", 2, base::Time()));
-  databases_["http_gdbhost2_2:db2"] = true;
+  response_.push_back(content::StorageUsageInfo(
+      url::Origin::Create(GURL("http://gdbhost1:1")), 1, base::Time()));
+  databases_["http_gdbhost1_1"] = true;
+  response_.push_back(content::StorageUsageInfo(
+      url::Origin::Create(GURL("http://gdbhost2:2")), 2, base::Time()));
+  databases_["http_gdbhost2_2"] = true;
 }
 
 void MockBrowsingDataDatabaseHelper::Notify() {
diff --git a/chrome/browser/browsing_data/mock_browsing_data_database_helper.h b/chrome/browser/browsing_data/mock_browsing_data_database_helper.h
index 9594a5f..55fe3945 100644
--- a/chrome/browser/browsing_data/mock_browsing_data_database_helper.h
+++ b/chrome/browser/browsing_data/mock_browsing_data_database_helper.h
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "chrome/browser/browsing_data/browsing_data_database_helper.h"
+#include "content/public/browser/storage_usage_info.h"
 
 // Mock for BrowsingDataDatabaseHelper.
 // Use AddDatabaseSamples() or add directly to response_ list, then call
@@ -22,8 +23,7 @@
 
   void StartFetching(FetchCallback callback) override;
 
-  void DeleteDatabase(const std::string& origin,
-                      const std::string& name) override;
+  void DeleteDatabase(const url::Origin& origin) override;
 
   // Adds some DatabaseInfo samples.
   void AddDatabaseSamples();
@@ -50,7 +50,7 @@
   // Stores which databases exist.
   std::map<const std::string, bool> databases_;
 
-  std::list<DatabaseInfo> response_;
+  std::list<content::StorageUsageInfo> response_;
 
   DISALLOW_COPY_AND_ASSIGN(MockBrowsingDataDatabaseHelper);
 };
diff --git a/chrome/browser/browsing_data/site_data_size_collector.cc b/chrome/browser/browsing_data/site_data_size_collector.cc
index d73fa30..2bf1e2e 100644
--- a/chrome/browser/browsing_data/site_data_size_collector.cc
+++ b/chrome/browser/browsing_data/site_data_size_collector.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/browsing_data/site_data_size_collector.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/task/post_task.h"
@@ -153,7 +155,7 @@
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   int64_t total_size = 0;
   for (const auto& database_info : database_info_list)
-    total_size += database_info.size;
+    total_size += database_info.total_size_bytes;
   OnStorageSizeFetched(total_size);
 }
 
diff --git a/chrome/browser/browsing_data/site_data_size_collector.h b/chrome/browser/browsing_data/site_data_size_collector.h
index 7e9f519..6bb5221 100644
--- a/chrome/browser/browsing_data/site_data_size_collector.h
+++ b/chrome/browser/browsing_data/site_data_size_collector.h
@@ -5,6 +5,10 @@
 #ifndef CHROME_BROWSER_BROWSING_DATA_SITE_DATA_SIZE_COLLECTOR_H_
 #define CHROME_BROWSER_BROWSING_DATA_SITE_DATA_SIZE_COLLECTOR_H_
 
+#include <list>
+#include <string>
+#include <vector>
+
 #include "base/memory/weak_ptr.h"
 #include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
 #include "chrome/browser/browsing_data/browsing_data_cache_storage_helper.h"
@@ -21,7 +25,7 @@
 namespace {
 
 typedef std::list<net::CanonicalCookie> CookieList;
-typedef std::list<BrowsingDataDatabaseHelper::DatabaseInfo> DatabaseInfoList;
+typedef std::list<content::StorageUsageInfo> DatabaseInfoList;
 typedef std::list<content::StorageUsageInfo> LocalStorageInfoList;
 typedef std::list<content::StorageUsageInfo> IndexedDBInfoList;
 typedef std::list<BrowsingDataFileSystemHelper::FileSystemInfo>
diff --git a/chrome/browser/certificate_manager_model.cc b/chrome/browser/certificate_manager_model.cc
index 775dc356..15ce9af 100644
--- a/chrome/browser/certificate_manager_model.cc
+++ b/chrome/browser/certificate_manager_model.cc
@@ -236,11 +236,15 @@
       bool untrusted = cert_db_->IsUntrusted(cert.get());
       bool hardware_backed = cert_db_->IsHardwareBacked(cert.get());
       bool web_trust_anchor = cert_db_->IsWebTrustAnchor(cert.get());
+      bool device_wide = false;
+#if defined(OS_CHROMEOS)
+      device_wide = cert_db_->IsCertificateOnSystemSlot(cert.get());
+#endif
       base::string16 name = GetName(cert.get(), hardware_backed);
       cert_infos.push_back(std::make_unique<CertificateManagerModel::CertInfo>(
           std::move(cert), type, name, read_only, untrusted,
           CertificateManagerModel::CertInfo::Source::kPlatform,
-          web_trust_anchor, hardware_backed));
+          web_trust_anchor, hardware_backed, device_wide));
     }
 
     SetCertInfos(std::move(cert_infos));
@@ -354,7 +358,7 @@
           false /* untrusted */,
           CertificateManagerModel::CertInfo::Source::kPolicy,
           policy_web_trusted /* web_trust_anchor */,
-          false /* hardware_backed */));
+          false /* hardware_backed */, false /* device_wide */));
     }
 
     SetCertInfos(std::move(cert_infos));
@@ -422,7 +426,8 @@
           std::move(nss_cert), net::CertType::USER_CERT /* type */,
           display_name, true /* read_only */, false /* untrusted */,
           CertificateManagerModel::CertInfo::Source::kExtension,
-          false /* web_trust_anchor */, false /* hardware_backed */));
+          false /* web_trust_anchor */, false /* hardware_backed */,
+          false /* device_wide */));
     }
 
     SetCertInfos(std::move(cert_infos));
@@ -446,7 +451,8 @@
                                             bool untrusted,
                                             Source source,
                                             bool web_trust_anchor,
-                                            bool hardware_backed)
+                                            bool hardware_backed,
+                                            bool device_wide)
     : cert_(std::move(cert)),
       type_(type),
       name_(std::move(name)),
@@ -454,7 +460,8 @@
       untrusted_(untrusted),
       source_(source),
       web_trust_anchor_(web_trust_anchor),
-      hardware_backed_(hardware_backed) {}
+      hardware_backed_(hardware_backed),
+      device_wide_(device_wide) {}
 
 CertificateManagerModel::CertInfo::~CertInfo() {}
 
@@ -465,7 +472,7 @@
       net::x509_util::DupCERTCertificate(cert_info->cert()), cert_info->type(),
       cert_info->name(), cert_info->read_only(), cert_info->untrusted(),
       cert_info->source(), cert_info->web_trust_anchor(),
-      cert_info->hardware_backed());
+      cert_info->hardware_backed(), cert_info->device_wide());
 }
 
 CertificateManagerModel::Params::Params() = default;
diff --git a/chrome/browser/certificate_manager_model.h b/chrome/browser/certificate_manager_model.h
index cb296a2..bf59d11 100644
--- a/chrome/browser/certificate_manager_model.h
+++ b/chrome/browser/certificate_manager_model.h
@@ -52,7 +52,8 @@
              bool untrusted,
              Source source,
              bool web_trust_anchor,
-             bool hardware_backed);
+             bool hardware_backed,
+             bool device_wide);
     ~CertInfo();
 
     CERTCertificate* cert() const { return cert_.get(); }
@@ -63,6 +64,7 @@
     Source source() const { return source_; }
     bool web_trust_anchor() const { return web_trust_anchor_; }
     bool hardware_backed() const { return hardware_backed_; }
+    bool device_wide() const { return device_wide_; }
 
     // Clones a CertInfo, duplicating the contained NSS certificate.
     static std::unique_ptr<CertInfo> Clone(const CertInfo* cert_info);
@@ -96,6 +98,10 @@
     // certificates are not regarded as hardware-backed.
     bool hardware_backed_;
 
+    // true if the certificate is device-wide.
+    // Note: can be true only on Chrome OS.
+    bool device_wide_;
+
     DISALLOW_COPY_AND_ASSIGN(CertInfo);
   };
 
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index e8f1beb..05a9efa 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -240,6 +240,8 @@
 #include "components/safe_browsing/features.h"
 #include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
 #include "components/security_interstitials/content/origin_policy_ui.h"
+#include "components/services/heap_profiling/heap_profiling_service.h"
+#include "components/services/heap_profiling/public/cpp/settings.h"
 #include "components/services/heap_profiling/public/mojom/constants.mojom.h"
 #include "components/services/unzip/public/interfaces/constants.mojom.h"
 #include "components/signin/core/browser/account_consistency_method.h"
@@ -3761,6 +3763,12 @@
   connection->AddServiceRequestHandler(
       "download_manager", base::BindRepeating(&StartDownloadManager));
 #endif
+
+  if (heap_profiling::IsInProcessModeEnabled()) {
+    connection->AddServiceRequestHandler(
+        heap_profiling::mojom::kServiceName,
+        heap_profiling::HeapProfilingService::GetServiceFactory());
+  }
 }
 
 void ChromeContentBrowserClient::RegisterOutOfProcessServices(
@@ -3788,8 +3796,10 @@
                           IDS_UTILITY_PROCESS_PRINTING_SERVICE_NAME);
 #endif
 
-  (*services)[heap_profiling::mojom::kServiceName] = base::BindRepeating(
-      &l10n_util::GetStringUTF16, IDS_UTILITY_PROCESS_PROFILING_SERVICE_NAME);
+  if (!heap_profiling::IsInProcessModeEnabled()) {
+    (*services)[heap_profiling::mojom::kServiceName] = base::BindRepeating(
+        &l10n_util::GetStringUTF16, IDS_UTILITY_PROCESS_PROFILING_SERVICE_NAME);
+  }
 
 #if BUILDFLAG(ENABLE_EXTENSIONS) || defined(OS_ANDROID)
   (*services)[chrome::mojom::kMediaGalleryUtilServiceName] =
@@ -5134,10 +5144,9 @@
 base::Optional<std::string>
 ChromeContentBrowserClient::GetOriginPolicyErrorPage(
     content::OriginPolicyErrorReason error_reason,
-    const url::Origin& origin,
-    const GURL& url) {
-  return security_interstitials::OriginPolicyUI::GetErrorPage(error_reason,
-                                                              origin, url);
+    content::NavigationHandle* handle) {
+  return security_interstitials::OriginPolicyUI::GetErrorPageAsHTML(
+      error_reason, handle);
 }
 
 bool ChromeContentBrowserClient::CanIgnoreCertificateErrorIfNeeded() {
diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h
index 1c60a8f..84946f7c 100644
--- a/chrome/browser/chrome_content_browser_client.h
+++ b/chrome/browser/chrome_content_browser_client.h
@@ -527,8 +527,7 @@
       blink::mojom::RendererPreferenceWatcherPtr watcher) override;
   base::Optional<std::string> GetOriginPolicyErrorPage(
       content::OriginPolicyErrorReason error_reason,
-      const url::Origin& origin,
-      const GURL& url) override;
+      content::NavigationHandle* handle) override;
   bool CanIgnoreCertificateErrorIfNeeded() override;
   void OnNetworkServiceDataUseUpdate(int32_t network_traffic_annotation_id_hash,
                                      int64_t recv_bytes,
diff --git a/chrome/browser/chrome_content_browser_client_browsertest_chromeos.cc b/chrome/browser/chrome_content_browser_client_browsertest_chromeos.cc
index aea2590..80aa8eb 100644
--- a/chrome/browser/chrome_content_browser_client_browsertest_chromeos.cc
+++ b/chrome/browser/chrome_content_browser_client_browsertest_chromeos.cc
@@ -37,8 +37,9 @@
 std::string ReadCmdLine(pid_t pid) {
   // Files in "/proc" are in-memory, so it's safe to do IO.
   base::ScopedAllowBlockingForTesting allow_io;
-  base::FilePath cmdline_file =
-      base::FilePath("/proc").Append(base::IntToString(pid)).Append("cmdline");
+  base::FilePath cmdline_file = base::FilePath("/proc")
+                                    .Append(base::NumberToString(pid))
+                                    .Append("cmdline");
   std::string cmdline;
   base::ReadFileToString(cmdline_file, &cmdline);
   return cmdline;
diff --git a/chrome/browser/chrome_navigation_browsertest.cc b/chrome/browser/chrome_navigation_browsertest.cc
index 44c8213..b2a651a 100644
--- a/chrome/browser/chrome_navigation_browsertest.cc
+++ b/chrome/browser/chrome_navigation_browsertest.cc
@@ -435,7 +435,7 @@
     // the |embedded_test_server| uses. It is required to test with potentially
     // malformed URLs.
     std::string port =
-        base::IntToString(embedded_test_server()->host_port_pair().port());
+        base::NumberToString(embedded_test_server()->host_port_pair().port());
     command_line->AppendSwitchASCII(
         "host-resolver-rules",
         "MAP * 127.0.0.1:" + port + ", EXCLUDE 127.0.0.1*");
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 83c7c1d..167a3868 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -233,7 +233,6 @@
     "//ui/aura",
     "//ui/base",
     "//ui/base/idle",
-    "//ui/base/user_activity",
     "//ui/chromeos",
     "//ui/chromeos/events",
     "//ui/compositor",
diff --git a/chrome/browser/chromeos/accessibility/accessibility_panel.cc b/chrome/browser/chromeos/accessibility/accessibility_panel.cc
index 24125a7..37502bc 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_panel.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_panel.cc
@@ -71,9 +71,8 @@
   params.delegate = this;
   params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
   params.name = widget_name;
+  params.shadow_elevation = wm::kShadowElevationInactiveWindow;
   widget_->Init(params);
-  wm::SetShadowElevation(widget_->GetNativeWindow(),
-                         wm::kShadowElevationInactiveWindow);
 
   // WebContentsObserver::DidFirstVisuallyNonEmptyPaint is not called under
   // mash. Work around this by showing the window immediately.
diff --git a/chrome/browser/chromeos/app_mode/fake_cws.cc b/chrome/browser/chromeos/app_mode/fake_cws.cc
index 6b20913..3fd5d017 100644
--- a/chrome/browser/chromeos/app_mode/fake_cws.cc
+++ b/chrome/browser/chromeos/app_mode/fake_cws.cc
@@ -131,7 +131,7 @@
   base::ReplaceSubstringsAfterOffset(&update_check_content, 0, "$FP",
                                      sha256_hex);
   base::ReplaceSubstringsAfterOffset(&update_check_content, 0, "$Size",
-                                     base::UintToString(crx_content.size()));
+                                     base::NumberToString(crx_content.size()));
   base::ReplaceSubstringsAfterOffset(&update_check_content, 0, "$Version",
                                      version);
   id_to_update_check_content_map_[app_id] = update_check_content;
diff --git a/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.cc b/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.cc
index 829bb2f1..2afee80 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_diagnosis_runner.cc
@@ -85,7 +85,7 @@
 void KioskDiagnosisRunner::StartSystemLogCollection() {
   const std::string description = base::StringPrintf(
       "Autogenerated feedback:\nAppId: %s\n(uniquifier:%s)", app_id_.c_str(),
-      base::Int64ToString(base::Time::Now().ToInternalValue()).c_str());
+      base::NumberToString(base::Time::Now().ToInternalValue()).c_str());
   feedback_util::SendSysLogFeedback(
       profile_, description, base::Bind(&KioskDiagnosisRunner::OnFeedbackSent,
                                         weak_factory_.GetWeakPtr()));
diff --git a/chrome/browser/chromeos/arc/arc_support_host.cc b/chrome/browser/chromeos/arc/arc_support_host.cc
index 77192490..caef03e5 100644
--- a/chrome/browser/chromeos/arc/arc_support_host.cc
+++ b/chrome/browser/chromeos/arc/arc_support_host.cc
@@ -575,6 +575,7 @@
 
   const std::string& app_locale = g_browser_process->GetApplicationLocale();
   webui::SetLoadTimeDataDefaults(app_locale, loadtime_data.get());
+  loadtime_data->SetString("locale", app_locale);
 
   base::DictionaryValue message;
   message.SetString(kAction, kActionInitialize);
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc
index 889ea5a..a4e424d0 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_async_file_util_unittest.cc
@@ -79,7 +79,7 @@
     EXPECT_TRUE(
         mount_point_virtual_path.AppendRelativePath(virtual_path, &path));
     return storage::FileSystemURL::CreateForTest(
-        GURL(),  // origin
+        url::Origin(),  // origin
         storage::kFileSystemTypeArcContent, path);
   }
 
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util_unittest.cc
index e58c0f5..31f99e8 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_content_file_system_url_util_unittest.cc
@@ -71,7 +71,7 @@
       base::FilePath(kContentFileSystemMountPointPath)
           .Append(base::FilePath::FromUTF8Unsafe(EscapeArcUrl(arc_url)));
   storage::FileSystemURL file_system_url =
-      storage::FileSystemURL::CreateForTest(GURL(),  // origin
+      storage::FileSystemURL::CreateForTest(url::Origin(),  // origin
                                             storage::kFileSystemTypeArcContent,
                                             path);
 
diff --git a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util_unittest.cc b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util_unittest.cc
index 33dd1752..47861d8 100644
--- a/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util_unittest.cc
+++ b/chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util_unittest.cc
@@ -66,7 +66,7 @@
 
   EXPECT_TRUE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath(kDocumentsProviderMountPointPath)
               .Append(FILE_PATH_LITERAL("cats/root/home/calico.jpg"))),
       &authority, &root_document_id, &path));
@@ -85,7 +85,7 @@
   // Should accept a path pointing to a root directory.
   EXPECT_TRUE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath(kDocumentsProviderMountPointPath)
               .Append(FILE_PATH_LITERAL("cats/root"))),
       &authority, &root_document_id, &path));
@@ -104,7 +104,7 @@
   // Should accept a path pointing to a root directory.
   EXPECT_TRUE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath(kDocumentsProviderMountPointPath)
               .Append(FILE_PATH_LITERAL("cats/root/"))),
       &authority, &root_document_id, &path));
@@ -121,7 +121,7 @@
   // Not storage::kFileSystemTypeArcDocumentsProvider.
   EXPECT_FALSE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcContent,
+          url::Origin(), storage::kFileSystemTypeArcContent,
           base::FilePath(kDocumentsProviderMountPointPath)
               .Append(FILE_PATH_LITERAL("cats/root/home/calico.jpg"))),
       &authority, &root_document_id, &path));
@@ -135,7 +135,7 @@
   // root_document_id part is missing.
   EXPECT_FALSE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath(kDocumentsProviderMountPointPath)
               .Append(FILE_PATH_LITERAL("root-missing"))),
       &authority, &root_document_id, &path));
@@ -143,7 +143,7 @@
   // Leading / is missing.
   EXPECT_FALSE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath(FILE_PATH_LITERAL(
               "special/arc-documents-provider/cats/root/home/calico.jpg"))),
       &authority, &root_document_id, &path));
@@ -151,7 +151,7 @@
   // Not under /special.
   EXPECT_FALSE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath(FILE_PATH_LITERAL(
               "/invalid/arc-documents-provider/cats/root/home/calico.jpg"))),
       &authority, &root_document_id, &path));
@@ -159,7 +159,7 @@
   // Not under /special/arc-documents-provider.
   EXPECT_FALSE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath(FILE_PATH_LITERAL(
               "/special/something-else/cats/root/home/calico.jpg"))),
       &authority, &root_document_id, &path));
@@ -172,7 +172,7 @@
 
   EXPECT_TRUE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath(
               "/special/arc-documents-provider/cats/ro%2Fot/home/calico.jpg")),
       &authority, &root_document_id, &path));
@@ -188,7 +188,7 @@
 
   EXPECT_TRUE(ParseDocumentsProviderUrl(
       storage::FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath(
               "/special/arc-documents-provider/cats/root/home/みけねこ.jpg")),
       &authority, &root_document_id, &path));
diff --git a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator_unittest.cc b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator_unittest.cc
index b20a5ba..2957982 100644
--- a/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator_unittest.cc
+++ b/chrome/browser/chromeos/arc/optin/arc_terms_of_service_default_negotiator_unittest.cc
@@ -19,7 +19,6 @@
 #include "chrome/browser/consent_auditor/consent_auditor_factory.h"
 #include "chrome/browser/consent_auditor/consent_auditor_test_utils.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/browser_with_test_window_test.h"
 #include "chrome/test/base/testing_browser_process.h"
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
index 71ead54..834b6b0 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.cc
@@ -120,9 +120,7 @@
   DCHECK(profile_);
   PrefService* prefs = profile_->GetPrefs();
   bool completed =
-      chromeos::switches::IsAssistantEnabled()
-          ? prefs->GetBoolean(prefs::kVoiceInteractionActivityControlAccepted)
-          : prefs->GetBoolean(prefs::kArcVoiceInteractionValuePropAccepted);
+      prefs->GetBoolean(prefs::kVoiceInteractionActivityControlAccepted);
   voice_interaction_controller_->NotifySetupCompleted(completed);
 }
 
@@ -165,11 +163,6 @@
     SetProfile(ProfileManager::GetActiveUserProfile());
 }
 
-void VoiceInteractionControllerClient::OnArcPlayStoreEnabledChanged(
-    bool enabled) {
-  NotifyFeatureAllowed();
-}
-
 void VoiceInteractionControllerClient::SetProfile(Profile* profile) {
   // Do nothing if this is called for the current profile. This can happen. For
   // example, ChromeSessionManager fires both
@@ -187,21 +180,13 @@
   PrefService* prefs = profile->GetPrefs();
   pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
   pref_change_registrar_->Init(prefs);
-  if (chromeos::switches::IsAssistantEnabled()) {
-    pref_change_registrar_->Add(
-        prefs::kVoiceInteractionActivityControlAccepted,
-        base::BindRepeating(
-            &VoiceInteractionControllerClient::NotifySetupCompleted,
-            base::Unretained(this)));
-  } else {
-    pref_change_registrar_->Add(
-        prefs::kArcVoiceInteractionValuePropAccepted,
-        base::BindRepeating(
-            &VoiceInteractionControllerClient::NotifySetupCompleted,
-            base::Unretained(this)));
-  }
 
   pref_change_registrar_->Add(
+      prefs::kVoiceInteractionActivityControlAccepted,
+      base::BindRepeating(
+          &VoiceInteractionControllerClient::NotifySetupCompleted,
+          base::Unretained(this)));
+  pref_change_registrar_->Add(
       language::prefs::kApplicationLocale,
       base::BindRepeating(
           &VoiceInteractionControllerClient::NotifyLocaleChanged,
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
index 9fb71731..008ce13 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client.h
@@ -20,8 +20,7 @@
 // states and notifies Ash side.  It can also be used to notify some specific
 // state changes that does not have an observer interface.
 class VoiceInteractionControllerClient
-    : public ArcSessionManager::Observer,
-      public content::NotificationObserver,
+    : public content::NotificationObserver,
       public user_manager::UserManager::UserSessionStateObserver {
  public:
   class Observer {
@@ -66,9 +65,6 @@
   // user_manager::UserManager::UserSessionStateObserver overrides:
   void ActiveUserChanged(const user_manager::User* active_user) override;
 
-  // ArcSessionManager::Observer overrides:
-  void OnArcPlayStoreEnabledChanged(bool enabled) override;
-
   // content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
diff --git a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
index 148e5b6..5d428d1 100644
--- a/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
+++ b/chrome/browser/chromeos/arc/voice_interaction/voice_interaction_controller_client_unittest.cc
@@ -139,10 +139,10 @@
       voice_interaction_controller()->voice_interaction_notification_enabled());
 
   ASSERT_EQ(false,
-            prefs->GetBoolean(prefs::kArcVoiceInteractionValuePropAccepted));
-  prefs->SetBoolean(prefs::kArcVoiceInteractionValuePropAccepted, true);
+            prefs->GetBoolean(prefs::kVoiceInteractionActivityControlAccepted));
+  prefs->SetBoolean(prefs::kVoiceInteractionActivityControlAccepted, true);
   ASSERT_EQ(true,
-            prefs->GetBoolean(prefs::kArcVoiceInteractionValuePropAccepted));
+            prefs->GetBoolean(prefs::kVoiceInteractionActivityControlAccepted));
   voice_interaction_controller_client()->FlushMojoForTesting();
   EXPECT_EQ(
       true,
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
index 8c8e913..4eae616a 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
@@ -134,6 +134,56 @@
   EXPECT_FALSE(IsAuthEnabled());
 }
 
+// Tests an unlock override on a bedtime.
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, UnlockBedtime) {
+  SetupTaskRunnerWithTime(utils::TimeFromString("5 Jan 2018 22:00:00 BRT"));
+  SkipToLoginScreen();
+  LogIn(kAccountId, kAccountPassword, test::kChildAccountServiceFlags);
+  MockClockForActiveUser();
+  ScreenLockerTester().Lock();
+
+  system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
+      base::UTF8ToUTF16("BRT"));
+
+  // Set new policy.
+  base::Time last_updated = utils::TimeFromString("1 Jan 2018 0:00 BRT");
+  std::unique_ptr<base::DictionaryValue> policy_content =
+      utils::CreateTimeLimitPolicy(utils::CreateTime(6, 0));
+  utils::AddTimeWindowLimit(policy_content.get(), utils::kFriday,
+                            utils::CreateTime(21, 0), utils::CreateTime(7, 0),
+                            last_updated);
+  utils::AddTimeWindowLimit(policy_content.get(), utils::kSaturday,
+                            utils::CreateTime(21, 0), utils::CreateTime(7, 0),
+                            last_updated);
+  auto policy_one = std::make_unique<base::DictionaryValue>();
+  policy_one->SetKey("UsageTimeLimit",
+                     base::Value(utils::PolicyToString(policy_content.get())));
+  user_policy_helper()->UpdatePolicy(*policy_one, base::DictionaryValue(),
+                                     child_profile_);
+
+  // Check that auth is disabled, since the bedtime has already started.
+  EXPECT_FALSE(IsAuthEnabled());
+
+  // Create unlock override and update the policy.
+  utils::AddOverride(policy_content.get(), utils::kUnlock, task_runner_->Now());
+  auto policy_two = std::make_unique<base::DictionaryValue>();
+  policy_two->SetKey("UsageTimeLimit",
+                     base::Value(utils::PolicyToString(policy_content.get())));
+  user_policy_helper()->UpdatePolicy(*policy_two, base::DictionaryValue(),
+                                     child_profile_);
+
+  // Check that the unlock worked and auth is enabled.
+  EXPECT_TRUE(IsAuthEnabled());
+
+  // Forward to 6 AM and check that auth is still enabled.
+  task_runner_->FastForwardBy(base::TimeDelta::FromHours(8));
+  EXPECT_TRUE(IsAuthEnabled());
+
+  // Forward to 9 PM and check that auth is disabled because bedtime started.
+  task_runner_->FastForwardBy(base::TimeDelta::FromHours(15));
+  EXPECT_FALSE(IsAuthEnabled());
+}
+
 // Tests the default time window limit.
 IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, DefaultBedtime) {
   SetupTaskRunnerWithTime(utils::TimeFromString("1 Jan 2018 10:00:00 GMT"));
@@ -349,4 +399,107 @@
   EXPECT_TRUE(IsAuthEnabled());
 }
 
+// Tests bedtime during timezone changes.
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest, BedtimeOnTimezoneChange) {
+  SetupTaskRunnerWithTime(
+      utils::TimeFromString("3 Jan 2018 10:00:00 GMT-0600"));
+  SkipToLoginScreen();
+  LogIn(kAccountId, kAccountPassword, test::kChildAccountServiceFlags);
+  MockClockForActiveUser();
+  ScreenLockerTester().Lock();
+
+  system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
+      base::UTF8ToUTF16("GMT-0600"));
+
+  // Set new policy.
+  base::Time last_updated = utils::TimeFromString("3 Jan 2018 0:00 GMT-0600");
+  std::unique_ptr<base::DictionaryValue> policy_content =
+      utils::CreateTimeLimitPolicy(utils::CreateTime(6, 0));
+  utils::AddTimeWindowLimit(policy_content.get(), utils::kWednesday,
+                            utils::CreateTime(19, 0), utils::CreateTime(7, 0),
+                            last_updated);
+
+  auto policy = std::make_unique<base::DictionaryValue>();
+  policy->SetKey("UsageTimeLimit",
+                 base::Value(utils::PolicyToString(policy_content.get())));
+
+  user_policy_helper()->UpdatePolicy(*policy, base::DictionaryValue(),
+                                     child_profile_);
+
+  // Verify that auth is enabled at 10 AM.
+  EXPECT_TRUE(IsAuthEnabled());
+
+  // Verify that auth is enabled at 6 PM.
+  task_runner_->FastForwardBy(base::TimeDelta::FromHours(8));
+  EXPECT_TRUE(IsAuthEnabled());
+
+  // Verify that the auth is disabled at 7 PM (start of bedtime).
+  task_runner_->FastForwardBy(base::TimeDelta::FromHours(1));
+  EXPECT_FALSE(IsAuthEnabled());
+
+  // Change timezone, so that local time goes back to 6 PM and check that auth
+  // is enabled since bedtime has not started yet.
+  system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
+      base::UTF8ToUTF16("GMT-0700"));
+  EXPECT_TRUE(IsAuthEnabled());
+
+  // Verify that auth is disabled at 7 PM (start of bedtime).
+  task_runner_->FastForwardBy(base::TimeDelta::FromHours(1));
+  EXPECT_FALSE(IsAuthEnabled());
+
+  // Change timezone, so that local time goes forward to 7 AM and check that
+  // auth is enabled since bedtime has ended in the new local time.
+  system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
+      base::UTF8ToUTF16("GMT+0500"));
+  EXPECT_TRUE(IsAuthEnabled());
+}
+
+// Tests bedtime during timezone changes that make the clock go back in time.
+IN_PROC_BROWSER_TEST_F(ScreenTimeControllerTest,
+                       BedtimeOnEastToWestTimezoneChanges) {
+  SetupTaskRunnerWithTime(utils::TimeFromString("3 Jan 2018 8:00:00 GMT+1300"));
+  SkipToLoginScreen();
+  LogIn(kAccountId, kAccountPassword, test::kChildAccountServiceFlags);
+  MockClockForActiveUser();
+  ScreenLockerTester().Lock();
+
+  system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
+      base::UTF8ToUTF16("GMT+1300"));
+
+  // Set new policy.
+  base::Time last_updated = utils::TimeFromString("3 Jan 2018 0:00 GMT+1300");
+  std::unique_ptr<base::DictionaryValue> policy_content =
+      utils::CreateTimeLimitPolicy(utils::CreateTime(6, 0));
+  utils::AddTimeWindowLimit(policy_content.get(), utils::kTuesday,
+                            utils::CreateTime(20, 0), utils::CreateTime(7, 0),
+                            last_updated);
+
+  auto policy = std::make_unique<base::DictionaryValue>();
+  policy->SetKey("UsageTimeLimit",
+                 base::Value(utils::PolicyToString(policy_content.get())));
+
+  user_policy_helper()->UpdatePolicy(*policy, base::DictionaryValue(),
+                                     child_profile_);
+
+  // Verify that auth is disabled at 8 AM.
+  EXPECT_TRUE(IsAuthEnabled());
+
+  // Change timezone so that local time goes back to 6 AM and check that auth is
+  // disable, since the tuesday's bedtime is not over yet.
+  system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
+      base::UTF8ToUTF16("GMT+1100"));
+  EXPECT_FALSE(IsAuthEnabled());
+
+  // Change timezone so that local time goes back to 7 PM on Tuesday and check
+  // that auth is enabled, because the bedtime has not started yet in the
+  // new local time.
+  system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
+      base::UTF8ToUTF16("GMT"));
+  EXPECT_TRUE(IsAuthEnabled());
+
+  // Verify that auth is disabled at 8 PM (start of bedtime).
+  task_runner_->FastForwardBy(base::TimeDelta::FromHours(1));
+  EXPECT_FALSE(IsAuthEnabled());
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.cc b/chrome/browser/chromeos/crostini/crostini_manager.cc
index 814ac111..f30016b 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager.cc
@@ -474,6 +474,7 @@
 }
 
 LinuxPackageInfo::LinuxPackageInfo() = default;
+LinuxPackageInfo::LinuxPackageInfo(const LinuxPackageInfo&) = default;
 LinuxPackageInfo::~LinuxPackageInfo() = default;
 
 ContainerInfo::ContainerInfo(std::string container_name,
@@ -1036,6 +1037,23 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void CrostiniManager::GetLinuxPackageInfoFromApt(
+    const std::string& vm_name,
+    const std::string& container_name,
+    const std::string& package_name,
+    GetLinuxPackageInfoCallback callback) {
+  vm_tools::cicerone::LinuxPackageInfoFromAptRequest request;
+  request.set_owner_id(owner_id_);
+  request.set_vm_name(vm_name);
+  request.set_container_name(container_name);
+  request.set_package_name(package_name);
+
+  GetCiceroneClient()->GetLinuxPackageInfoFromApt(
+      std::move(request),
+      base::BindOnce(&CrostiniManager::OnGetLinuxPackageInfo,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
 void CrostiniManager::InstallLinuxPackage(
     std::string vm_name,
     std::string container_name,
@@ -1062,6 +1080,32 @@
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
+void CrostiniManager::InstallLinuxPackageFromApt(
+    const std::string& vm_name,
+    const std::string& container_name,
+    const std::string& package_id,
+    InstallLinuxPackageCallback callback) {
+  if (!GetCiceroneClient()->IsInstallLinuxPackageProgressSignalConnected()) {
+    // Technically we could still start the install, but we wouldn't be able to
+    // detect when the install completes, successfully or otherwise.
+    LOG(ERROR)
+        << "Attempted to install package when progress signal not connected.";
+    std::move(callback).Run(CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED);
+    return;
+  }
+
+  vm_tools::cicerone::InstallLinuxPackageFromAptRequest request;
+  request.set_owner_id(owner_id_);
+  request.set_vm_name(vm_name);
+  request.set_container_name(container_name);
+  request.set_package_id(package_id);
+
+  GetCiceroneClient()->InstallLinuxPackageFromApt(
+      std::move(request),
+      base::BindOnce(&CrostiniManager::OnInstallLinuxPackage,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+}
+
 void CrostiniManager::UninstallPackageOwningFile(
     std::string vm_name,
     std::string container_name,
@@ -2096,6 +2140,7 @@
   }
 
   result.success = true;
+  result.package_id = response.package_id();
   result.name = split[0];
   result.version = split[1];
   result.description = response.description();
diff --git a/chrome/browser/chromeos/crostini/crostini_manager.h b/chrome/browser/chromeos/crostini/crostini_manager.h
index 19b5574..db54d91 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager.h
+++ b/chrome/browser/chromeos/crostini/crostini_manager.h
@@ -109,6 +109,7 @@
 
 struct LinuxPackageInfo {
   LinuxPackageInfo();
+  LinuxPackageInfo(const LinuxPackageInfo&);
   ~LinuxPackageInfo();
 
   bool success;
@@ -117,6 +118,8 @@
   std::string failure_reason;
 
   // The remaining fields are only set when success is true.
+  // package_id is given as "name;version;arch;data".
+  std::string package_id;
   std::string name;
   std::string version;
   std::string summary;
@@ -364,6 +367,13 @@
                            std::string package_path,
                            GetLinuxPackageInfoCallback callback);
 
+  // Asynchronously retrieve information about a Linux Package in the APT
+  // repository. This uses a package_name to identify a package.
+  void GetLinuxPackageInfoFromApt(const std::string& vm_name,
+                                  const std::string& container_name,
+                                  const std::string& package_name,
+                                  GetLinuxPackageInfoCallback callback);
+
   // Begin installation of a Linux Package inside the container. If the
   // installation is successfully started, further updates will be sent to
   // added LinuxPackageOperationProgressObservers.
@@ -372,6 +382,16 @@
                            std::string package_path,
                            InstallLinuxPackageCallback callback);
 
+  // Begin installation of a Linux Package inside the container. If the
+  // installation is successfully started, further updates will be sent to
+  // added LinuxPackageOperationProgressObservers. Uses a package_id, given
+  // by "package_name;version;arch;data", to identify the package to install
+  // from the APT repository.
+  void InstallLinuxPackageFromApt(const std::string& vm_name,
+                                  const std::string& container_name,
+                                  const std::string& package_id,
+                                  InstallLinuxPackageCallback callback);
+
   // Begin uninstallation of a Linux Package inside the container. The package
   // is identified by its associated .desktop file's ID; we don't use package_id
   // to avoid problems with stale package_ids (such as after upgrades). If the
@@ -609,7 +629,8 @@
       GetContainerAppIconsCallback callback,
       base::Optional<vm_tools::cicerone::ContainerAppIconResponse> reply);
 
-  // Callback for CrostiniManager::GetLinuxPackageInfo.
+  // Callback for CrostiniManager::GetLinuxPackageInfo and
+  // CrostiniManager::GetLinuxPackageInfoFromApt.
   void OnGetLinuxPackageInfo(
       GetLinuxPackageInfoCallback callback,
       base::Optional<vm_tools::cicerone::LinuxPackageInfoResponse> reply);
diff --git a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
index a34d282..2546e2b 100644
--- a/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crostini_manager_unittest.cc
@@ -18,6 +18,7 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "device/usb/public/cpp/fake_usb_device_manager.h"
 #include "storage/browser/fileapi/external_mount_points.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace crostini {
@@ -25,6 +26,7 @@
 namespace {
 const char kVmName[] = "vm_name";
 const char kContainerName[] = "container_name";
+const char kPackageID[] = "package;1;;";
 constexpr int64_t kDiskSizeBytes = 4ll * 1024 * 1024 * 1024;  // 4 GiB
 }  // namespace
 
@@ -162,6 +164,27 @@
     std::move(closure).Run();
   }
 
+  void SearchAppCallback(base::OnceClosure closure,
+                         const std::vector<std::string>& expected_result,
+                         const std::vector<std::string>& result) {
+    EXPECT_THAT(result, testing::ContainerEq(expected_result));
+    std::move(closure).Run();
+  }
+
+  void GetLinuxPackageInfoFromAptCallback(
+      base::OnceClosure closure,
+      const LinuxPackageInfo& expected_result,
+      const LinuxPackageInfo& result) {
+    EXPECT_EQ(result.success, expected_result.success);
+    EXPECT_EQ(result.failure_reason, expected_result.failure_reason);
+    EXPECT_EQ(result.package_id, expected_result.package_id);
+    EXPECT_EQ(result.name, expected_result.name);
+    EXPECT_EQ(result.version, expected_result.version);
+    EXPECT_EQ(result.summary, expected_result.summary);
+    EXPECT_EQ(result.description, expected_result.description);
+    std::move(closure).Run();
+  }
+
   CrostiniManagerTest()
       : test_browser_thread_bundle_(
             content::TestBrowserThreadBundle::REAL_IO_THREAD) {
@@ -995,4 +1018,140 @@
   EXPECT_FALSE(crostini_manager()->GetContainerInfo(kVmName, kContainerName));
 }
 
+TEST_F(CrostiniManagerTest, InstallLinuxPackageFromAptSignalNotConnectedError) {
+  fake_cicerone_client_->set_install_linux_package_progress_signal_connected(
+      false);
+  crostini_manager()->InstallLinuxPackageFromApt(
+      kVmName, kContainerName, kPackageID,
+      base::BindOnce(&CrostiniManagerTest::InstallLinuxPackageCallback,
+                     base::Unretained(this), run_loop()->QuitClosure(),
+                     CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED));
+  run_loop()->Run();
+}
+
+TEST_F(CrostiniManagerTest, InstallLinuxPackageFromAptSignalSuccess) {
+  vm_tools::cicerone::InstallLinuxPackageResponse response;
+  response.set_status(vm_tools::cicerone::InstallLinuxPackageResponse::STARTED);
+  fake_cicerone_client_->set_install_linux_package_response(response);
+  crostini_manager()->InstallLinuxPackageFromApt(
+      kVmName, kContainerName, kPackageID,
+      base::BindOnce(&CrostiniManagerTest::InstallLinuxPackageCallback,
+                     base::Unretained(this), run_loop()->QuitClosure(),
+                     CrostiniResult::SUCCESS));
+  run_loop()->Run();
+}
+
+TEST_F(CrostiniManagerTest, InstallLinuxPackageFromAptSignalFailure) {
+  vm_tools::cicerone::InstallLinuxPackageResponse response;
+  response.set_status(vm_tools::cicerone::InstallLinuxPackageResponse::FAILED);
+  response.set_failure_reason(
+      "Unit tests can't install Linux package from apt!");
+  fake_cicerone_client_->set_install_linux_package_response(response);
+  crostini_manager()->InstallLinuxPackageFromApt(
+      kVmName, kContainerName, kPackageID,
+      base::BindOnce(&CrostiniManagerTest::InstallLinuxPackageCallback,
+                     base::Unretained(this), run_loop()->QuitClosure(),
+                     CrostiniResult::INSTALL_LINUX_PACKAGE_FAILED));
+  run_loop()->Run();
+}
+
+TEST_F(CrostiniManagerTest, InstallLinuxPackageFromAptSignalOperationBlocked) {
+  vm_tools::cicerone::InstallLinuxPackageResponse response;
+  response.set_status(
+      vm_tools::cicerone::InstallLinuxPackageResponse::INSTALL_ALREADY_ACTIVE);
+  fake_cicerone_client_->set_install_linux_package_response(response);
+  crostini_manager()->InstallLinuxPackageFromApt(
+      kVmName, kContainerName, kPackageID,
+      base::BindOnce(&CrostiniManagerTest::InstallLinuxPackageCallback,
+                     base::Unretained(this), run_loop()->QuitClosure(),
+                     CrostiniResult::BLOCKING_OPERATION_ALREADY_ACTIVE));
+  run_loop()->Run();
+}
+
+TEST_F(CrostiniManagerTest, SearchAppSuccess) {
+  vm_tools::cicerone::AppSearchResponse response;
+  vm_tools::cicerone::AppSearchResponse::AppSearchResult* app =
+      response.add_packages();
+  app->set_package_name("fake app");
+  app = response.add_packages();
+  app->set_package_name("fake app1");
+  app = response.add_packages();
+  app->set_package_name("fake app2");
+  fake_cicerone_client_->set_search_app_response(response);
+  std::vector<std::string> expected = {"fake app", "fake app1", "fake app2"};
+  crostini_manager()->SearchApp(
+      kVmName, kContainerName, "fake ap",
+      base::BindOnce(&CrostiniManagerTest::SearchAppCallback,
+                     base::Unretained(this), run_loop()->QuitClosure(),
+                     expected));
+  run_loop()->Run();
+}
+
+TEST_F(CrostiniManagerTest, SearchAppNoResults) {
+  vm_tools::cicerone::AppSearchResponse response;
+  fake_cicerone_client_->set_search_app_response(response);
+  std::vector<std::string> expected = {};
+  crostini_manager()->SearchApp(
+      kVmName, kContainerName, "fake ap",
+      base::BindOnce(&CrostiniManagerTest::SearchAppCallback,
+                     base::Unretained(this), run_loop()->QuitClosure(),
+                     expected));
+  run_loop()->Run();
+}
+
+TEST_F(CrostiniManagerTest, GetLinuxPackageInfoFromAptFailedToGetInfo) {
+  const char kFailMessage[] = "Failed to get package info.";
+  vm_tools::cicerone::LinuxPackageInfoResponse response;
+  response.set_success(false);
+  response.set_failure_reason(kFailMessage);
+  fake_cicerone_client_->set_linux_package_info_response(response);
+  LinuxPackageInfo expected;
+  expected.success = false;
+  expected.failure_reason = kFailMessage;
+  crostini_manager()->GetLinuxPackageInfoFromApt(
+      kVmName, kContainerName, "fake app",
+      base::BindOnce(&CrostiniManagerTest::GetLinuxPackageInfoFromAptCallback,
+                     base::Unretained(this), run_loop()->QuitClosure(),
+                     expected));
+  run_loop()->Run();
+}
+
+TEST_F(CrostiniManagerTest, GetLinuxPackageInfoFromAptInvalidID) {
+  vm_tools::cicerone::LinuxPackageInfoResponse response;
+  response.set_success(true);
+  response.set_package_id("Bad;;id;");
+  fake_cicerone_client_->set_linux_package_info_response(response);
+  LinuxPackageInfo expected;
+  expected.success = false;
+  expected.failure_reason = "Linux package info contained invalid package id.";
+  crostini_manager()->GetLinuxPackageInfoFromApt(
+      kVmName, kContainerName, "fake app",
+      base::BindOnce(&CrostiniManagerTest::GetLinuxPackageInfoFromAptCallback,
+                     base::Unretained(this), run_loop()->QuitClosure(),
+                     expected));
+  run_loop()->Run();
+}
+
+TEST_F(CrostiniManagerTest, GetLinuxPackageInfoFromAptSuccess) {
+  vm_tools::cicerone::LinuxPackageInfoResponse response;
+  response.set_success(true);
+  response.set_package_id("good;1.1;id;");
+  response.set_summary("A summary");
+  response.set_description("A description");
+  fake_cicerone_client_->set_linux_package_info_response(response);
+  LinuxPackageInfo expected;
+  expected.success = true;
+  expected.package_id = "good;1.1;id;";
+  expected.name = "good";
+  expected.version = "1.1";
+  expected.summary = "A summary";
+  expected.description = "A description";
+  crostini_manager()->GetLinuxPackageInfoFromApt(
+      kVmName, kContainerName, "fake app",
+      base::BindOnce(&CrostiniManagerTest::GetLinuxPackageInfoFromAptCallback,
+                     base::Unretained(this), run_loop()->QuitClosure(),
+                     expected));
+  run_loop()->Run();
+}
+
 }  // namespace crostini
diff --git a/chrome/browser/chromeos/crostini/crostini_package_service.cc b/chrome/browser/chromeos/crostini/crostini_package_service.cc
index c9ca0c63..153f7cb 100644
--- a/chrome/browser/chromeos/crostini/crostini_package_service.cc
+++ b/chrome/browser/chromeos/crostini/crostini_package_service.cc
@@ -154,6 +154,18 @@
                      std::move(callback)));
 }
 
+void CrostiniPackageService::InstallLinuxPackageFromApt(
+    const std::string& vm_name,
+    const std::string& container_name,
+    const std::string& package_id,
+    CrostiniManager::InstallLinuxPackageCallback callback) {
+  CrostiniManager::GetForProfile(profile_)->InstallLinuxPackageFromApt(
+      vm_name, container_name, package_id,
+      base::BindOnce(&CrostiniPackageService::OnInstallLinuxPackage,
+                     weak_ptr_factory_.GetWeakPtr(), vm_name, container_name,
+                     std::move(callback)));
+}
+
 void CrostiniPackageService::OnInstallLinuxPackageProgress(
     const std::string& vm_name,
     const std::string& container_name,
diff --git a/chrome/browser/chromeos/crostini/crostini_package_service.h b/chrome/browser/chromeos/crostini/crostini_package_service.h
index 749e5ad..ef64f2e 100644
--- a/chrome/browser/chromeos/crostini/crostini_package_service.h
+++ b/chrome/browser/chromeos/crostini/crostini_package_service.h
@@ -51,6 +51,14 @@
       const std::string& package_path,
       CrostiniManager::InstallLinuxPackageCallback callback);
 
+  // Install a Linux package via a package_id. If successfully started, a
+  // system notification will be used to display further updates.
+  void InstallLinuxPackageFromApt(
+      const std::string& vm_name,
+      const std::string& container_name,
+      const std::string& package_id,
+      CrostiniManager::InstallLinuxPackageCallback callback);
+
   // LinuxPackageOperationProgressObserver:
   void OnInstallLinuxPackageProgress(const std::string& vm_name,
                                      const std::string& container_name,
diff --git a/chrome/browser/chromeos/crostini/crostini_registry_service.cc b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
index 32e1b75..0bd0736 100644
--- a/chrome/browser/chromeos/crostini/crostini_registry_service.cc
+++ b/chrome/browser/chromeos/crostini/crostini_registry_service.cc
@@ -851,7 +851,7 @@
                                              const char* key) const {
   DCHECK(dictionary);
   int64_t time = clock_->Now().ToDeltaSinceWindowsEpoch().InMicroseconds();
-  dictionary->SetKey(key, base::Value(base::Int64ToString(time)));
+  dictionary->SetKey(key, base::Value(base::NumberToString(time)));
 }
 
 void CrostiniRegistryService::SetAppScaled(const std::string& app_id,
diff --git a/chrome/browser/chromeos/crostini/crostini_share_path.cc b/chrome/browser/chromeos/crostini/crostini_share_path.cc
index 320a0c9..068b4d0 100644
--- a/chrome/browser/chromeos/crostini/crostini_share_path.cc
+++ b/chrome/browser/chromeos/crostini/crostini_share_path.cc
@@ -293,7 +293,7 @@
   bool result = mount_points->GetVirtualPath(path, &virtual_path);
   if (result) {
     storage::FileSystemURL url = mount_points->CreateCrackedFileSystemURL(
-        GURL(), storage::kFileSystemTypeExternal, virtual_path);
+        url::Origin(), storage::kFileSystemTypeExternal, virtual_path);
     result = file_manager::util::ConvertFileSystemURLToPathInsideCrostini(
         profile_, url, &inside);
   }
diff --git a/chrome/browser/chromeos/crostini/crostini_util.cc b/chrome/browser/chromeos/crostini/crostini_util.cc
index ba442527..fbe2c59 100644
--- a/chrome/browser/chromeos/crostini/crostini_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_util.cc
@@ -16,6 +16,7 @@
 #include "base/timer/timer.h"
 #include "chrome/browser/chromeos/crostini/crostini_app_launch_observer.h"
 #include "chrome/browser/chromeos/crostini/crostini_manager.h"
+#include "chrome/browser/chromeos/crostini/crostini_package_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
diff --git a/chrome/browser/chromeos/crostini/crosvm_process_list.cc b/chrome/browser/chromeos/crostini/crosvm_process_list.cc
index ad0a19b..9e7f69b 100644
--- a/chrome/browser/chromeos/crostini/crosvm_process_list.cc
+++ b/chrome/browser/chromeos/crostini/crosvm_process_list.cc
@@ -31,7 +31,7 @@
                          pid_t* vm_concierge_pid_out,
                          const base::FilePath& slash_proc) {
   base::FilePath file_path =
-      slash_proc.Append(base::IntToString(pid)).Append("stat");
+      slash_proc.Append(base::NumberToString(pid)).Append("stat");
   base::Optional<chromeos::system::SingleProcStat> stat =
       chromeos::system::GetSingleProcStat(file_path);
   if (!stat.has_value())
diff --git a/chrome/browser/chromeos/crostini/crosvm_process_list_unittest.cc b/chrome/browser/chromeos/crostini/crosvm_process_list_unittest.cc
index 433c25e..1499e814 100644
--- a/chrome/browser/chromeos/crostini/crosvm_process_list_unittest.cc
+++ b/chrome/browser/chromeos/crostini/crosvm_process_list_unittest.cc
@@ -60,7 +60,7 @@
                            .stime = 1333,
                            .rss = 3149}}};
   WriteContentsToFileUnderSubdir(stat_contents,
-                                 base::IntToString(concierge_pid), "stat");
+                                 base::NumberToString(concierge_pid), "stat");
   EXPECT_EQ(expected, GetCrosvmPidStatMap(slash_proc_));
 }
 
@@ -85,7 +85,7 @@
       "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
       "140726650461974 140726650461996 140726650461996 140726650462178 0";
   WriteContentsToFileUnderSubdir(stat_contents,
-                                 base::IntToString(concierge_pid), "stat");
+                                 base::NumberToString(concierge_pid), "stat");
 
   pid_t other_pid = 1111;
   std::string other_stat_contents =
@@ -95,7 +95,7 @@
       "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
       "140726650461974 140726650461996 140726650461996 140726650462178 0";
   WriteContentsToFileUnderSubdir(other_stat_contents,
-                                 base::IntToString(other_pid), "stat");
+                                 base::NumberToString(other_pid), "stat");
 
   PidStatMap expected = {{concierge_pid,
                           {.pid = concierge_pid,
@@ -116,7 +116,7 @@
       "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
       "140726650461974 140726650461996 140726650461996 140726650462178 0";
   WriteContentsToFileUnderSubdir(stat_contents,
-                                 base::IntToString(concierge_pid), "stat");
+                                 base::NumberToString(concierge_pid), "stat");
 
   pid_t child_pid = 3333;
   std::string child_stat_contents =
@@ -126,7 +126,7 @@
       "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
       "140726650461974 140726650461996 140726650461996 140726650462178 0";
   WriteContentsToFileUnderSubdir(child_stat_contents,
-                                 base::IntToString(child_pid), "stat");
+                                 base::NumberToString(child_pid), "stat");
 
   pid_t grand_child_pid = 4444;
   std::string grand_child_stat_contents =
@@ -137,7 +137,7 @@
       "0 0 0 0 0 17 2 0 0 0 0 0 96193203229904 96193203257392 96193216815104 "
       "140726650461974 140726650461996 140726650461996 140726650462178 0";
   WriteContentsToFileUnderSubdir(grand_child_stat_contents,
-                                 base::IntToString(grand_child_pid), "stat");
+                                 base::NumberToString(grand_child_pid), "stat");
 
   PidStatMap expected = {{concierge_pid,
                           {.pid = concierge_pid,
diff --git a/chrome/browser/chromeos/display/display_prefs_browsertest.cc b/chrome/browser/chromeos/display/display_prefs_browsertest.cc
index 50dea52b..704698cc 100644
--- a/chrome/browser/chromeos/display/display_prefs_browsertest.cc
+++ b/chrome/browser/chromeos/display/display_prefs_browsertest.cc
@@ -28,7 +28,7 @@
     const base::DictionaryValue* display_properties =
         local_state_->GetDictionary(ash::prefs::kDisplayProperties);
     return display_properties ? display_properties->FindKeyOfType(
-                                    base::Int64ToString(display_id),
+                                    base::NumberToString(display_id),
                                     base::Value::Type::DICTIONARY)
                               : nullptr;
   }
diff --git a/chrome/browser/chromeos/drive/file_system_util.cc b/chrome/browser/chromeos/drive/file_system_util.cc
index e8dc94d..a80838c 100644
--- a/chrome/browser/chromeos/drive/file_system_util.cc
+++ b/chrome/browser/chromeos/drive/file_system_util.cc
@@ -41,7 +41,9 @@
 #include "components/prefs/pref_service.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/network_service_instance.h"
 #include "net/base/escape.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
 #include "storage/browser/fileapi/file_system_url.h"
 
 using content::BrowserThread;
@@ -231,15 +233,18 @@
   auto* drive_integration_service = GetIntegrationServiceByProfile(profile);
   if (!drive_integration_service)
     return DRIVE_DISCONNECTED_NOSERVICE;
-  if (net::NetworkChangeNotifier::IsOffline())
+  auto* network_connection_tracker = content::GetNetworkConnectionTracker();
+  if (network_connection_tracker->IsOffline())
     return DRIVE_DISCONNECTED_NONETWORK;
   auto* drive_service = drive_integration_service->drive_service();
   if (drive_service && !drive_service->CanSendRequest())
     return DRIVE_DISCONNECTED_NOTREADY;
 
+  auto connection_type = network::mojom::ConnectionType::CONNECTION_UNKNOWN;
+  network_connection_tracker->GetConnectionType(&connection_type,
+                                                base::DoNothing());
   const bool is_connection_cellular =
-      net::NetworkChangeNotifier::IsConnectionCellular(
-          net::NetworkChangeNotifier::GetConnectionType());
+      network::NetworkConnectionTracker::IsConnectionCellular(connection_type);
   const bool disable_sync_over_celluar =
       profile->GetPrefs()->GetBoolean(prefs::kDisableDriveOverCellular);
 
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 c876501..698f488 100644
--- a/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
+++ b/chrome/browser/chromeos/extensions/autotest_private/autotest_private_api.cc
@@ -264,7 +264,7 @@
           break;
 
         default:
-          user_image = base::IntToString(user->image_index());
+          user_image = base::NumberToString(user->image_index());
           break;
       }
       result->SetString("userImage", user_image);
@@ -784,7 +784,7 @@
                         base::Value(package_info->package_version));
   package_value->SetKey(
       "lastBackupAndroidId",
-      base::Value(base::Int64ToString(package_info->last_backup_android_id)));
+      base::Value(base::NumberToString(package_info->last_backup_android_id)));
   package_value->SetKey("lastBackupTime",
                         base::Value(base::Time::FromDeltaSinceWindowsEpoch(
                                         base::TimeDelta::FromMicroseconds(
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 c356da5..528a2de2 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_base.cc
@@ -33,11 +33,11 @@
     if (log_on_completion_) {
       logger->Log(logging::LOG_INFO, "%s[%d] %s. (elapsed time: %sms)", name(),
                   request_id(), success ? "succeeded" : "failed",
-                  base::Int64ToString(elapsed).c_str());
+                  base::NumberToString(elapsed).c_str());
     } else if (elapsed >= kSlowOperationThresholdMs) {
       logger->Log(logging::LOG_WARNING,
                   "PEFORMANCE WARNING: %s[%d] was slow. (elapsed time: %sms)",
-                  name(), request_id(), base::Int64ToString(elapsed).c_str());
+                  name(), request_id(), base::NumberToString(elapsed).c_str());
     }
   }
   ChromeAsyncExtensionFunction::OnResponded();
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
index d6600ca..f92e2b8 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_drive.cc
@@ -43,12 +43,13 @@
 #include "components/drive/chromeos/search_metadata.h"
 #include "components/drive/event_logger.h"
 #include "content/public/browser/browser_thread.h"
+#include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/storage_partition.h"
 #include "google_apis/drive/auth_service.h"
 #include "google_apis/drive/drive_api_url_generator.h"
 #include "mojo/public/cpp/bindings/callback_helpers.h"
-#include "net/base/network_change_notifier.h"
 #include "services/identity/public/cpp/identity_manager.h"
+#include "services/network/public/cpp/network_connection_tracker.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "storage/common/fileapi/file_system_info.h"
 #include "storage/common/fileapi/file_system_util.h"
@@ -1129,7 +1130,7 @@
   }
 
   operation_start_ = base::TimeTicks::Now();
-  is_offline_ = net::NetworkChangeNotifier::IsOffline();
+  is_offline_ = content::GetNetworkConnectionTracker()->IsOffline();
 
   drive::FileSystemInterface* const file_system =
       drive::util::GetFileSystemByProfile(GetProfile());
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
index 67cab0d..6e92b9f 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest.cc
@@ -152,10 +152,6 @@
       command_line->AppendSwitchASCII("force-tablet-mode", "touch_view");
       command_line->AppendSwitch(keyboard::switches::kEnableVirtualKeyboard);
     }
-
-    // TODO(crbug.com/879404): Fix tests to work with NativeSMB.
-    // Tests assume that no native FSPs are enabled.
-    scoped_feature_list_.InitAndDisableFeature(features::kNativeSmb);
   }
 
   GuestMode GetGuestMode() const override { return GetParam().guest_mode; }
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
index 5c96bef7..d270402 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.cc
@@ -1836,6 +1836,11 @@
     return;
   }
 
+  if (name == "isSmbEnabled") {
+    *output = IsSmbEnabled() ? "true" : "false";
+    return;
+  }
+
   FAIL() << "Unknown test message: " << name;
 }
 
@@ -1889,4 +1894,8 @@
   waiter.EnableVirtualKeyboard();
 }
 
+bool FileManagerBrowserTestBase::IsSmbEnabled() const {
+  return base::FeatureList::IsEnabled(features::kNativeSmb);
+}
+
 }  // namespace file_manager
diff --git a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
index d4aa368..92b9c0951 100644
--- a/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
+++ b/chrome/browser/chromeos/file_manager/file_manager_browsertest_base.h
@@ -117,6 +117,9 @@
   // Called during tablet mode test setup to enable the Ash virtual keyboard.
   void EnableVirtualKeyboard();
 
+  // Called during tests to determine if SMB file shares is enabled.
+  bool IsSmbEnabled() const;
+
   base::test::ScopedFeatureList feature_list_;
 
   std::unique_ptr<DownloadsTestVolume> local_volume_;
diff --git a/chrome/browser/chromeos/file_manager/fileapi_util_unittest.cc b/chrome/browser/chromeos/file_manager/fileapi_util_unittest.cc
index 8e2280f..3aa205f 100644
--- a/chrome/browser/chromeos/file_manager/fileapi_util_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/fileapi_util_unittest.cc
@@ -119,7 +119,7 @@
   EXPECT_TRUE(result[2]->get_file_system()->url.is_valid());
   const storage::FileSystemURL url =
       context->CrackURL(result[2]->get_file_system()->url);
-  EXPECT_EQ(GURL("http://example.com"), url.origin());
+  EXPECT_EQ(GURL("http://example.com"), url.origin().GetURL());
   EXPECT_EQ(storage::kFileSystemTypeIsolated, url.mount_type());
   EXPECT_EQ(storage::kFileSystemTypeProvided, url.type());
   EXPECT_EQ(55u, result[2]->get_file_system()->length);
diff --git a/chrome/browser/chromeos/file_manager/path_util.cc b/chrome/browser/chromeos/file_manager/path_util.cc
index 709b989..aec6e53 100644
--- a/chrome/browser/chromeos/file_manager/path_util.cc
+++ b/chrome/browser/chromeos/file_manager/path_util.cc
@@ -165,14 +165,22 @@
 }
 
 bool MigratePathFromOldFormat(Profile* profile,
+                              const base::FilePath& old_base,
                               const base::FilePath& old_path,
                               base::FilePath* new_path) {
-  const base::FilePath old_base = DownloadPrefs::GetDefaultDownloadDirectory();
   const base::FilePath new_base = GetMyFilesFolderForProfile(profile);
 
+  // Special case, migrating /home/chronos/user which is set early (before a
+  // profile is attached to the browser process) to default to
+  // /home/chronos/u-{hash}/MyFiles/Downloads.
+  if (old_path == old_base &&
+      old_path == base::FilePath("/home/chronos/user")) {
+    *new_path = GetDownloadsFolderForProfile(profile);
+    return true;
+  }
+
   base::FilePath relative;
-  if (old_path == old_base ||
-      old_base.AppendRelativePath(old_path, &relative)) {
+  if (old_base.AppendRelativePath(old_path, &relative)) {
     *new_path = new_base.Append(relative);
     return old_path != *new_path;
   }
@@ -498,6 +506,9 @@
   } else if (ReplacePrefix(&result, GetCrostiniMountDirectory(profile).value(),
                            l10n_util::GetStringUTF8(
                                IDS_FILE_BROWSER_LINUX_FILES_ROOT_LABEL))) {
+  } else if (ReplacePrefix(&result, kRemovableMediaPath,
+                           l10n_util::GetStringUTF8(
+                               IDS_FILE_BROWSER_EXTERNAL_STORAGE_ROOT_LABEL))) {
   }
 
   base::ReplaceChars(result, "/", " \u203a ", &result);
diff --git a/chrome/browser/chromeos/file_manager/path_util.h b/chrome/browser/chromeos/file_manager/path_util.h
index 4ad9490..f082d4a0 100644
--- a/chrome/browser/chromeos/file_manager/path_util.h
+++ b/chrome/browser/chromeos/file_manager/path_util.h
@@ -38,12 +38,15 @@
 // As of now (M40), the conversion is used only during initialization of
 // download_prefs, where profile unaware initialization precedes profile
 // aware stage. Below are the list of relocations we have made in the past.
+// *Updated in M73 to handle /home/chronos/user to
+// /home/chronos/u-{hash}/MyFiles/Downloads
 //
 // M27: crbug.com/229304, for supporting {offline, recent, shared} folders
 //   in Drive. Migration code for this is removed in M34.
 // M34-35: crbug.com/313539, 356322, for supporting multi profiles.
 //   Migration code is removed in M40.
 bool MigratePathFromOldFormat(Profile* profile,
+                              const base::FilePath& old_base,
                               const base::FilePath& old_path,
                               base::FilePath* new_path);
 
diff --git a/chrome/browser/chromeos/file_manager/path_util_unittest.cc b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
index a9b4529..9000c4a 100644
--- a/chrome/browser/chromeos/file_manager/path_util_unittest.cc
+++ b/chrome/browser/chromeos/file_manager/path_util_unittest.cc
@@ -41,6 +41,7 @@
 #include "storage/browser/fileapi/external_mount_points.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using base::FilePath;
 using storage::FileSystemURL;
 
 namespace file_manager {
@@ -183,7 +184,9 @@
             GetPathDisplayTextForSettings(
                 profile_.get(),
                 "/media/fuse/crostini_0123456789abcdef_termina_penguin/foo"));
-
+  EXPECT_EQ(
+      "External storage \u203a foo",
+      GetPathDisplayTextForSettings(profile_.get(), "/media/removable/foo"));
   {
     base::test::ScopedFeatureList features;
     features.InitAndDisableFeature(chromeos::features::kDriveFs);
@@ -312,29 +315,59 @@
   chromeos::ScopedSetRunningOnChromeOSForTesting fake_release(kLsbRelease,
                                                               base::Time());
 
-  // This looks like "/home/chronos/u-hash/MyFiles" in the production
-  // environment.
-  const base::FilePath kDownloads = GetMyFilesFolderForProfile(profile_.get());
-  const base::FilePath kOldDownloads =
-      DownloadPrefs::GetDefaultDownloadDirectory();
+  // /home/chronos/u-${HASH}/MyFiles/Downloads
+  const FilePath kDownloadsFolder =
+      GetDownloadsFolderForProfile(profile_.get());
+  // /home/chronos/u-${HASH}/MyFiles/
+  const FilePath kMyFilesFolder = GetMyFilesFolderForProfile(profile_.get());
+  // In the device: /home/chronos/user
+  // In browser tests: /tmp/.org.chromium.Chromium.F0Ejp5
+  const FilePath old_base = DownloadPrefs::GetDefaultDownloadDirectory();
 
-  base::FilePath path;
+  FilePath path;
 
-  EXPECT_TRUE(MigratePathFromOldFormat(profile_.get(), kOldDownloads, &path));
-  EXPECT_EQ(kDownloads, path);
+  // Special case to convert the base pkth directly to MyFiles/Downloads,
+  // because DownloadPrefs is initially initialized to /home/chronos/user before
+  // we have the Profile fully set up and we want to set it to MyFiles/Downloads
+  // which is the default download folder for new users.
+  EXPECT_TRUE(MigratePathFromOldFormat(profile_.get(),
+                                       FilePath("/home/chronos/user"),
+                                       FilePath("/home/chronos/user"), &path));
+  EXPECT_EQ(kDownloadsFolder, path);
 
-  EXPECT_TRUE(MigratePathFromOldFormat(
-      profile_.get(), kOldDownloads.AppendASCII("a/b"), &path));
-  EXPECT_EQ(kDownloads.AppendASCII("a/b"), path);
+  EXPECT_TRUE(
+      MigratePathFromOldFormat(profile_.get(), FilePath("/home/chronos/user"),
+                               FilePath("/home/chronos/user/a/b"), &path));
+  EXPECT_EQ(kMyFilesFolder.AppendASCII("a/b"), path);
+  EXPECT_TRUE(
+      MigratePathFromOldFormat(profile_.get(), FilePath("/home/chronos/u-1234"),
+                               FilePath("/home/chronos/u-1234/a/b"), &path));
+  EXPECT_EQ(kMyFilesFolder.AppendASCII("a/b"), path);
 
-  // Path already in the new format is not converted.
-  EXPECT_FALSE(MigratePathFromOldFormat(profile_.get(),
-                                        kDownloads.AppendASCII("a/b"), &path));
-
-  // Only the "Downloads" path is converted.
+  // Path already in the new format is not converted, it's already inside
+  // MyFiles or MyFiles/Downloads.
   EXPECT_FALSE(MigratePathFromOldFormat(
-      profile_.get(), base::FilePath::FromUTF8Unsafe("/home/chronos/user/dl"),
-      &path));
+      profile_.get(), DownloadPrefs::GetDefaultDownloadDirectory(),
+      kMyFilesFolder.AppendASCII("a/b"), &path));
+  EXPECT_FALSE(MigratePathFromOldFormat(profile_.get(), kMyFilesFolder,
+                                        kMyFilesFolder.AppendASCII("a/b"),
+                                        &path));
+  EXPECT_FALSE(MigratePathFromOldFormat(
+      profile_.get(), DownloadPrefs::GetDefaultDownloadDirectory(),
+      kDownloadsFolder.AppendASCII("a/b"), &path));
+  EXPECT_FALSE(MigratePathFromOldFormat(profile_.get(), kMyFilesFolder,
+                                        kDownloadsFolder.AppendASCII("a/b"),
+                                        &path));
+
+  // Only /home/chronos/user is migrated when old_base == old_path.
+  EXPECT_FALSE(
+      MigratePathFromOldFormat(profile_.get(), FilePath("/home/chronos/u-1234"),
+                               FilePath("/home/chronos/u-1234"), &path));
+  // Won't migrate because old_path isn't inside the default downloads
+  // directory.
+  EXPECT_FALSE(MigratePathFromOldFormat(
+      profile_.get(), DownloadPrefs::GetDefaultDownloadDirectory(),
+      FilePath::FromUTF8Unsafe("/home/chronos/user/dl"), &path));
 }
 
 TEST_F(FileManagerPathUtilTest, ConvertFileSystemURLToPathInsideCrostini) {
@@ -598,8 +631,8 @@
 };
 
 FileSystemURL CreateExternalURL(const base::FilePath& path) {
-  return FileSystemURL::CreateForTest(GURL(), storage::kFileSystemTypeExternal,
-                                      path);
+  return FileSystemURL::CreateForTest(url::Origin(),
+                                      storage::kFileSystemTypeExternal, path);
 }
 
 TEST_F(FileManagerPathUtilConvertUrlTest, ConvertPathToArcUrl_Removable) {
@@ -657,7 +690,7 @@
   base::RunLoop run_loop;
   ConvertToContentUrls(
       std::vector<FileSystemURL>{FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeTest,
+          url::Origin(), storage::kFileSystemTypeTest,
           base::FilePath::FromUTF8Unsafe("/media/removable/a/b/c"))},
       base::BindOnce(
           [](base::RunLoop* run_loop, const std::vector<GURL>& urls) {
@@ -776,7 +809,7 @@
   base::RunLoop run_loop;
   ConvertToContentUrls(
       std::vector<FileSystemURL>{FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath::FromUTF8Unsafe(
               "/special/arc-documents-provider/"
               "com.android.providers.media.documents/"
@@ -798,7 +831,7 @@
   base::RunLoop run_loop;
   ConvertToContentUrls(
       std::vector<FileSystemURL>{FileSystemURL::CreateForTest(
-          GURL(), storage::kFileSystemTypeArcDocumentsProvider,
+          url::Origin(), storage::kFileSystemTypeArcDocumentsProvider,
           base::FilePath::FromUTF8Unsafe(
               "/special/arc-documents-provider/"
               "com.android.providers.media.documents/"
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc
index 7da7762..31336519e 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_reader_unittest.cc
@@ -73,8 +73,7 @@
   const storage::ExternalMountPoints* const mount_points =
       storage::ExternalMountPoints::GetSystemInstance();
   return mount_points->CreateCrackedFileSystemURL(
-      GURL(origin),
-      storage::kFileSystemTypeExternal,
+      url::Origin::Create(GURL(origin)), storage::kFileSystemTypeExternal,
       base::FilePath::FromUTF8Unsafe(mount_point_name).Append(file_path));
 }
 
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer_unittest.cc b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer_unittest.cc
index a3c05e7..f0eb1e5 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer_unittest.cc
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/file_stream_writer_unittest.cc
@@ -55,8 +55,7 @@
   const storage::ExternalMountPoints* const mount_points =
       storage::ExternalMountPoints::GetSystemInstance();
   return mount_points->CreateCrackedFileSystemURL(
-      GURL(origin),
-      storage::kFileSystemTypeExternal,
+      url::Origin::Create(GURL(origin)), storage::kFileSystemTypeExternal,
       base::FilePath::FromUTF8Unsafe(mount_point_name).Append(file_path));
 }
 
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc b/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc
index 07997a3c..7cb7aa9 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/provider_async_file_util_unittest.cc
@@ -100,8 +100,7 @@
   const storage::ExternalMountPoints* const mount_points =
       storage::ExternalMountPoints::GetSystemInstance();
   return mount_points->CreateCrackedFileSystemURL(
-      GURL(origin),
-      storage::kFileSystemTypeExternal,
+      url::Origin::Create(GURL(origin)), storage::kFileSystemTypeExternal,
       base::FilePath::FromUTF8Unsafe(mount_point_name).Append(file_path));
 }
 
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.cc b/chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.cc
index af1b8f0c..b3ceef61 100644
--- a/chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.cc
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/watcher_manager.cc
@@ -56,11 +56,8 @@
     return;
   }
 
-  parser.file_system()->AddWatcher(url.origin(),
-                                   parser.file_path(),
-                                   recursive,
-                                   false /* persistent */,
-                                   callback,
+  parser.file_system()->AddWatcher(url.origin().GetURL(), parser.file_path(),
+                                   recursive, false /* persistent */, callback,
                                    notification_callback);
 }
 
@@ -80,8 +77,8 @@
     return;
   }
 
-  parser.file_system()->RemoveWatcher(
-      url.origin(), parser.file_path(), recursive, callback);
+  parser.file_system()->RemoveWatcher(url.origin().GetURL(), parser.file_path(),
+                                      recursive, callback);
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc b/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc
index bbb9650..bf4b57e44 100644
--- a/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc
+++ b/chrome/browser/chromeos/file_system_provider/mount_path_util_unittest.cc
@@ -55,8 +55,7 @@
   DCHECK(file_path.IsAbsolute());
   base::FilePath relative_path(file_path.value().substr(1));
   return mount_points->CreateCrackedFileSystemURL(
-      GURL(origin),
-      storage::kFileSystemTypeExternal,
+      url::Origin::Create(GURL(origin)), storage::kFileSystemTypeExternal,
       base::FilePath(mount_path.BaseName().Append(relative_path)));
 }
 
diff --git a/chrome/browser/chromeos/file_system_provider/service_unittest.cc b/chrome/browser/chromeos/file_system_provider/service_unittest.cc
index 5cf1eef3..cdf842e 100644
--- a/chrome/browser/chromeos/file_system_provider/service_unittest.cc
+++ b/chrome/browser/chromeos/file_system_provider/service_unittest.cc
@@ -215,7 +215,7 @@
   const size_t kMaxFileSystems = 16;
   for (size_t i = 0; i < kMaxFileSystems; ++i) {
     const std::string file_system_id =
-        std::string("test-") + base::IntToString(i);
+        std::string("test-") + base::NumberToString(i);
     EXPECT_EQ(base::File::FILE_OK,
               service_->MountFileSystem(
                   kProviderId, MountOptions(file_system_id, kDisplayName)));
diff --git a/chrome/browser/chromeos/fileapi/external_file_url_util_unittest.cc b/chrome/browser/chromeos/fileapi/external_file_url_util_unittest.cc
index 99951e3..fcf8af9 100644
--- a/chrome/browser/chromeos/fileapi/external_file_url_util_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/external_file_url_util_unittest.cc
@@ -33,13 +33,10 @@
 
   storage::FileSystemURL CreateExpectedURL(const base::FilePath& path) {
     return storage::FileSystemURL::CreateForTest(
-        GURL("chrome-extension://xxx"),
+        url::Origin::Create(GURL("chrome-extension://xxx")),
         storage::kFileSystemTypeExternal,
-        base::FilePath("drive-test-user-hash").Append(path),
-        "",
-        storage::kFileSystemTypeDrive,
-        base::FilePath(),
-        "",
+        base::FilePath("drive-test-user-hash").Append(path), "",
+        storage::kFileSystemTypeDrive, base::FilePath(), "",
         storage::FileSystemMountOption());
   }
 
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend.cc b/chrome/browser/chromeos/fileapi/file_system_backend.cc
index d319e597..1958129a 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend.cc
@@ -136,7 +136,7 @@
     // Not under a mount point, so return an error, since the root is not
     // accessible.
     GURL root_url = GURL(storage::GetExternalFileSystemRootURIString(
-        url.origin(), std::string()));
+        url.origin().GetURL(), std::string()));
     std::move(callback).Run(root_url, std::string(),
                             base::File::FILE_ERROR_SECURITY);
     return;
@@ -145,7 +145,7 @@
   std::string name;
   // Construct a URL restricted to the found mount point.
   std::string root_url =
-      storage::GetExternalFileSystemRootURIString(url.origin(), id);
+      storage::GetExternalFileSystemRootURIString(url.origin().GetURL(), id);
 
   // For removable and archives, the file system root is the external mount
   // point plus the inner mount point.
@@ -224,7 +224,7 @@
     return false;
 
   // If there is no origin set, then it's an internal access.
-  if (url.origin().is_empty())
+  if (url.origin().opaque())
     return true;
 
   const std::string& extension_id = url.origin().host();
diff --git a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
index 1c60ed91..2060402 100644
--- a/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/file_system_backend_unittest.cc
@@ -29,9 +29,8 @@
                                   const char* path,
                                   ExternalMountPoints* mount_points) {
   return mount_points->CreateCrackedFileSystemURL(
-      GURL("chrome-extension://" + extension + "/"),
-      storage::kFileSystemTypeExternal,
-      base::FilePath::FromUTF8Unsafe(path));
+      url::Origin::Create(GURL("chrome-extension://" + extension + "/")),
+      storage::kFileSystemTypeExternal, base::FilePath::FromUTF8Unsafe(path));
 }
 
 TEST(ChromeOSFileSystemBackendTest, DefaultMountPoints) {
diff --git a/chrome/browser/chromeos/fileapi/recent_model_unittest.cc b/chrome/browser/chromeos/fileapi/recent_model_unittest.cc
index edfabe4..09f1485 100644
--- a/chrome/browser/chromeos/fileapi/recent_model_unittest.cc
+++ b/chrome/browser/chromeos/fileapi/recent_model_unittest.cc
@@ -27,7 +27,7 @@
 RecentFile MakeRecentFile(const std::string& name,
                           const base::Time& last_modified) {
   storage::FileSystemURL url = storage::FileSystemURL::CreateForTest(
-      GURL(),  // origin
+      url::Origin(),  // origin
       storage::kFileSystemTypeNativeLocal, base::FilePath(name));
   return RecentFile(url, last_modified);
 }
diff --git a/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc b/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc
index efe4580..d7e7ff02 100644
--- a/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc
+++ b/chrome/browser/chromeos/first_run/drive_first_run_browsertest.cc
@@ -111,7 +111,7 @@
   // Configure the endpoint to use the test server's port.
   const GURL url(kTestEndpointUrl);
   GURL::Replacements replacements;
-  std::string port(base::IntToString(embedded_test_server()->port()));
+  std::string port(base::NumberToString(embedded_test_server()->port()));
   replacements.SetPortStr(port);
   endpoint_url_ = url.ReplaceComponents(replacements).spec();
   controller_->SetAppInfoForTest(kTestAppId, endpoint_url_);
diff --git a/chrome/browser/chromeos/login/crash_restore_browsertest.cc b/chrome/browser/chromeos/login/crash_restore_browsertest.cc
index 394de345..73a5aff 100644
--- a/chrome/browser/chromeos/login/crash_restore_browsertest.cc
+++ b/chrome/browser/chromeos/login/crash_restore_browsertest.cc
@@ -176,7 +176,7 @@
       auto user_dict = std::make_unique<base::DictionaryValue>();
       user_dict->SetString("account_type", "google");
       user_dict->SetString("email", user_id);
-      user_dict->SetString("gaia_id", base::IntToString(gaia_id++));
+      user_dict->SetString("gaia_id", base::NumberToString(gaia_id++));
       known_users_list->Append(std::move(user_dict));
     }
     local_state.SetList("KnownUsers", std::move(known_users_list));
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_mode_detector_unittest.cc b/chrome/browser/chromeos/login/demo_mode/demo_mode_detector_unittest.cc
index 203f1906..a61578a0 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_mode_detector_unittest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_mode_detector_unittest.cc
@@ -80,7 +80,7 @@
 void DemoModeDetectorTest::SetTimeOnOobePref(base::TimeDelta time_on_oobe) {
   local_state_.SetUserPref(prefs::kTimeOnOobe,
                            std::make_unique<base::Value>(
-                               base::Int64ToString(time_on_oobe.InSeconds())));
+                               base::NumberToString(time_on_oobe.InSeconds())));
 }
 
 base::TimeDelta DemoModeDetectorTest::GetTimeOnOobePref() {
diff --git a/chrome/browser/chromeos/login/screens/hid_detection_screen.cc b/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
index 124be540..8d02b4c 100644
--- a/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
+++ b/chrome/browser/chromeos/login/screens/hid_detection_screen.cc
@@ -187,7 +187,7 @@
                                         uint32_t passkey) {
   VLOG(1) << "DisplayPassKey id = " << device->GetDeviceID()
           << " name = " << device->GetNameForDisplay();
-  std::string pincode = base::UintToString(passkey);
+  std::string pincode = base::NumberToString(passkey);
   pincode = std::string(kPincodeLength - pincode.length(), '0').append(pincode);
   // No differences in UI for passkey and pincode authentication calls.
   DisplayPinCode(device, pincode);
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc b/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
index 431e2249..1a382a3 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager.cc
@@ -13,13 +13,12 @@
 #include "chrome/browser/signin/chrome_device_id_helper.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/account_id/account_id.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/user_manager/user_manager.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/gaia_urls.h"
+#include "services/identity/public/cpp/accounts_mutator.h"
 #include "services/identity/public/cpp/identity_manager.h"
 
 namespace chromeos {
@@ -28,7 +27,7 @@
     : user_profile_(user_profile),
       restore_strategy_(RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN),
       state_(SESSION_RESTORE_NOT_STARTED) {
-  GetTokenService()->AddObserver(this);
+  GetIdentityManager()->AddObserver(this);
 
   // For telemetry, we mark session restore completed to avoid warnings from
   // MergeSessionThrottle.
@@ -80,20 +79,20 @@
 }
 
 void OAuth2LoginManager::RestoreSessionFromSavedTokens() {
-  ProfileOAuth2TokenService* token_service = GetTokenService();
-  const std::string primary_account_id = GetPrimaryAccountId();
-  if (token_service->RefreshTokenIsAvailable(primary_account_id)) {
+  identity::IdentityManager* identity_manager = GetIdentityManager();
+  if (identity_manager->HasPrimaryAccountWithRefreshToken()) {
     VLOG(1) << "OAuth2 refresh token is already loaded.";
     VerifySessionCookies();
   } else {
     VLOG(1) << "Waiting for OAuth2 refresh token being loaded from database.";
 
+    AccountInfo account_info = identity_manager->GetPrimaryAccountInfo();
     // Flag user with unknown token status in case there are no saved tokens
     // and OnRefreshTokenAvailable is not called. Flagging it here would
     // cause user to go through Gaia in next login to obtain a new refresh
     // token.
     user_manager::UserManager::Get()->SaveUserOAuthStatus(
-        AccountId::FromUserEmail(primary_account_id),
+        AccountId::FromUserEmail(account_info.email),
         user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN);
   }
 }
@@ -111,9 +110,9 @@
   return SessionRestoreIsRunning();
 }
 
-void OAuth2LoginManager::OnRefreshTokenAvailable(
-    const std::string& user_email) {
-  VLOG(1) << "OnRefreshTokenAvailable";
+void OAuth2LoginManager::OnRefreshTokenUpdatedForAccount(
+    const AccountInfo& account_info) {
+  VLOG(1) << "OnRefreshTokenUpdatedForAccount";
 
   if (state_ == SESSION_RESTORE_NOT_STARTED)
     return;
@@ -128,41 +127,45 @@
     return;
   }
   // Only restore session cookies for the primary account in the profile.
-  if (GetPrimaryAccountId() == user_email) {
+  if (GetPrimaryAccountId() == account_info.account_id) {
     // The refresh token has changed, so stop any ongoing actions that were
     // based on the old refresh token.
     Stop();
 
     // Token is loaded. Undo the flagging before token loading.
     user_manager::UserManager::Get()->SaveUserOAuthStatus(
-        AccountId::FromUserEmail(user_email),
+        AccountId::FromUserEmail(account_info.email),
         user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
 
     VerifySessionCookies();
   }
 }
 
-ProfileOAuth2TokenService* OAuth2LoginManager::GetTokenService() {
-  return ProfileOAuth2TokenServiceFactory::GetForProfile(user_profile_);
+identity::IdentityManager* OAuth2LoginManager::GetIdentityManager() {
+  return IdentityManagerFactory::GetForProfile(user_profile_);
 }
 
 std::string OAuth2LoginManager::GetPrimaryAccountId() {
   const std::string primary_account_id =
-      IdentityManagerFactory::GetForProfile(user_profile_)
-          ->GetPrimaryAccountId();
+      GetIdentityManager()->GetPrimaryAccountId();
   LOG_IF(ERROR, primary_account_id.empty()) << "Primary account id is empty.";
   return primary_account_id;
 }
 
 void OAuth2LoginManager::StoreOAuth2Token() {
-  UpdateCredentials(GetPrimaryAccountId());
-}
-
-void OAuth2LoginManager::UpdateCredentials(const std::string& account_id) {
-  DCHECK(!account_id.empty());
+  identity::IdentityManager* identity_manager = GetIdentityManager();
+  DCHECK(identity_manager->HasPrimaryAccount());
   DCHECK(!refresh_token_.empty());
-  // |account_id| is assumed to be already canonicalized if it's an email.
-  GetTokenService()->UpdateCredentials(account_id, refresh_token_);
+
+  // On ChromeOS, the primary account is set via
+  // IdentityManager::SetPrimaryAccountSynchronously(), which seeds the account
+  // info with AccountTrackerService. Hence, the primary account info will be
+  // available at this point.
+  AccountInfo primary_account_info = identity_manager->GetPrimaryAccountInfo();
+  identity_manager->GetAccountsMutator()->AddOrUpdateAccount(
+      primary_account_info.gaia, primary_account_info.email, refresh_token_,
+      primary_account_info.is_under_advanced_protection,
+      signin_metrics::SourceForRefreshTokenOperation::kUnknown);
 
   for (auto& observer : observer_list_)
     observer.OnNewRefreshTokenAvaiable(user_profile_);
@@ -172,8 +175,7 @@
   DCHECK(!login_verifier_.get());
   login_verifier_.reset(new OAuth2LoginVerifier(
       this, GaiaCookieManagerServiceFactory::GetForProfile(user_profile_),
-      IdentityManagerFactory::GetForProfile(user_profile_),
-      GetPrimaryAccountId(), oauthlogin_access_token_));
+      GetIdentityManager(), GetPrimaryAccountId(), oauthlogin_access_token_));
 
   if (restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN) {
     login_verifier_->VerifyUserCookies();
@@ -189,7 +191,7 @@
 }
 
 void OAuth2LoginManager::Shutdown() {
-  GetTokenService()->RemoveObserver(this);
+  GetIdentityManager()->RemoveObserver(this);
   login_verifier_.reset();
 }
 
diff --git a/chrome/browser/chromeos/login/signin/oauth2_login_manager.h b/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
index 3c08c11..887af40c 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
+++ b/chrome/browser/chromeos/login/signin/oauth2_login_manager.h
@@ -14,11 +14,10 @@
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/login/signin/oauth2_login_verifier.h"
 #include "components/keyed_service/core/keyed_service.h"
-#include "google_apis/gaia/oauth2_token_service.h"
+#include "services/identity/public/cpp/identity_manager.h"
 
 class GoogleServiceAuthError;
 class Profile;
-class ProfileOAuth2TokenService;
 
 namespace chromeos {
 
@@ -26,7 +25,7 @@
 // OAuth2 refresh tokens or pre-authenticated cookie jar.
 class OAuth2LoginManager : public KeyedService,
                            public OAuth2LoginVerifier::Delegate,
-                           public OAuth2TokenService::Observer {
+                           public identity::IdentityManager::Observer {
  public:
   // Session restore states.
   enum SessionRestoreState {
@@ -153,15 +152,16 @@
       const std::vector<gaia::ListedAccount>& accounts) override;
   void OnListAccountsFailure(bool connection_error) override;
 
-  // OAuth2TokenService::Observer implementation:
-  void OnRefreshTokenAvailable(const std::string& user_email) override;
+  // identity::IdentityManager::Observer implementation:
+  void OnRefreshTokenUpdatedForAccount(
+      const AccountInfo& account_info) override;
 
   // Signals delegate that authentication is completed, kicks off token fetching
   // process.
   void CompleteAuthentication();
 
-  // Retrieves ProfileOAuth2TokenService for |user_profile_|.
-  ProfileOAuth2TokenService* GetTokenService();
+  // Retrieves IdentityManager for |user_profile_|.
+  identity::IdentityManager* GetIdentityManager();
 
   // Retrieves the primary account for |user_profile_|.
   std::string GetPrimaryAccountId();
@@ -172,9 +172,6 @@
   // retrieve the associated account info.
   void StoreOAuth2Token();
 
-  // Update the token service and inform listeners of a new refresh token.
-  void UpdateCredentials(const std::string& account_id);
-
   // Checks if primary account sessions cookies are stale and restores them
   // if needed.
   void VerifySessionCookies();
diff --git a/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc b/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc
index df7f43d..9b3cdb1 100644
--- a/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc
+++ b/chrome/browser/chromeos/mobile/mobile_activator_unittest.cc
@@ -8,7 +8,7 @@
 #include <utility>
 
 #include "base/macros.h"
-#include "base/message_loop/message_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/values.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/network/network_connection_handler.h"
@@ -140,7 +140,7 @@
     cellular_network_.connection_state_ = state;
   }
 
-  base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   NetworkState cellular_network_;
   TestMobileActivator mobile_activator_;
 
diff --git a/chrome/browser/chromeos/net/network_throttling_observer_unittest.cc b/chrome/browser/chromeos/net/network_throttling_observer_unittest.cc
index 64578f1b..40f1a3b 100644
--- a/chrome/browser/chromeos/net/network_throttling_observer_unittest.cc
+++ b/chrome/browser/chromeos/net/network_throttling_observer_unittest.cc
@@ -4,7 +4,7 @@
 
 #include <memory>
 
-#include "base/message_loop/message_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "chrome/browser/chromeos/net/network_throttling_observer.h"
 #include "chrome/common/pref_names.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
@@ -50,7 +50,7 @@
   }
 
  private:
-  base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<NetworkStateHandler> network_state_handler_;
   std::unique_ptr<TestingPrefServiceSimple> local_state_;
   std::unique_ptr<NetworkThrottlingObserver> observer_;
diff --git a/chrome/browser/chromeos/policy/cloud_external_data_store.cc b/chrome/browser/chromeos/policy/cloud_external_data_store.cc
index 519a1f7..0fee627 100644
--- a/chrome/browser/chromeos/policy/cloud_external_data_store.cc
+++ b/chrome/browser/chromeos/policy/cloud_external_data_store.cc
@@ -20,9 +20,8 @@
 std::string GetSubkey(const std::string& policy, const std::string& hash) {
   DCHECK(!policy.empty());
   DCHECK(!hash.empty());
-  return base::IntToString(policy.size()) + ":" +
-         base::IntToString(hash.size()) + ":" +
-         policy + hash;
+  return base::NumberToString(policy.size()) + ":" +
+         base::NumberToString(hash.size()) + ":" + policy + hash;
 }
 
 }  // namespace
diff --git a/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager.cc b/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager.cc
index c992719..6ff8bad 100644
--- a/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager.cc
+++ b/chrome/browser/chromeos/policy/device_policy_cloud_external_data_manager.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/chromeos/policy/cloud_external_data_store.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/core/common/cloud/resource_cache.h"
+#include "components/policy/policy_constants.h"
 
 namespace policy {
 
@@ -19,11 +20,6 @@
 
 const char kCacheKey[] = "device_policy_external_data";
 
-// The maximum size of the device policy external data cache directory is set to
-// 21 MiB. It's calculated as the sum of the maximum allowed sizes for each of
-// the external data type device policies. At the moment, we have two of them:
-// DeviceWallpaperImage (16 MiB) and DeviceNativePrinters (5 MiB).
-const int64_t kCacheMaxSize = 21 * 1024 * 1024;
 // Only used for tests.
 int64_t g_cache_max_size_override = 0;
 
@@ -35,7 +31,7 @@
     const base::FilePath& cache_path,
     CloudPolicyStore* policy_store)
     : CloudExternalDataManagerBase(get_policy_details, backend_task_runner) {
-  int cache_max_size = kCacheMaxSize;
+  int cache_max_size = kDevicePolicyExternalDataResourceCacheSize;
   if (g_cache_max_size_override != 0)
     cache_max_size = g_cache_max_size_override;
   resource_cache_ = std::make_unique<ResourceCache>(
diff --git a/chrome/browser/chromeos/policy/device_status_collector.cc b/chrome/browser/chromeos/policy/device_status_collector.cc
index 319851f..e756938 100644
--- a/chrome/browser/chromeos/policy/device_status_collector.cc
+++ b/chrome/browser/chromeos/policy/device_status_collector.cc
@@ -852,7 +852,7 @@
 std::string DeviceStatusCollector::ActivityStorage::MakeActivityPeriodPrefKey(
     int64_t start,
     const std::string& user_email) {
-  const std::string day_key = base::Int64ToString(start);
+  const std::string day_key = base::NumberToString(start);
   if (user_email.empty())
     return day_key;
 
diff --git a/chrome/browser/chromeos/policy/heartbeat_scheduler.cc b/chrome/browser/chromeos/policy/heartbeat_scheduler.cc
index e20dfa01..3112cd4 100644
--- a/chrome/browser/chromeos/policy/heartbeat_scheduler.cc
+++ b/chrome/browser/chromeos/policy/heartbeat_scheduler.cc
@@ -351,11 +351,11 @@
   // big deal (the new message will replace the old, which is the behavior we
   // want anyway, per:
   // https://developer.chrome.com/apps/cloudMessaging#send_messages
-  message.id = base::Int64ToString(
-      base::Time::NowFromSystemTime().ToInternalValue());
+  message.id =
+      base::NumberToString(base::Time::NowFromSystemTime().ToInternalValue());
   message.data[kGcmMessageTypeKey] = kHeartbeatTypeValue;
-  message.data[kHeartbeatTimestampKey] = base::Int64ToString(
-      base::Time::NowFromSystemTime().ToJavaTime());
+  message.data[kHeartbeatTimestampKey] =
+      base::NumberToString(base::Time::NowFromSystemTime().ToJavaTime());
   message.data[kHeartbeatDomainNameKey] = enrollment_domain_;
   message.data[kHeartbeatDeviceIDKey] = device_id_;
   gcm_driver_->Send(kHeartbeatGCMAppID,
@@ -380,7 +380,7 @@
 
   gcm::OutgoingMessage message;
   message.id =
-      base::Int64ToString(base::Time::NowFromSystemTime().ToInternalValue());
+      base::NumberToString(base::Time::NowFromSystemTime().ToInternalValue());
   message.data[kGcmMessageTypeKey] = kUpstreamNotificationSignUpListeningEvents;
   message.data[kUpstreamNotificationNotifyKey] =
       GetDestinationID() + kHeartbeatGCMSenderSuffix;
diff --git a/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl_unittest.cc b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl_unittest.cc
index af294652..8c96de07 100644
--- a/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl_unittest.cc
+++ b/chrome/browser/chromeos/power/auto_screen_brightness/als_reader_impl_unittest.cc
@@ -68,7 +68,7 @@
 
  protected:
   void WriteLux(int lux) {
-    const std::string lux_string = base::IntToString(lux);
+    const std::string lux_string = base::NumberToString(lux);
     const int bytes_written = base::WriteFile(
         ambient_light_path_, lux_string.data(), lux_string.size());
     ASSERT_EQ(bytes_written, static_cast<int>(lux_string.size()))
diff --git a/chrome/browser/chromeos/power/cpu_data_collector.cc b/chrome/browser/chromeos/power/cpu_data_collector.cc
index c104501d..558eaa9 100644
--- a/chrome/browser/chromeos/power/cpu_data_collector.cc
+++ b/chrome/browser/chromeos/power/cpu_data_collector.cc
@@ -360,7 +360,7 @@
       return false;
     }
 
-    const std::string state_name = base::IntToString(freq_in_khz / 1000);
+    const std::string state_name = base::NumberToString(freq_in_khz / 1000);
     size_t index = EnsureInVector(state_name, cpu_freq_state_names);
     if (index >= freq_sample->time_in_state.size())
       freq_sample->time_in_state.resize(index + 1);
@@ -407,7 +407,7 @@
       return false;
     }
 
-    const std::string state_name = base::IntToString(freq_in_khz / 1000);
+    const std::string state_name = base::NumberToString(freq_in_khz / 1000);
     size_t index = EnsureInVector(state_name, cpu_freq_state_names);
     for (int cpu = 0; cpu < online_cpu_count; ++cpu) {
       // array.size() is previously checked to be equal to online_cpu_count+1.
diff --git a/chrome/browser/chromeos/power/freezer_cgroup_process_manager.cc b/chrome/browser/chromeos/power/freezer_cgroup_process_manager.cc
index 4948294e..69cf8a6 100644
--- a/chrome/browser/chromeos/power/freezer_cgroup_process_manager.cc
+++ b/chrome/browser/chromeos/power/freezer_cgroup_process_manager.cc
@@ -77,9 +77,9 @@
   void SetShouldFreezeRenderer(base::ProcessHandle handle, bool frozen) {
     DCHECK(file_thread_->RunsTasksInCurrentSequence());
 
-    WriteCommandToFile(base::IntToString(handle),
-                       frozen ? to_be_frozen_control_path_
-                              : default_control_path_);
+    WriteCommandToFile(
+        base::NumberToString(handle),
+        frozen ? to_be_frozen_control_path_ : default_control_path_);
   }
 
   void FreezeRenderers() {
diff --git a/chrome/browser/chromeos/printing/cups_print_job_notification.cc b/chrome/browser/chromeos/printing/cups_print_job_notification.cc
index ea66997b..da3bffd 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_notification.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_notification.cc
@@ -223,7 +223,7 @@
   if (print_job_->total_page_number() > 1) {
     message = l10n_util::GetStringFUTF16(
         IDS_PRINT_JOB_NOTIFICATION_MESSAGE,
-        base::IntToString16(print_job_->total_page_number()),
+        base::NumberToString16(print_job_->total_page_number()),
         base::UTF8ToUTF16(print_job_->printer().display_name()));
   } else {
     message = l10n_util::GetStringFUTF16(
diff --git a/chrome/browser/chromeos/printing/cups_printers_manager.cc b/chrome/browser/chromeos/printing/cups_printers_manager.cc
index 846a268..1faee11 100644
--- a/chrome/browser/chromeos/printing/cups_printers_manager.cc
+++ b/chrome/browser/chromeos/printing/cups_printers_manager.cc
@@ -128,6 +128,8 @@
 
     native_printers_allowed_.Init(prefs::kUserNativePrintersAllowed,
                                   pref_service);
+    send_username_and_filename_.Init(
+        prefs::kPrintingSendUsernameAndFilenameEnabled, pref_service);
   }
 
   ~CupsPrintersManagerImpl() override = default;
@@ -141,6 +143,17 @@
                       "UserNativePrintersAllowed is set to false";
       return {};
     }
+    if (send_username_and_filename_.GetValue()) {
+      std::vector<Printer> result(printers_[printer_class].size());
+      auto it_end = std::copy_if(
+          printers_[printer_class].begin(), printers_[printer_class].end(),
+          result.begin(), [](const Printer& printer) {
+            return !printer.HasNetworkProtocol() ||
+                   printer.GetProtocol() == Printer::kIpps;
+          });
+      result.resize(it_end - result.begin());
+      return result;
+    }
     return printers_.at(printer_class);
   }
 
@@ -556,6 +569,10 @@
   // Holds the current value of the pref |UserNativePrintersAllowed|.
   BooleanPrefMember native_printers_allowed_;
 
+  // Holds the current value of the pref
+  // |PrintingSendUsernameAndFilenameEnabled|.
+  BooleanPrefMember send_username_and_filename_;
+
   base::WeakPtrFactory<CupsPrintersManagerImpl> weak_ptr_factory_;
 };
 
@@ -599,6 +616,8 @@
   registry->RegisterBooleanPref(
       prefs::kUserNativePrintersAllowed, true,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+  registry->RegisterBooleanPref(prefs::kPrintingSendUsernameAndFilenameEnabled,
+                                false);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/session_length_limiter_unittest.cc b/chrome/browser/chromeos/session_length_limiter_unittest.cc
index 6d4fc32..b24dc155 100644
--- a/chrome/browser/chromeos/session_length_limiter_unittest.cc
+++ b/chrome/browser/chromeos/session_length_limiter_unittest.cc
@@ -122,7 +122,7 @@
 void SessionLengthLimiterTest::SetSessionStartTimePref(
     const base::TimeTicks& session_start_time) {
   local_state_.SetUserPref(prefs::kSessionStartTime,
-                           std::make_unique<base::Value>(base::Int64ToString(
+                           std::make_unique<base::Value>(base::NumberToString(
                                session_start_time.ToInternalValue())));
 }
 
diff --git a/chrome/browser/chromeos/settings/device_settings_provider.cc b/chrome/browser/chromeos/settings/device_settings_provider.cc
index 13048d4..3626a474 100644
--- a/chrome/browser/chromeos/settings/device_settings_provider.cc
+++ b/chrome/browser/chromeos/settings/device_settings_provider.cc
@@ -139,8 +139,10 @@
   std::string error;
   std::unique_ptr<base::Value> decoded_json =
       policy::DecodeJsonStringAndNormalize(json_string, policy_name, &error);
-  if (decoded_json)
-    pref_value_map->SetValue(setting_name, std::move(decoded_json));
+  if (decoded_json) {
+    pref_value_map->SetValue(
+        setting_name, base::Value::FromUniquePtrValue(std::move(decoded_json)));
+  }
 }
 
 void DecodeLoginPolicies(const em::ChromeDeviceSettingsProto& policy,
@@ -200,73 +202,71 @@
       policy.ephemeral_users_enabled().has_ephemeral_users_enabled() &&
       policy.ephemeral_users_enabled().ephemeral_users_enabled());
 
-  std::unique_ptr<base::ListValue> list(new base::ListValue());
+  std::vector<base::Value> list;
   const em::UserWhitelistProto& whitelist_proto = policy.user_whitelist();
   const RepeatedPtrField<std::string>& whitelist =
       whitelist_proto.user_whitelist();
-  for (RepeatedPtrField<std::string>::const_iterator it = whitelist.begin();
-       it != whitelist.end(); ++it) {
-    list->AppendString(*it);
+  for (const std::string& value : whitelist) {
+    list.push_back(base::Value(value));
   }
-  new_values_cache->SetValue(kAccountsPrefUsers, std::move(list));
+  new_values_cache->SetValue(kAccountsPrefUsers, base::Value(std::move(list)));
 
-  std::unique_ptr<base::ListValue> account_list(new base::ListValue());
+  std::vector<base::Value> account_list;
   const em::DeviceLocalAccountsProto device_local_accounts_proto =
       policy.device_local_accounts();
   const RepeatedPtrField<em::DeviceLocalAccountInfoProto>& accounts =
       device_local_accounts_proto.account();
   RepeatedPtrField<em::DeviceLocalAccountInfoProto>::const_iterator entry;
-  for (entry = accounts.begin(); entry != accounts.end(); ++entry) {
-    std::unique_ptr<base::DictionaryValue> entry_dict(
-        new base::DictionaryValue());
-    if (entry->has_type()) {
-      if (entry->has_account_id()) {
-        entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyId,
-                           base::Value(entry->account_id()));
+  for (const em::DeviceLocalAccountInfoProto& entry : accounts) {
+    base::Value entry_dict(base::Value::Type::DICTIONARY);
+    if (entry.has_type()) {
+      if (entry.has_account_id()) {
+        entry_dict.SetKey(kAccountsPrefDeviceLocalAccountsKeyId,
+                          base::Value(entry.account_id()));
       }
-      entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyType,
-                         base::Value(entry->type()));
-      if (entry->kiosk_app().has_app_id()) {
-        entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyKioskAppId,
-                           base::Value(entry->kiosk_app().app_id()));
+      entry_dict.SetKey(kAccountsPrefDeviceLocalAccountsKeyType,
+                        base::Value(entry.type()));
+      if (entry.kiosk_app().has_app_id()) {
+        entry_dict.SetKey(kAccountsPrefDeviceLocalAccountsKeyKioskAppId,
+                          base::Value(entry.kiosk_app().app_id()));
       }
-      if (entry->kiosk_app().has_update_url()) {
-        entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyKioskAppUpdateURL,
-                           base::Value(entry->kiosk_app().update_url()));
+      if (entry.kiosk_app().has_update_url()) {
+        entry_dict.SetKey(kAccountsPrefDeviceLocalAccountsKeyKioskAppUpdateURL,
+                          base::Value(entry.kiosk_app().update_url()));
       }
-      if (entry->android_kiosk_app().has_package_name()) {
-        entry_dict->SetKey(
+      if (entry.android_kiosk_app().has_package_name()) {
+        entry_dict.SetKey(
             chromeos::kAccountsPrefDeviceLocalAccountsKeyArcKioskPackage,
-            base::Value(entry->android_kiosk_app().package_name()));
+            base::Value(entry.android_kiosk_app().package_name()));
       }
-      if (entry->android_kiosk_app().has_class_name()) {
-        entry_dict->SetKey(
+      if (entry.android_kiosk_app().has_class_name()) {
+        entry_dict.SetKey(
             chromeos::kAccountsPrefDeviceLocalAccountsKeyArcKioskClass,
-            base::Value(entry->android_kiosk_app().class_name()));
+            base::Value(entry.android_kiosk_app().class_name()));
       }
-      if (entry->android_kiosk_app().has_action()) {
-        entry_dict->SetKey(
+      if (entry.android_kiosk_app().has_action()) {
+        entry_dict.SetKey(
             chromeos::kAccountsPrefDeviceLocalAccountsKeyArcKioskAction,
-            base::Value(entry->android_kiosk_app().action()));
+            base::Value(entry.android_kiosk_app().action()));
       }
-      if (entry->android_kiosk_app().has_display_name()) {
-        entry_dict->SetKey(
+      if (entry.android_kiosk_app().has_display_name()) {
+        entry_dict.SetKey(
             chromeos::kAccountsPrefDeviceLocalAccountsKeyArcKioskDisplayName,
-            base::Value(entry->android_kiosk_app().display_name()));
+            base::Value(entry.android_kiosk_app().display_name()));
       }
-    } else if (entry->has_deprecated_public_session_id()) {
+    } else if (entry.has_deprecated_public_session_id()) {
       // Deprecated public session specification.
-      entry_dict->SetKey(kAccountsPrefDeviceLocalAccountsKeyId,
-                         base::Value(entry->deprecated_public_session_id()));
-      entry_dict->SetKey(
+      entry_dict.SetKey(kAccountsPrefDeviceLocalAccountsKeyId,
+                        base::Value(entry.deprecated_public_session_id()));
+      entry_dict.SetKey(
           kAccountsPrefDeviceLocalAccountsKeyType,
           base::Value(
               em::DeviceLocalAccountInfoProto::ACCOUNT_TYPE_PUBLIC_SESSION));
     }
-    account_list->Append(std::move(entry_dict));
+    account_list.push_back(std::move(entry_dict));
   }
   new_values_cache->SetValue(kAccountsPrefDeviceLocalAccounts,
-                             std::move(account_list));
+                             base::Value(std::move(account_list)));
 
   if (policy.has_device_local_accounts()) {
     if (policy.device_local_accounts().has_auto_login_id()) {
@@ -289,14 +289,13 @@
       policy.device_local_accounts().prompt_for_network_when_offline());
 
   if (policy.has_start_up_flags()) {
-    std::unique_ptr<base::ListValue> list(new base::ListValue());
+    std::vector<base::Value> list;
     const em::StartUpFlagsProto& flags_proto = policy.start_up_flags();
     const RepeatedPtrField<std::string>& flags = flags_proto.flags();
-    for (RepeatedPtrField<std::string>::const_iterator it = flags.begin();
-         it != flags.end(); ++it) {
-      list->AppendString(*it);
+    for (const std::string& entry : flags) {
+      list.push_back(base::Value(entry));
     }
-    new_values_cache->SetValue(kStartUpFlags, std::move(list));
+    new_values_cache->SetValue(kStartUpFlags, base::Value(std::move(list)));
   }
 
   if (policy.has_saml_settings()) {
@@ -328,44 +327,46 @@
   }
 
   if (policy.has_login_video_capture_allowed_urls()) {
-    std::unique_ptr<base::ListValue> list(new base::ListValue());
+    std::vector<base::Value> list;
     const em::LoginVideoCaptureAllowedUrlsProto&
         login_video_capture_allowed_urls_proto =
             policy.login_video_capture_allowed_urls();
     for (const auto& value : login_video_capture_allowed_urls_proto.urls()) {
-      list->AppendString(value);
+      list.push_back(base::Value(value));
     }
-    new_values_cache->SetValue(kLoginVideoCaptureAllowedUrls, std::move(list));
+    new_values_cache->SetValue(kLoginVideoCaptureAllowedUrls,
+                               base::Value(std::move(list)));
   }
 
   if (policy.has_device_login_screen_app_install_list()) {
-    std::unique_ptr<base::ListValue> apps(new base::ListValue);
+    std::vector<base::Value> apps;
     const em::DeviceLoginScreenAppInstallListProto& proto(
         policy.device_login_screen_app_install_list());
     for (const auto& app : proto.device_login_screen_app_install_list())
-      apps->AppendString(app);
+      apps.push_back(base::Value(app));
     new_values_cache->SetValue(kDeviceLoginScreenAppInstallList,
-                               std::move(apps));
+                               base::Value(std::move(apps)));
   }
 
   if (policy.has_login_screen_locales()) {
-    std::unique_ptr<base::ListValue> locales(new base::ListValue);
+    std::vector<base::Value> locales;
     const em::LoginScreenLocalesProto& login_screen_locales(
         policy.login_screen_locales());
     for (const auto& locale : login_screen_locales.login_screen_locales())
-      locales->AppendString(locale);
-    new_values_cache->SetValue(kDeviceLoginScreenLocales, std::move(locales));
+      locales.push_back(base::Value(locale));
+    new_values_cache->SetValue(kDeviceLoginScreenLocales,
+                               base::Value(std::move(locales)));
   }
 
   if (policy.has_login_screen_input_methods()) {
-    std::unique_ptr<base::ListValue> input_methods(new base::ListValue);
+    std::vector<base::Value> input_methods;
     const em::LoginScreenInputMethodsProto& login_screen_input_methods(
         policy.login_screen_input_methods());
     for (const auto& input_method :
          login_screen_input_methods.login_screen_input_methods())
-      input_methods->AppendString(input_method);
+      input_methods.push_back(base::Value(input_method));
     new_values_cache->SetValue(kDeviceLoginScreenInputMethods,
-                               std::move(input_methods));
+                               base::Value(std::move(input_methods)));
   }
 
   if (policy.has_saml_login_authentication_type() &&
@@ -406,14 +407,13 @@
 
     const RepeatedField<int>& allowed_connection_types =
         au_settings_proto.allowed_connection_types();
-    std::unique_ptr<base::ListValue> list(new base::ListValue());
-    for (RepeatedField<int>::const_iterator i(allowed_connection_types.begin());
-         i != allowed_connection_types.end(); ++i) {
-      list->AppendInteger(*i);
+    std::vector<base::Value> list;
+    for (int value : allowed_connection_types) {
+      list.push_back(base::Value(value));
     }
-    if (!list->empty()) {
+    if (!list.empty()) {
       new_values_cache->SetValue(kAllowedConnectionTypesForUpdate,
-                                 std::move(list));
+                                 base::Value(std::move(list)));
     }
 
     if (au_settings_proto.has_disallowed_time_intervals()) {
@@ -611,7 +611,7 @@
     // Set empty value if policy is missing, to make sure that webui
     // will receive setting update.
     new_values_cache->SetValue(kDeviceDisplayResolution,
-                               std::make_unique<base::DictionaryValue>());
+                               base::Value(base::Value::Type::DICTIONARY));
   }
 
   if (policy.has_allow_bluetooth() &&
@@ -633,13 +633,16 @@
     auto off_hours_policy = policy::off_hours::ConvertOffHoursProtoToValue(
         policy.device_off_hours());
     if (off_hours_policy)
-      new_values_cache->SetValue(kDeviceOffHours, std::move(off_hours_policy));
+      new_values_cache->SetValue(
+          kDeviceOffHours,
+          base::Value::FromUniquePtrValue(std::move(off_hours_policy)));
   }
 
   if (policy.has_tpm_firmware_update_settings()) {
     new_values_cache->SetValue(kTPMFirmwareUpdateSettings,
-                               tpm_firmware_update::DecodeSettingsProto(
-                                   policy.tpm_firmware_update_settings()));
+                               base::Value::FromUniquePtrValue(
+                                   tpm_firmware_update::DecodeSettingsProto(
+                                       policy.tpm_firmware_update_settings())));
   }
 
   if (policy.has_minimum_required_version()) {
@@ -653,8 +656,8 @@
   if (policy.has_cast_receiver_name()) {
     const em::CastReceiverNameProto& container(policy.cast_receiver_name());
     if (container.has_name()) {
-      new_values_cache->SetValue(
-          kCastReceiverName, std::make_unique<base::Value>(container.name()));
+      new_values_cache->SetValue(kCastReceiverName,
+                                 base::Value(container.name()));
     }
   }
 
@@ -664,7 +667,7 @@
     if (container.has_unaffiliated_arc_allowed()) {
       new_values_cache->SetValue(
           kUnaffiliatedArcAllowed,
-          std::make_unique<base::Value>(container.unaffiliated_arc_allowed()));
+          base::Value(container.unaffiliated_arc_allowed()));
     }
   }
 
@@ -695,17 +698,15 @@
     if (container.has_device_unaffiliated_crostini_allowed()) {
       new_values_cache->SetValue(
           kDeviceUnaffiliatedCrostiniAllowed,
-          std::make_unique<base::Value>(
-              container.device_unaffiliated_crostini_allowed()));
+          base::Value(container.device_unaffiliated_crostini_allowed()));
     }
   }
 
   if (policy.has_plugin_vm_allowed()) {
     const em::PluginVmAllowedProto& container(policy.plugin_vm_allowed());
     if (container.has_plugin_vm_allowed()) {
-      new_values_cache->SetValue(
-          kPluginVmAllowed,
-          std::make_unique<base::Value>(container.plugin_vm_allowed()));
+      new_values_cache->SetValue(kPluginVmAllowed,
+                                 base::Value(container.plugin_vm_allowed()));
     }
   }
 
@@ -714,8 +715,7 @@
         policy.plugin_vm_license_key());
     if (container.has_plugin_vm_license_key()) {
       new_values_cache->SetValue(
-          kPluginVmLicenseKey,
-          std::make_unique<base::Value>(container.plugin_vm_license_key()));
+          kPluginVmLicenseKey, base::Value(container.plugin_vm_license_key()));
     }
   }
 
diff --git a/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc b/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc
index b7c29ce..f5151bb 100644
--- a/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc
+++ b/chrome/browser/chromeos/settings/stub_cros_settings_provider.cc
@@ -84,7 +84,7 @@
                                      const base::Value& value) {
   bool is_value_changed = false;
   if (current_user_is_owner_)
-    is_value_changed = values_.SetValue(path, value.CreateDeepCopy());
+    is_value_changed = values_.SetValue(path, value.Clone());
   else
     LOG(WARNING) << "Changing settings from non-owner, setting=" << path;
 
@@ -97,12 +97,12 @@
   values_.SetBoolean(kAccountsPrefAllowNewUser, true);
   values_.SetBoolean(kAccountsPrefSupervisedUsersEnabled, true);
   values_.SetBoolean(kAccountsPrefShowUserNamesOnSignIn, true);
-  values_.SetValue(kAccountsPrefUsers, base::WrapUnique(new base::ListValue));
+  values_.SetValue(kAccountsPrefUsers, base::Value(base::Value::Type::LIST));
   values_.SetBoolean(kAllowBluetooth, true);
   values_.SetBoolean(kAttestationForContentProtectionEnabled, true);
   values_.SetBoolean(kStatsReportingPref, true);
   values_.SetValue(kAccountsPrefDeviceLocalAccounts,
-                   base::WrapUnique(new base::ListValue));
+                   base::Value(base::Value::Type::LIST));
   // |kDeviceOwner| will be set to the logged-in user by |UserManager|.
 }
 
diff --git a/chrome/browser/chromeos/system/procfs_util_unittest.cc b/chrome/browser/chromeos/system/procfs_util_unittest.cc
index fe8dc5aa..30d5c1b 100644
--- a/chrome/browser/chromeos/system/procfs_util_unittest.cc
+++ b/chrome/browser/chromeos/system/procfs_util_unittest.cc
@@ -64,10 +64,11 @@
                              .utime = 466410,
                              .stime = 80831,
                              .rss = 130441};
-  WriteContentsToFileUnderSubdir(stat_contents, base::IntToString(pid), "stat");
+  WriteContentsToFileUnderSubdir(stat_contents, base::NumberToString(pid),
+                                 "stat");
   EXPECT_EQ(expected,
             GetSingleProcStat(
-                slash_proc_.Append(base::IntToString(pid)).Append("stat"))
+                slash_proc_.Append(base::NumberToString(pid)).Append("stat"))
                 .value());
 }
 
diff --git a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
index 997b871..2dff3f2 100644
--- a/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
+++ b/chrome/browser/chromeos/system_logs/debug_daemon_log_source.cc
@@ -54,7 +54,7 @@
 void ReadUserLogFiles(const std::vector<base::FilePath>& profile_dirs,
                       SystemLogsResponse* response) {
   for (size_t i = 0; i < profile_dirs.size(); ++i) {
-    std::string profile_prefix = "Profile[" + base::UintToString(i) + "] ";
+    std::string profile_prefix = "Profile[" + base::NumberToString(i) + "] ";
     for (const auto& log : kUserLogs) {
       std::string value;
       const bool read_success = base::ReadFileToString(
diff --git a/chrome/browser/client_hints/client_hints.cc b/chrome/browser/client_hints/client_hints.cc
index eb302bc5..e8c72703f6 100644
--- a/chrome/browser/client_hints/client_hints.cc
+++ b/chrome/browser/client_hints/client_hints.cc
@@ -17,14 +17,17 @@
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/client_hints/client_hints.h"
+#include "chrome/common/pref_names.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_utils.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/common/content_features.h"
 #include "content/public/common/origin_util.h"
 #include "net/base/url_util.h"
 #include "net/http/http_request_headers.h"
+#include "net/http/http_util.h"
 #include "net/nqe/effective_connection_type.h"
 #include "net/nqe/network_quality_estimator_params.h"
 #include "net/url_request/url_request.h"
@@ -367,12 +370,20 @@
         blink::kWebEffectiveConnectionTypeMapping[effective_connection_type]);
   }
 
+  if (web_client_hints.IsEnabled(blink::mojom::WebClientHintsType::kLang)) {
+    additional_headers->SetHeader(
+        blink::kClientHintsHeaderMapping[static_cast<int>(
+            blink::mojom::WebClientHintsType::kLang)],
+        blink::SerializeLangClientHint(
+            profile->GetPrefs()->GetString(prefs::kAcceptLanguages)));
+  }
+
   // Static assert that triggers if a new client hint header is added. If a
   // new client hint header is added, the following assertion should be updated.
   // If possible, logic should be added above so that the request headers for
   // the newly added client hint can be added to the request.
   static_assert(
-      blink::mojom::WebClientHintsType::kEct ==
+      blink::mojom::WebClientHintsType::kLang ==
           blink::mojom::WebClientHintsType::kMaxValue,
       "Consider adding client hint request headers from the browser process");
 
diff --git a/chrome/browser/client_hints/client_hints_browsertest.cc b/chrome/browser/client_hints/client_hints_browsertest.cc
index d8164504..76fc819 100644
--- a/chrome/browser/client_hints/client_hints_browsertest.cc
+++ b/chrome/browser/client_hints/client_hints_browsertest.cc
@@ -65,7 +65,7 @@
       return false;
 
     request_count_seen_++;
-    for (size_t i = 0; i < blink::kClientHintsHeaderMappingCount; ++i) {
+    for (size_t i = 0; i < blink::kClientHintsMappingsCount; ++i) {
       if (params->url_request.headers.HasHeader(
               blink::kClientHintsHeaderMapping[i])) {
         client_hints_count_seen_++;
@@ -247,6 +247,8 @@
   void SetUpCommandLine(base::CommandLine* cmd) override {
     cmd->AppendSwitchASCII(network::switches::kForceEffectiveConnectionType,
                            net::kEffectiveConnectionType2G);
+    cmd->AppendSwitchASCII(switches::kEnableBlinkFeatures,
+                           "LangClientHintHeader");
   }
 
   void SetClientHintExpectationsOnMainFrame(bool expect_client_hints) {
@@ -531,7 +533,7 @@
       }
     }
 
-    for (size_t i = 0; i < blink::kClientHintsHeaderMappingCount; ++i) {
+    for (size_t i = 0; i < blink::kClientHintsMappingsCount; ++i) {
       if (base::ContainsKey(request.headers,
                             blink::kClientHintsHeaderMapping[i])) {
         count_client_hints_headers_seen_++;
@@ -541,7 +543,9 @@
 
   void VerifyClientHintsReceived(bool expect_client_hints,
                                  const net::test_server::HttpRequest& request) {
-    for (size_t i = 0; i < blink::kClientHintsHeaderMappingCount; ++i) {
+    for (size_t i = 0; i < blink::kClientHintsMappingsCount; ++i) {
+      SCOPED_TRACE(testing::Message()
+                   << std::string(blink::kClientHintsHeaderMapping[i]));
       // Resource width client hint is only attached on image subresources.
       if (std::string(blink::kClientHintsHeaderMapping[i]) == "width") {
         continue;
@@ -673,7 +677,7 @@
 };
 
 IN_PROC_BROWSER_TEST_F(ClientHintsBrowserTest, CorsChecks) {
-  for (size_t i = 0; i < blink::kClientHintsHeaderMappingCount; ++i) {
+  for (size_t i = 0; i < blink::kClientHintsMappingsCount; ++i) {
     // Do not test for headers that have not been enabled on the blink "stable"
     // yet.
     if (std::string(blink::kClientHintsHeaderMapping[i]) == "rtt" ||
@@ -704,8 +708,8 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  // client_hints_url() sets six client hints.
-  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 6, 1);
+  // client_hints_url() sets seven client hints.
+  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 7, 1);
   // accept_ch_with_lifetime_url() sets client hints persist duration to 3600
   // seconds.
   histogram_tester.ExpectUniqueSample("ClientHints.PersistDuration",
@@ -760,9 +764,9 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  // Six client hints are attached to the image request, and six to the main
+  // seven client hints are attached to the image request, and seven to the main
   // frame request.
-  EXPECT_EQ(12u, count_client_hints_headers_seen());
+  EXPECT_EQ(14u, count_client_hints_headers_seen());
 
   // Navigating to without_accept_ch_without_lifetime_img_foo_com() should not
   // attach client hints to the image subresouce contained in that page since
@@ -775,9 +779,9 @@
 
   // The device-memory and dprheader is attached to the main frame request.
 #if defined(OS_ANDROID)
-  EXPECT_EQ(6u, count_client_hints_headers_seen());
+  EXPECT_EQ(7u, count_client_hints_headers_seen());
 #else
-  EXPECT_EQ(18u, count_client_hints_headers_seen());
+  EXPECT_EQ(21u, count_client_hints_headers_seen());
 #endif
   // Requests to third party servers should not have client hints attached.
   EXPECT_EQ(1u, third_party_request_count_seen());
@@ -801,7 +805,7 @@
   ui_test_utils::NavigateToURL(browser(), gurl);
   histogram_tester.ExpectTotalCount("ClientHints.UpdateEventCount", 0);
 
-  EXPECT_EQ(6u, count_client_hints_headers_seen());
+  EXPECT_EQ(7u, count_client_hints_headers_seen());
 
   // Requests to third party servers should not have client hints attached.
   EXPECT_EQ(1u, third_party_request_count_seen());
@@ -828,7 +832,7 @@
   ui_test_utils::NavigateToURL(browser(), gurl);
   histogram_tester.ExpectTotalCount("ClientHints.UpdateEventCount", 0);
 
-  EXPECT_EQ(6u, count_client_hints_headers_seen());
+  EXPECT_EQ(7u, count_client_hints_headers_seen());
 
   // Requests to third party servers should not have client hints attached.
   EXPECT_EQ(1u, third_party_request_count_seen());
@@ -997,7 +1001,7 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 6, 1);
+  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 7, 1);
   // |gurl| sets client hints persist duration to 3600 seconds.
   histogram_tester.ExpectUniqueSample("ClientHints.PersistDuration",
                                       3600 * 1000, 1);
@@ -1015,9 +1019,9 @@
   ui_test_utils::NavigateToURL(browser(),
                                without_accept_ch_without_lifetime_local_url());
 
-  // Six client hints are attached to the image request, and six to the main
+  // seven client hints are attached to the image request, and seven to the main
   // frame request.
-  EXPECT_EQ(12u, count_client_hints_headers_seen());
+  EXPECT_EQ(14u, count_client_hints_headers_seen());
 }
 
 // Loads a webpage that does not request persisting of client hints.
@@ -1057,7 +1061,7 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 6, 1);
+  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 7, 1);
   // accept_ch_with_lifetime_url() sets client hints persist duration to 3600
   // seconds.
   histogram_tester.ExpectUniqueSample("ClientHints.PersistDuration",
@@ -1075,9 +1079,9 @@
   ui_test_utils::NavigateToURL(browser(),
                                without_accept_ch_without_lifetime_url());
 
-  // Six client hints are attached to the image request, and six to the main
+  // seven client hints are attached to the image request, and seven to the main
   // frame request.
-  EXPECT_EQ(12u, count_client_hints_headers_seen());
+  EXPECT_EQ(14u, count_client_hints_headers_seen());
 }
 
 // The test first fetches a page that sets Accept-CH-Lifetime. Next, it fetches
@@ -1107,7 +1111,7 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 6, 1);
+  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 7, 1);
   // accept_ch_with_lifetime_url() sets client hints persist duration to 3600
   // seconds.
   histogram_tester.ExpectUniqueSample("ClientHints.PersistDuration",
@@ -1124,9 +1128,9 @@
   SetClientHintExpectationsOnSubresources(true);
   ui_test_utils::NavigateToURL(browser(), redirect_url());
 
-  // Six client hints are attached to the image request, and six to the main
+  // seven client hints are attached to the image request, and seven to the main
   // frame request.
-  EXPECT_EQ(12u, count_client_hints_headers_seen());
+  EXPECT_EQ(14u, count_client_hints_headers_seen());
 }
 
 // Ensure that even when cookies are blocked, client hint preferences are
@@ -1178,7 +1182,7 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 6, 1);
+  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 7, 1);
   // |gurl_with| tries to set client hints persist duration to 3600 seconds.
   histogram_tester.ExpectUniqueSample("ClientHints.PersistDuration",
                                       3600 * 1000, 1);
@@ -1201,9 +1205,9 @@
   ui_test_utils::NavigateToURL(browser(),
                                without_accept_ch_without_lifetime_url());
 
-  // Six client hints are attached to the image request, and six to the main
+  // seven client hints are attached to the image request, and seven to the main
   // frame request.
-  EXPECT_EQ(12u, count_client_hints_headers_seen());
+  EXPECT_EQ(14u, count_client_hints_headers_seen());
 
   // Clear settings.
   HostContentSettingsMapFactory::GetForProfile(browser()->profile())
@@ -1275,7 +1279,7 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 6, 1);
+  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 7, 1);
   // accept_ch_with_lifetime_url() tries to set client hints persist duration to
   // 3600 seconds.
   histogram_tester.ExpectUniqueSample("ClientHints.PersistDuration",
@@ -1309,9 +1313,9 @@
   ui_test_utils::NavigateToURL(browser(),
                                without_accept_ch_without_lifetime_url());
 
-  // Six client hints are attached to the image request, and six to the main
+  // seven client hints are attached to the image request, and seven to the main
   // frame request.
-  EXPECT_EQ(12u, count_client_hints_headers_seen());
+  EXPECT_EQ(14u, count_client_hints_headers_seen());
 
   // Clear settings.
   HostContentSettingsMapFactory::GetForProfile(browser()->profile())
@@ -1357,7 +1361,7 @@
   ui_test_utils::NavigateToURL(browser(),
                                accept_ch_without_lifetime_img_localhost());
 
-  EXPECT_EQ(6u, count_client_hints_headers_seen());
+  EXPECT_EQ(7u, count_client_hints_headers_seen());
   EXPECT_EQ(2u, third_party_request_count_seen());
   EXPECT_EQ(0u, third_party_client_hints_count_seen());
   VerifyContentSettingsNotNotified();
@@ -1375,7 +1379,7 @@
           CONTENT_SETTING_BLOCK);
   ui_test_utils::NavigateToURL(browser(),
                                accept_ch_without_lifetime_img_localhost());
-  EXPECT_EQ(6u, count_client_hints_headers_seen());
+  EXPECT_EQ(7u, count_client_hints_headers_seen());
   EXPECT_EQ(3u, third_party_request_count_seen());
   EXPECT_EQ(0u, third_party_client_hints_count_seen());
 
@@ -1411,7 +1415,7 @@
 
   SetClientHintExpectationsOnSubresources(true);
   ui_test_utils::NavigateToURL(browser(), gurl);
-  EXPECT_EQ(6u, count_client_hints_headers_seen());
+  EXPECT_EQ(7u, count_client_hints_headers_seen());
   EXPECT_EQ(1u, third_party_request_count_seen());
   EXPECT_EQ(0u, third_party_client_hints_count_seen());
 
@@ -1444,7 +1448,7 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 6, 1);
+  histogram_tester.ExpectUniqueSample("ClientHints.UpdateSize", 7, 1);
   // accept_ch_with_lifetime_url() sets client hints persist duration to 3600
   // seconds.
   histogram_tester.ExpectUniqueSample("ClientHints.PersistDuration",
@@ -1462,9 +1466,9 @@
   ui_test_utils::NavigateToURL(incognito,
                                without_accept_ch_without_lifetime_url());
 
-  // Six client hints are attached to the image request, and six to the main
+  // Seven client hints are attached to the image request, and seven to the main
   // frame request.
-  EXPECT_EQ(12u, count_client_hints_headers_seen());
+  EXPECT_EQ(14u, count_client_hints_headers_seen());
 
   // Navigate using regular profile. Client hints should not be send.
   SetClientHintExpectationsOnMainFrame(false);
@@ -1472,9 +1476,9 @@
   ui_test_utils::NavigateToURL(browser(),
                                without_accept_ch_without_lifetime_url());
 
-  // Six client hints are attached to the image request, and six to the main
+  // Seven client hints are attached to the image request, and seven to the main
   // frame request.
-  EXPECT_EQ(12u, count_client_hints_headers_seen());
+  EXPECT_EQ(14u, count_client_hints_headers_seen());
 }
 
 class ClientHintsWebHoldbackBrowserTest : public ClientHintsBrowserTest {
@@ -1544,7 +1548,7 @@
   content::FetchHistogramsFromChildProcesses();
   SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-  EXPECT_EQ(12u, count_client_hints_headers_seen());
+  EXPECT_EQ(14u, count_client_hints_headers_seen());
   EXPECT_EQ(0u, third_party_request_count_seen());
   EXPECT_EQ(0u, third_party_client_hints_count_seen());
 }
diff --git a/chrome/browser/client_hints/client_hints_unittest.cc b/chrome/browser/client_hints/client_hints_unittest.cc
index 3b8412e..4012f8d3 100644
--- a/chrome/browser/client_hints/client_hints_unittest.cc
+++ b/chrome/browser/client_hints/client_hints_unittest.cc
@@ -147,7 +147,7 @@
   // test failing to (1/20)^20.
   for (size_t i = 0; i < 20; ++i) {
     int value = client_hints::internal::RoundRtt(
-        base::IntToString(i), base::TimeDelta::FromMilliseconds(1023));
+        base::NumberToString(i), base::TimeDelta::FromMilliseconds(1023));
     // If |value| is different than |initial_value|, it implies that RTT is
     // randomized by host. This verifies the behavior, and test can be ended.
     if (value != initial_value)
@@ -172,7 +172,7 @@
   // test failing to (1/20)^20.
   for (size_t i = 0; i < 20; ++i) {
     int value =
-        client_hints::internal::RoundKbpsToMbps(base::IntToString(i), 1023);
+        client_hints::internal::RoundKbpsToMbps(base::NumberToString(i), 1023);
     // If |value| is different than |initial_value|, it implies that downlink is
     // randomized by host. This verifies the behavior, and test can be ended.
     if (value != initial_value)
diff --git a/chrome/browser/component_updater/cros_component_installer_chromeos.cc b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
index f890a27..2bc5c76 100644
--- a/chrome/browser/component_updater/cros_component_installer_chromeos.cc
+++ b/chrome/browser/component_updater/cros_component_installer_chromeos.cc
@@ -35,7 +35,7 @@
 const ComponentConfig kConfigs[] = {
     {"epson-inkjet-printer-escpr", "3.0",
      "1913a5e0a6cad30b6f03e176177e0d7ed62c5d6700a9c66da556d7c3f5d6a47e"},
-    {"cros-termina", "730.1",
+    {"cros-termina", "740.1",
      "e9d960f84f628e1f42d05de4046bb5b3154b6f1f65c08412c6af57a29aecaffb"},
     {"rtanalytics-light", "9.0",
      "69f09d33c439c2ab55bbbe24b47ab55cb3f6c0bd1f1ef46eefea3216ec925038"},
diff --git a/chrome/browser/conflicts/module_inspector_win.cc b/chrome/browser/conflicts/module_inspector_win.cc
index 36b545d..a3f8fd6 100644
--- a/chrome/browser/conflicts/module_inspector_win.cc
+++ b/chrome/browser/conflicts/module_inspector_win.cc
@@ -7,14 +7,24 @@
 #include <utility>
 
 #include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/task/post_task.h"
 #include "base/task_runner_util.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/after_startup_task_utils.h"
 #include "chrome/browser/conflicts/module_info_util_win.h"
+#include "chrome/services/util_win/public/mojom/constants.mojom.h"
+#include "content/public/common/service_manager_connection.h"
+#include "services/service_manager/public/cpp/connector.h"
 
 namespace {
 
+constexpr base::Feature kWinOOPInspectModuleFeature = {
+    "WinOOPInspectModule", base::FEATURE_DISABLED_BY_DEFAULT};
+
+constexpr int kConnectionErrorRetryCount = 10;
+
 StringMapping GetPathMapping() {
   return GetEnvironmentVariablesMapping({
       L"LOCALAPPDATA", L"ProgramFiles", L"ProgramData", L"USERPROFILE",
@@ -22,6 +32,25 @@
   });
 }
 
+// Returns a bound pointer to the UtilWin service.
+chrome::mojom::UtilWinPtr ConnectToUtilWinService(
+    base::OnceClosure connection_error_handler) {
+  chrome::mojom::UtilWinPtr util_win_ptr;
+
+  content::ServiceManagerConnection::GetForProcess()
+      ->GetConnector()
+      ->BindInterface(chrome::mojom::kUtilWinServiceName, &util_win_ptr);
+
+  util_win_ptr.set_connection_error_handler(
+      std::move(connection_error_handler));
+
+  return util_win_ptr;
+}
+
+void ReportConnectionError(bool value) {
+  base::UmaHistogramBoolean("Windows.InspectModule.ConnectionError", value);
+}
+
 }  // namespace
 
 ModuleInspector::ModuleInspector(
@@ -32,6 +61,7 @@
           {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
            base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
       path_mapping_(GetPathMapping()),
+      connection_error_retry_count_(kConnectionErrorRetryCount),
       weak_ptr_factory_(this) {
   // Use AfterStartupTaskUtils to be notified when startup is finished.
   AfterStartupTaskUtils::PostTask(
@@ -85,6 +115,20 @@
     StartInspectingModule();
 }
 
+void ModuleInspector::OnUtilWinServiceConnectionError() {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  ReportConnectionError(true);
+
+  // Reset the pointer to the service.
+  util_win_ptr_ = nullptr;
+
+  // Restart inspection for the current module, only if the retry limit wasn't
+  // reached.
+  if (connection_error_retry_count_--)
+    StartInspectingModule();
+}
+
 void ModuleInspector::StartInspectingModule() {
   DCHECK(is_after_startup_);
   DCHECK(!queue_.empty());
@@ -102,11 +146,26 @@
   // In practice, this is not an issue because the only caller of
   // IncreaseInspectionPriority() (chrome://conflicts) does not depend on the
   // inspection to finish synchronously and is not blocking anything else.
-  base::PostTaskAndReplyWithResult(
-      task_runner_.get(), FROM_HERE,
-      base::BindOnce(&InspectModule, module_key.module_path),
-      base::BindOnce(&ModuleInspector::OnInspectionFinished,
-                     weak_ptr_factory_.GetWeakPtr(), module_key));
+  if (base::FeatureList::IsEnabled(kWinOOPInspectModuleFeature)) {
+    // Make sure the pointer is bound to the service first.
+    if (!util_win_ptr_) {
+      util_win_ptr_ = ConnectToUtilWinService(
+          base::BindOnce(&ModuleInspector::OnUtilWinServiceConnectionError,
+                         base::Unretained(this)));
+      ReportConnectionError(false);
+    }
+
+    util_win_ptr_->InspectModule(
+        module_key.module_path,
+        base::BindOnce(&ModuleInspector::OnInspectionFinished,
+                       weak_ptr_factory_.GetWeakPtr(), module_key));
+  } else {
+    base::PostTaskAndReplyWithResult(
+        task_runner_.get(), FROM_HERE,
+        base::BindOnce(&InspectModule, module_key.module_path),
+        base::BindOnce(&ModuleInspector::OnInspectionFinished,
+                       weak_ptr_factory_.GetWeakPtr(), module_key));
+  }
 }
 
 void ModuleInspector::OnInspectionFinished(
@@ -125,6 +184,13 @@
 
   on_module_inspected_callback_.Run(module_key, std::move(inspection_result));
 
+  // Free the pointer to the UtilWin service to clean up the utility process
+  // when it is no longer needed. While this code is only ever needed in the
+  // case the WinOOPInspectModule feature is enabled, it's faster to check the
+  // value of the pointer than to check the feature status.
+  if (queue_.empty() && util_win_ptr_)
+    util_win_ptr_ = nullptr;
+
   // Continue the work.
   if (!queue_.empty())
     StartInspectingModule();
diff --git a/chrome/browser/conflicts/module_inspector_win.h b/chrome/browser/conflicts/module_inspector_win.h
index f463197f..8e702fc 100644
--- a/chrome/browser/conflicts/module_inspector_win.h
+++ b/chrome/browser/conflicts/module_inspector_win.h
@@ -13,6 +13,7 @@
 #include "base/sequence_checker.h"
 #include "base/task/task_traits.h"
 #include "chrome/browser/conflicts/module_info_win.h"
+#include "chrome/services/util_win/public/mojom/util_win.mojom.h"
 
 namespace base {
 class SequencedTaskRunner;
@@ -53,6 +54,9 @@
   // queued modules.
   void OnStartupFinished();
 
+  // Handles a connection error to the UtilWin service.
+  void OnUtilWinServiceConnectionError();
+
   // Starts inspecting the module at the front of the queue.
   void StartInspectingModule();
 
@@ -72,6 +76,11 @@
   // inspection tasks in order to not negatively impact startup performance.
   bool is_after_startup_;
 
+  // A pointer to the UtilWin service. Only used if the WinOOPInspectModule
+  // feature is enabled. It is created when inspection is ongoing, and freed
+  // when no longer needed.
+  chrome::mojom::UtilWinPtr util_win_ptr_;
+
   // The task runner where module inspections takes place. It originally starts
   // at BEST_EFFORT priority, but is changed to USER_VISIBLE when
   // IncreaseInspectionPriority() is called.
@@ -82,6 +91,11 @@
   // e.g. c:\windows vs d:\windows
   StringMapping path_mapping_;
 
+  // The number of time this class will try to restart the UtilWin service if a
+  // connection error occurs. This is to prevent the degenerate case where the
+  // service always fails to start and the restart cycle happens infinitely.
+  int connection_error_retry_count_;
+
   SEQUENCE_CHECKER(sequence_checker_);
 
   // Weak pointers are used to safely post the inspection result back to the
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.cc b/chrome/browser/content_settings/tab_specific_content_settings.cc
index e20b7a0..a4228da 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings.cc
@@ -173,14 +173,12 @@
     int render_process_id,
     int render_frame_id,
     const GURL& url,
-    const base::string16& name,
-    const base::string16& display_name,
     bool blocked_by_policy) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   TabSpecificContentSettings* settings = GetForFrame(
       render_process_id, render_frame_id);
   if (settings)
-    settings->OnWebDatabaseAccessed(url, name, display_name, blocked_by_policy);
+    settings->OnWebDatabaseAccessed(url, blocked_by_policy);
 }
 
 // static
@@ -490,16 +488,12 @@
 
 void TabSpecificContentSettings::OnWebDatabaseAccessed(
     const GURL& url,
-    const base::string16& name,
-    const base::string16& display_name,
     bool blocked_by_policy) {
   if (blocked_by_policy) {
-    blocked_local_shared_objects_.databases()->AddDatabase(
-        url, base::UTF16ToUTF8(name), base::UTF16ToUTF8(display_name));
+    blocked_local_shared_objects_.databases()->AddDatabase(url);
     OnContentBlocked(CONTENT_SETTINGS_TYPE_COOKIES);
   } else {
-    allowed_local_shared_objects_.databases()->AddDatabase(
-        url, base::UTF16ToUTF8(name), base::UTF16ToUTF8(display_name));
+    allowed_local_shared_objects_.databases()->AddDatabase(url);
     OnContentAllowed(CONTENT_SETTINGS_TYPE_COOKIES);
   }
 
diff --git a/chrome/browser/content_settings/tab_specific_content_settings.h b/chrome/browser/content_settings/tab_specific_content_settings.h
index cd043b97..f063802 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings.h
+++ b/chrome/browser/content_settings/tab_specific_content_settings.h
@@ -9,6 +9,7 @@
 
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
 
 #include "base/macros.h"
@@ -125,8 +126,6 @@
   static void WebDatabaseAccessed(int render_process_id,
                                   int render_frame_id,
                                   const GURL& url,
-                                  const base::string16& name,
-                                  const base::string16& display_name,
                                   bool blocked_by_policy);
 
   // Called when a specific DOM storage area in the current page was
@@ -331,10 +330,7 @@
                               const std::string& name,
                               const url::Origin& constructor_origin,
                               bool blocked_by_policy);
-  void OnWebDatabaseAccessed(const GURL& url,
-                             const base::string16& name,
-                             const base::string16& display_name,
-                             bool blocked_by_policy);
+  void OnWebDatabaseAccessed(const GURL& url, bool blocked_by_policy);
   void OnGeolocationPermissionSet(const GURL& requesting_frame,
                                   bool allowed);
 #if defined(OS_ANDROID) || defined(OS_CHROMEOS)
diff --git a/chrome/browser/content_settings/tab_specific_content_settings_unittest.cc b/chrome/browser/content_settings/tab_specific_content_settings_unittest.cc
index 552302c..7bb7495 100644
--- a/chrome/browser/content_settings/tab_specific_content_settings_unittest.cc
+++ b/chrome/browser/content_settings/tab_specific_content_settings_unittest.cc
@@ -269,7 +269,5 @@
                                            true,
                                            blocked_by_policy);
   content_settings->OnWebDatabaseAccessed(GURL("http://google.com"),
-                                          base::UTF8ToUTF16("name"),
-                                          base::UTF8ToUTF16("display_name"),
                                           blocked_by_policy);
 }
diff --git a/chrome/browser/data_use_measurement/page_load_capping/page_load_capping_browsertest.cc b/chrome/browser/data_use_measurement/page_load_capping/page_load_capping_browsertest.cc
index f4c46fc..3f561b7 100644
--- a/chrome/browser/data_use_measurement/page_load_capping/page_load_capping_browsertest.cc
+++ b/chrome/browser/data_use_measurement/page_load_capping/page_load_capping_browsertest.cc
@@ -222,7 +222,7 @@
             "var image = document.createElement('img'); "
             "document.body.appendChild(image); image.src = '")
             .append(kImagePrefix)
-            .append(base::IntToString(i))
+            .append(base::NumberToString(i))
             .append(".png';");
     EXPECT_TRUE(content::ExecuteScript(contents(), create_image_script));
   }
@@ -317,7 +317,7 @@
               "var image = document.createElement('img'); "
               "document.body.appendChild(image); image.src = '")
               .append(GetURL(std::string(kImagePrefix)
-                                 .append(base::IntToString(++j))
+                                 .append(base::NumberToString(++j))
                                  .append(".png';"))
                           .spec());
 
diff --git a/chrome/browser/devtools/device/cast_device_provider.cc b/chrome/browser/devtools/device/cast_device_provider.cc
index e40d8bd..7544bbd 100644
--- a/chrome/browser/devtools/device/cast_device_provider.cc
+++ b/chrome/browser/devtools/device/cast_device_provider.cc
@@ -71,7 +71,7 @@
     device_info.model = kUnknownCastDevice;
 
   AndroidDeviceManager::BrowserInfo browser_info;
-  browser_info.socket_name = base::IntToString(kCastInspectPort);
+  browser_info.socket_name = base::NumberToString(kCastInspectPort);
   browser_info.display_name =
       base::SplitString(service_description.service_name, ".",
                         base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)[0];
diff --git a/chrome/browser/devtools/device/port_forwarding_browsertest.cc b/chrome/browser/devtools/device/port_forwarding_browsertest.cc
index c62ddf1..3c48120 100644
--- a/chrome/browser/devtools/device/port_forwarding_browsertest.cc
+++ b/chrome/browser/devtools/device/port_forwarding_browsertest.cc
@@ -41,8 +41,9 @@
   }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII(switches::kRemoteDebuggingPort,
-        base::IntToString(GetRemoteDebuggingPort()));
+    command_line->AppendSwitchASCII(
+        switches::kRemoteDebuggingPort,
+        base::NumberToString(GetRemoteDebuggingPort()));
   }
 
  protected:
diff --git a/chrome/browser/devtools/device/tcp_device_provider.cc b/chrome/browser/devtools/device/tcp_device_provider.cc
index 84a7942..c761a0bb 100644
--- a/chrome/browser/devtools/device/tcp_device_provider.cc
+++ b/chrome/browser/devtools/device/tcp_device_provider.cc
@@ -111,7 +111,7 @@
     if (serial != target.host())
       continue;
     AndroidDeviceManager::BrowserInfo browser_info;
-    browser_info.socket_name = base::UintToString(target.port());
+    browser_info.socket_name = base::NumberToString(target.port());
     browser_info.display_name = kBrowserName;
     browser_info.type = AndroidDeviceManager::BrowserInfo::kTypeChrome;
 
diff --git a/chrome/browser/devtools/devtools_targets_ui.cc b/chrome/browser/devtools/devtools_targets_ui.cc
index f2871e4..2ad2200 100644
--- a/chrome/browser/devtools/devtools_targets_ui.cc
+++ b/chrome/browser/devtools/devtools_targets_ui.cc
@@ -386,7 +386,7 @@
     auto port_status_dict = std::make_unique<base::DictionaryValue>();
     const PortStatusMap& port_status_map = sit->second;
     for (auto it = port_status_map.begin(); it != port_status_map.end(); ++it) {
-      port_status_dict->SetInteger(base::IntToString(it->first), it->second);
+      port_status_dict->SetInteger(base::NumberToString(it->first), it->second);
     }
 
     auto device_status_dict = std::make_unique<base::DictionaryValue>();
diff --git a/chrome/browser/devtools/protocol_string.h b/chrome/browser/devtools/protocol_string.h
index 0e68d7f..b7eea7c 100644
--- a/chrome/browser/devtools/protocol_string.h
+++ b/chrome/browser/devtools/protocol_string.h
@@ -43,7 +43,7 @@
   static String substring(const String& s, unsigned pos, unsigned len) {
     return s.substr(pos, len);
   }
-  static String fromInteger(int number) { return base::IntToString(number); }
+  static String fromInteger(int number) { return base::NumberToString(number); }
   static String fromDouble(double number) {
     String s = base::NumberToString(number);
     if (!s.empty() && s[0] == '.')
diff --git a/chrome/browser/devtools/remote_debugging_server.cc b/chrome/browser/devtools/remote_debugging_server.cc
index 0f48441..e57ed9c 100644
--- a/chrome/browser/devtools/remote_debugging_server.cc
+++ b/chrome/browser/devtools/remote_debugging_server.cc
@@ -66,7 +66,7 @@
     if (last_tethering_port_ == kMaxTetheringPort)
       last_tethering_port_ = kMinTetheringPort;
     uint16_t port = ++last_tethering_port_;
-    *name = base::UintToString(port);
+    *name = base::NumberToString(port);
     return CreateLocalHostServerSocket(port);
   }
 
diff --git a/chrome/browser/diagnostics/recon_diagnostics.cc b/chrome/browser/diagnostics/recon_diagnostics.cc
index af2209d..ba4636b 100644
--- a/chrome/browser/diagnostics/recon_diagnostics.cc
+++ b/chrome/browser/diagnostics/recon_diagnostics.cc
@@ -65,7 +65,7 @@
       RecordFailure(DIAG_RECON_UNABLE_TO_QUERY, "Unable to query free space");
       return true;
     }
-    std::string printable_size = base::Int64ToString(disk_space);
+    std::string printable_size = base::NumberToString(disk_space);
     if (disk_space < 80 * kOneMegabyte) {
       RecordFailure(DIAG_RECON_LOW_DISK_SPACE,
                     "Low disk space: " + printable_size);
@@ -161,7 +161,7 @@
         json.Deserialize(&error_code, &error_message));
     if (base::JSONReader::JSON_NO_ERROR != error_code) {
       if (error_message.empty()) {
-        error_message = "Parse error " + base::IntToString(error_code);
+        error_message = "Parse error " + base::NumberToString(error_code);
       }
       RecordFailure(DIAG_RECON_PARSE_ERROR, error_message);
       return true;
@@ -253,7 +253,7 @@
                         base::UTF16ToUTF8(dir_or_file.LossyDisplayName()));
       return true;
     }
-    std::string printable_size = base::Int64ToString(dir_or_file_size);
+    std::string printable_size = base::NumberToString(dir_or_file_size);
 
     if (path_info_.max_size > 0) {
       if (dir_or_file_size > path_info_.max_size) {
diff --git a/chrome/browser/diagnostics/sqlite_diagnostics.cc b/chrome/browser/diagnostics/sqlite_diagnostics.cc
index deb5737..8925157 100644
--- a/chrome/browser/diagnostics/sqlite_diagnostics.cc
+++ b/chrome/browser/diagnostics/sqlite_diagnostics.cc
@@ -137,7 +137,7 @@
                         "Database locked by another process");
         } else {
           std::string str("Pragma failed. Error: ");
-          str += base::IntToString(error);
+          str += base::NumberToString(error);
           RecordFailure(DIAG_SQLITE_PRAGMA_FAILED, str);
         }
         return false;
@@ -158,7 +158,7 @@
     // All done. Report to the user.
     if (errors != 0) {
       std::string str("Database corruption detected: ");
-      str += base::IntToString(errors) + " errors";
+      str += base::NumberToString(errors) + " errors";
       RecordFailure(DIAG_SQLITE_DB_CORRUPTED, str);
       return true;
     }
diff --git a/chrome/browser/download/download_commands.cc b/chrome/browser/download/download_commands.cc
index 2f89253..bdf05087 100644
--- a/chrome/browser/download/download_commands.cc
+++ b/chrome/browser/download/download_commands.cc
@@ -131,7 +131,7 @@
       learn_more_url, g_browser_process->GetApplicationLocale());
   return net::AppendQueryParameter(
       learn_more_url, "ctx",
-      base::IntToString(static_cast<int>(model_->download()->GetLastReason())));
+      base::NumberToString(model_->download()->GetLastReason()));
 }
 
 bool DownloadCommands::IsCommandEnabled(Command command) const {
diff --git a/chrome/browser/download/download_prefs.cc b/chrome/browser/download/download_prefs.cc
index 84ce429..53d7b76 100644
--- a/chrome/browser/download/download_prefs.cc
+++ b/chrome/browser/download/download_prefs.cc
@@ -134,7 +134,7 @@
     base::FilePath migrated;
     if (!current.empty() &&
         file_manager::util::MigratePathFromOldFormat(
-            profile_, current, &migrated)) {
+            profile_, GetDefaultDownloadDirectory(), current, &migrated)) {
       prefs->SetFilePath(path_pref[i], migrated);
 
       // In M73 migrate /home/chronos/u-<hash>/Downloads to
diff --git a/chrome/browser/engagement/site_engagement_metrics.cc b/chrome/browser/engagement/site_engagement_metrics.cc
index 9e343a1..0de31bd 100644
--- a/chrome/browser/engagement/site_engagement_metrics.cc
+++ b/chrome/browser/engagement/site_engagement_metrics.cc
@@ -103,7 +103,7 @@
 
   for (const auto& b : score_buckets) {
     std::string histogram_name =
-        kEngagementBucketHistogramBase + base::IntToString(b.first);
+        kEngagementBucketHistogramBase + base::NumberToString(b.first);
 
     base::LinearHistogram::FactoryGet(
         histogram_name, 1, 100, 10,
@@ -153,7 +153,7 @@
   for (size_t i = 0; i < base::size(kEngagementBucketHistogramBuckets); ++i) {
     histogram_names.push_back(
         kEngagementBucketHistogramBase +
-        base::IntToString(kEngagementBucketHistogramBuckets[i]));
+        base::NumberToString(kEngagementBucketHistogramBuckets[i]));
   }
 
   return histogram_names;
diff --git a/chrome/browser/extensions/api/automation/automation_apitest.cc b/chrome/browser/extensions/api/automation/automation_apitest.cc
index eaac524..58e27e6 100644
--- a/chrome/browser/extensions/api/automation/automation_apitest.cc
+++ b/chrome/browser/extensions/api/automation/automation_apitest.cc
@@ -50,7 +50,7 @@
 class AutomationApiTest : public ExtensionApiTest {
  protected:
   GURL GetURLForPath(const std::string& host, const std::string& path) {
-    std::string port = base::UintToString(embedded_test_server()->port());
+    std::string port = base::NumberToString(embedded_test_server()->port());
     GURL::Replacements replacements;
     replacements.SetHostStr(host);
     replacements.SetPortStr(port);
diff --git a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
index 125d493..034bd1e 100644
--- a/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
+++ b/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc
@@ -266,8 +266,8 @@
             NULL, /* browser out param*/
             NULL, /* tab_strip out param */
             &contents, NULL /* tab_index out param */)) {
-      return RespondNow(
-          Error(tabs_constants::kTabNotFoundError, base::IntToString(tab_id)));
+      return RespondNow(Error(tabs_constants::kTabNotFoundError,
+                              base::NumberToString(tab_id)));
     }
   } else {
     contents = ChromeExtensionFunctionDetails(this)
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 b4e56fd..ff33336 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
@@ -112,9 +112,9 @@
   bookmark_manager_private::BookmarkNodeDataElement element;
   // Add id and parentId so we can associate the data with existing nodes on the
   // client side.
-  element.id.reset(new std::string(base::Int64ToString(node.id())));
+  element.id.reset(new std::string(base::NumberToString(node.id())));
   element.parent_id.reset(
-      new std::string(base::Int64ToString(node.parent()->id())));
+      new std::string(base::NumberToString(node.parent()->id())));
 
   if (node.is_url())
     element.url.reset(new std::string(node.url().spec()));
@@ -244,7 +244,7 @@
   DispatchEvent(events::BOOKMARK_MANAGER_PRIVATE_ON_META_INFO_CHANGED,
                 bookmark_manager_private::OnMetaInfoChanged::kEventName,
                 bookmark_manager_private::OnMetaInfoChanged::Create(
-                    base::Int64ToString(node->id()), changes));
+                    base::NumberToString(node->id()), changes));
 }
 
 BookmarkManagerPrivateAPI::BookmarkManagerPrivateAPI(
diff --git a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
index 9529f7e..b5099b0 100644
--- a/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
+++ b/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api_unittest.cc
@@ -30,7 +30,7 @@
         model_->AddURL(model_->other_node(), 0, base::ASCIIToUTF16("Goog"),
                        GURL("https://www.google.com"));
     // Store node->id() as we will delete |node| in RunOnDeletedNode().
-    node_id_ = base::Int64ToString(node->id());
+    node_id_ = base::NumberToString(node->id());
   }
 
   std::string node_id() const { return node_id_; }
diff --git a/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.cc b/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.cc
index e9316f3..8b04de24 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.cc
+++ b/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers.cc
@@ -59,12 +59,12 @@
     api::bookmarks::BookmarkTreeNode* out_bookmark_tree_node) {
   DCHECK(out_bookmark_tree_node);
 
-  out_bookmark_tree_node->id = base::Int64ToString(node->id());
+  out_bookmark_tree_node->id = base::NumberToString(node->id());
 
   const BookmarkNode* parent = node->parent();
   if (parent) {
     out_bookmark_tree_node->parent_id.reset(
-        new std::string(base::Int64ToString(parent->id())));
+        new std::string(base::NumberToString(parent->id())));
     out_bookmark_tree_node->index.reset(new int(parent->GetIndexOf(node)));
   }
 
@@ -159,7 +159,7 @@
       value->SetKey(itr->first, base::Value(itr->second));
     }
   }
-  id_to_meta_info_map->Set(base::Int64ToString(node.id()), std::move(value));
+  id_to_meta_info_map->Set(base::NumberToString(node.id()), std::move(value));
 
   if (node.is_folder()) {
     for (int i = 0; i < node.child_count(); ++i) {
diff --git a/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc b/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc
index 82aa4deb..b0e8290 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc
+++ b/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc
@@ -179,7 +179,7 @@
   // Verify top level node.
   const base::Value* value = NULL;
   EXPECT_TRUE(id_to_meta_info_map.Get(
-      base::Int64ToString(model_->other_node()->id()), &value));
+      base::NumberToString(model_->other_node()->id()), &value));
   ASSERT_TRUE(NULL != value);
   const base::DictionaryValue* dictionary_value = NULL;
   EXPECT_TRUE(value->GetAsDictionary(&dictionary_value));
@@ -188,8 +188,8 @@
 
   // Verify bookmark with two meta info key/value pairs.
   value = NULL;
-  EXPECT_TRUE(id_to_meta_info_map.Get(
-      base::Int64ToString(node_->id()), &value));
+  EXPECT_TRUE(
+      id_to_meta_info_map.Get(base::NumberToString(node_->id()), &value));
   ASSERT_TRUE(NULL != value);
   dictionary_value = NULL;
   EXPECT_TRUE(value->GetAsDictionary(&dictionary_value));
@@ -203,8 +203,8 @@
 
   // Verify folder with one meta info key/value pair.
   value = NULL;
-  EXPECT_TRUE(id_to_meta_info_map.Get(
-      base::Int64ToString(folder_->id()), &value));
+  EXPECT_TRUE(
+      id_to_meta_info_map.Get(base::NumberToString(folder_->id()), &value));
   ASSERT_TRUE(NULL != value);
   dictionary_value = NULL;
   EXPECT_TRUE(value->GetAsDictionary(&dictionary_value));
@@ -215,8 +215,8 @@
 
   // Verify bookmark in a subfolder with one meta info key/value pairs.
   value = NULL;
-  EXPECT_TRUE(id_to_meta_info_map.Get(
-      base::Int64ToString(node2_->id()), &value));
+  EXPECT_TRUE(
+      id_to_meta_info_map.Get(base::NumberToString(node2_->id()), &value));
   ASSERT_TRUE(NULL != value);
   dictionary_value = NULL;
   EXPECT_TRUE(value->GetAsDictionary(&dictionary_value));
diff --git a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
index b2f8c26..2d8c91c6 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
+++ b/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc
@@ -281,14 +281,14 @@
                                             int new_index) {
   const BookmarkNode* node = new_parent->GetChild(new_index);
   api::bookmarks::OnMoved::MoveInfo move_info;
-  move_info.parent_id = base::Int64ToString(new_parent->id());
+  move_info.parent_id = base::NumberToString(new_parent->id());
   move_info.index = new_index;
-  move_info.old_parent_id = base::Int64ToString(old_parent->id());
+  move_info.old_parent_id = base::NumberToString(old_parent->id());
   move_info.old_index = old_index;
 
   DispatchEvent(events::BOOKMARKS_ON_MOVED, api::bookmarks::OnMoved::kEventName,
-                api::bookmarks::OnMoved::Create(base::Int64ToString(node->id()),
-                                                move_info));
+                api::bookmarks::OnMoved::Create(
+                    base::NumberToString(node->id()), move_info));
 }
 
 void BookmarkEventRouter::BookmarkNodeAdded(BookmarkModel* model,
@@ -300,7 +300,7 @@
   DispatchEvent(events::BOOKMARKS_ON_CREATED,
                 api::bookmarks::OnCreated::kEventName,
                 api::bookmarks::OnCreated::Create(
-                    base::Int64ToString(node->id()), tree_node));
+                    base::NumberToString(node->id()), tree_node));
 }
 
 void BookmarkEventRouter::BookmarkNodeRemoved(
@@ -310,7 +310,7 @@
     const BookmarkNode* node,
     const std::set<GURL>& removed_urls) {
   api::bookmarks::OnRemoved::RemoveInfo remove_info;
-  remove_info.parent_id = base::Int64ToString(parent->id());
+  remove_info.parent_id = base::NumberToString(parent->id());
   remove_info.index = index;
   bookmark_api_helpers::PopulateBookmarkTreeNode(managed_, node, true, false,
                                                  &remove_info.node);
@@ -318,7 +318,7 @@
   DispatchEvent(events::BOOKMARKS_ON_REMOVED,
                 api::bookmarks::OnRemoved::kEventName,
                 api::bookmarks::OnRemoved::Create(
-                    base::Int64ToString(node->id()), remove_info));
+                    base::NumberToString(node->id()), remove_info));
 }
 
 void BookmarkEventRouter::BookmarkAllUserNodesRemoved(
@@ -345,7 +345,7 @@
   DispatchEvent(events::BOOKMARKS_ON_CHANGED,
                 api::bookmarks::OnChanged::kEventName,
                 api::bookmarks::OnChanged::Create(
-                    base::Int64ToString(node->id()), change_info));
+                    base::NumberToString(node->id()), change_info));
 }
 
 void BookmarkEventRouter::BookmarkNodeFaviconChanged(BookmarkModel* model,
@@ -360,13 +360,13 @@
   int childCount = node->child_count();
   for (int i = 0; i < childCount; ++i) {
     const BookmarkNode* child = node->GetChild(i);
-    reorder_info.child_ids.push_back(base::Int64ToString(child->id()));
+    reorder_info.child_ids.push_back(base::NumberToString(child->id()));
   }
 
   DispatchEvent(events::BOOKMARKS_ON_CHILDREN_REORDERED,
                 api::bookmarks::OnChildrenReordered::kEventName,
                 api::bookmarks::OnChildrenReordered::Create(
-                    base::Int64ToString(node->id()), reorder_info));
+                    base::NumberToString(node->id()), reorder_info));
 }
 
 void BookmarkEventRouter::ExtensiveBookmarkChangesBeginning(
diff --git a/chrome/browser/extensions/api/bookmarks/bookmarks_api_unittest.cc b/chrome/browser/extensions/api/bookmarks/bookmarks_api_unittest.cc
index ba165a8..124c9b08 100644
--- a/chrome/browser/extensions/api/bookmarks/bookmarks_api_unittest.cc
+++ b/chrome/browser/extensions/api/bookmarks/bookmarks_api_unittest.cc
@@ -29,7 +29,7 @@
 
     const bookmarks::BookmarkNode* node = model_->AddFolder(
         model_->other_node(), 0, base::ASCIIToUTF16("Empty folder"));
-    node_id_ = base::Int64ToString(node->id());
+    node_id_ = base::NumberToString(node->id());
   }
 
   std::string node_id() const { return node_id_; }
diff --git a/chrome/browser/extensions/api/cast_streaming/performance_test.cc b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
index 73d4dea..3e98be6 100644
--- a/chrome/browser/extensions/api/cast_streaming/performance_test.cc
+++ b/chrome/browser/extensions/api/cast_streaming/performance_test.cc
@@ -285,7 +285,7 @@
     }
     EXPECT_GT(change_count, 0);
     perf_test::PrintResult(name, modifier, "resolution_changes",
-                           base::IntToString(change_count), "count", true);
+                           base::NumberToString(change_count), "count", true);
   }
 
  private:
diff --git a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
index 909b6d1..f2343d7 100644
--- a/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
+++ b/chrome/browser/extensions/api/certificate_provider/certificate_provider_apitest.cc
@@ -102,7 +102,7 @@
 std::string JsUint8Array(const std::vector<uint8_t>& bytes) {
   std::string res = "new Uint8Array([";
   for (const uint8_t byte : bytes) {
-    res += base::UintToString(byte);
+    res += base::NumberToString(byte);
     res += ", ";
   }
   res += "])";
diff --git a/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.cc b/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.cc
index 6da2221..f111105 100644
--- a/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.cc
+++ b/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.cc
@@ -34,7 +34,7 @@
   if (id.uid == 0)
     return id.string_uid;
   else
-    return base::IntToString(id.uid);
+    return base::NumberToString(id.uid);
 }
 
 MenuItem* GetParent(MenuItem::Id parent_id,
diff --git a/chrome/browser/extensions/api/debugger/debugger_api.cc b/chrome/browser/extensions/api/debugger/debugger_api.cc
index aeb35a53..257b977 100644
--- a/chrome/browser/extensions/api/debugger/debugger_api.cc
+++ b/chrome/browser/extensions/api/debugger/debugger_api.cc
@@ -403,7 +403,7 @@
   if (debuggee_.tab_id)
     error_ = ErrorUtils::FormatErrorMessage(
         format, debugger_api_constants::kTabTargetType,
-        base::IntToString(*debuggee_.tab_id));
+        base::NumberToString(*debuggee_.tab_id));
   else if (debuggee_.extension_id)
     error_ = ErrorUtils::FormatErrorMessage(
         format, debugger_api_constants::kBackgroundPageTargetType,
diff --git a/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc b/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
index adc282b..106d9fb 100644
--- a/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
+++ b/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc
@@ -59,7 +59,7 @@
 
  protected:
   GURL GetURLForPath(const std::string& host, const std::string& path) {
-    std::string port = base::UintToString(embedded_test_server()->port());
+    std::string port = base::NumberToString(embedded_test_server()->port());
     GURL::Replacements replacements;
     replacements.SetHostStr(host);
     replacements.SetPortStr(port);
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
index 8c6ba90e..4c48e78 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_api.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_api.cc
@@ -344,7 +344,7 @@
                                  include_incognito_information(), nullptr,
                                  nullptr, &contents_, nullptr);
     if (!contents_)
-      return RespondNow(Error(kNoTabError, base::IntToString(tab_id_)));
+      return RespondNow(Error(kNoTabError, base::NumberToString(tab_id_)));
   } else {
     // Only browser actions and system indicators have a default tabId.
     ActionInfo::Type action_type = extension_action_->action_type();
diff --git a/chrome/browser/extensions/api/extension_action/extension_action_prefs_unittest.cc b/chrome/browser/extensions/api/extension_action/extension_action_prefs_unittest.cc
index 80a80a9..9eb67c6 100644
--- a/chrome/browser/extensions/api/extension_action/extension_action_prefs_unittest.cc
+++ b/chrome/browser/extensions/api/extension_action/extension_action_prefs_unittest.cc
@@ -26,7 +26,7 @@
 
     // Install 5 extensions.
     for (int i = 0; i < 5; i++) {
-      std::string name = "test" + base::IntToString(i);
+      std::string name = "test" + base::NumberToString(i);
       extensions_.push_back(prefs_.AddExtension(name));
     }
 
diff --git a/chrome/browser/extensions/api/history/history_api.cc b/chrome/browser/extensions/api/history/history_api.cc
index 8850cb6..8bbeeff6 100644
--- a/chrome/browser/extensions/api/history/history_api.cc
+++ b/chrome/browser/extensions/api/history/history_api.cc
@@ -64,7 +64,7 @@
 HistoryItem GetHistoryItem(const history::URLRow& row) {
   HistoryItem history_item;
 
-  history_item.id = base::Int64ToString(row.id());
+  history_item.id = base::NumberToString(row.id());
   history_item.url.reset(new std::string(row.url().spec()));
   history_item.title.reset(new std::string(base::UTF16ToUTF8(row.title())));
   history_item.last_visit_time.reset(
@@ -78,10 +78,10 @@
 VisitItem GetVisitItem(const history::VisitRow& row) {
   VisitItem visit_item;
 
-  visit_item.id = base::Int64ToString(row.url_id);
-  visit_item.visit_id = base::Int64ToString(row.visit_id);
+  visit_item.id = base::NumberToString(row.url_id);
+  visit_item.visit_id = base::NumberToString(row.visit_id);
   visit_item.visit_time.reset(new double(MilliSecondsFromTime(row.visit_time)));
-  visit_item.referring_visit_id = base::Int64ToString(row.referring_visit);
+  visit_item.referring_visit_id = base::NumberToString(row.referring_visit);
 
   api::history::TransitionType transition = api::history::TRANSITION_TYPE_LINK;
   switch (row.transition & ui::PAGE_TRANSITION_CORE_MASK) {
diff --git a/chrome/browser/extensions/api/identity/identity_apitest.cc b/chrome/browser/extensions/api/identity/identity_apitest.cc
index d933c45..fbe27fb 100644
--- a/chrome/browser/extensions/api/identity/identity_apitest.cc
+++ b/chrome/browser/extensions/api/identity/identity_apitest.cc
@@ -33,8 +33,8 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/account_fetcher_service_factory.h"
 #include "chrome/browser/signin/account_reconcilor_factory.h"
-#include "chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/gaia_cookie_manager_service_test_util.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
 #include "chrome/browser/ui/browser.h"
@@ -48,6 +48,7 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/core/browser/account_fetcher_service.h"
 #include "components/signin/core/browser/account_reconcilor.h"
+#include "components/signin/core/browser/list_accounts_test_utils.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_source.h"
@@ -59,6 +60,7 @@
 #include "services/identity/public/cpp/identity_manager.h"
 #include "services/identity/public/cpp/identity_test_utils.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
diff --git a/chrome/browser/extensions/api/processes/processes_api.cc b/chrome/browser/extensions/api/processes/processes_api.cc
index 0b60862..d2f3c29 100644
--- a/chrome/browser/extensions/api/processes/processes_api.cc
+++ b/chrome/browser/extensions/api/processes/processes_api.cc
@@ -290,7 +290,7 @@
 
     // Store each process indexed by the string version of its ChildProcessHost
     // ID.
-    processes_dictionary.Set(base::IntToString(child_process_host_id),
+    processes_dictionary.Set(base::NumberToString(child_process_host_id),
                              process.ToValue());
   }
 
@@ -463,8 +463,8 @@
           tab_id, Profile::FromBrowserContext(browser_context()),
           include_incognito_information(), nullptr, nullptr, &contents,
           &tab_index)) {
-    return RespondNow(Error(tabs_constants::kTabNotFoundError,
-                            base::IntToString(tab_id)));
+    return RespondNow(
+        Error(tabs_constants::kTabNotFoundError, base::NumberToString(tab_id)));
   }
 
   // TODO(https://crbug.com/767563): chrome.processes.getProcessIdForTab API
@@ -489,11 +489,11 @@
   child_process_host_id_ = params->process_id;
   if (child_process_host_id_ < 0) {
     return RespondNow(Error(errors::kInvalidArgument,
-                            base::IntToString(child_process_host_id_)));
+                            base::NumberToString(child_process_host_id_)));
   } else if (child_process_host_id_ == 0) {
     // Cannot kill the browser process.
     return RespondNow(Error(errors::kNotAllowedToTerminate,
-                            base::IntToString(child_process_host_id_)));
+                            base::NumberToString(child_process_host_id_)));
   }
 
   // Check if it's a renderer.
@@ -538,19 +538,19 @@
 ProcessesTerminateFunction::TerminateIfAllowed(base::ProcessHandle handle) {
   if (handle == base::kNullProcessHandle) {
     return Error(errors::kProcessNotFound,
-                 base::IntToString(child_process_host_id_));
+                 base::NumberToString(child_process_host_id_));
   }
 
   if (handle == base::GetCurrentProcessHandle()) {
     // Cannot kill the browser process.
     return Error(errors::kNotAllowedToTerminate,
-                 base::IntToString(child_process_host_id_));
+                 base::NumberToString(child_process_host_id_));
   }
 
   base::Process process = base::Process::Open(base::GetProcId(handle));
   if (!process.IsValid()) {
     return Error(errors::kProcessNotFound,
-                 base::IntToString(child_process_host_id_));
+                 base::NumberToString(child_process_host_id_));
   }
 
   const bool did_terminate =
@@ -675,15 +675,15 @@
     // Store each process indexed by the string version of its
     // ChildProcessHost ID.
     processes.additional_properties.Set(
-        base::IntToString(child_process_host_id),
-        process.ToValue());
+        base::NumberToString(child_process_host_id), process.ToValue());
   }
 
   // Report the invalid host ids sent in the arguments.
   for (const auto& host_id : process_host_ids_) {
-    WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR,
-                   ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
-                                                  base::IntToString(host_id)));
+    WriteToConsole(
+        content::CONSOLE_MESSAGE_LEVEL_ERROR,
+        ErrorUtils::FormatErrorMessage(errors::kProcessNotFound,
+                                       base::NumberToString(host_id)));
   }
 
   // Send the response.
diff --git a/chrome/browser/extensions/api/sessions/session_id.cc b/chrome/browser/extensions/api/sessions/session_id.cc
index 1862a3da..e2ea1bd 100644
--- a/chrome/browser/extensions/api/sessions/session_id.cc
+++ b/chrome/browser/extensions/api/sessions/session_id.cc
@@ -43,9 +43,8 @@
 }
 
 std::string SessionId::ToString() const {
-  return IsForeign() ?
-      (session_tag_ + kIdSeparator + base::IntToString(id_))
-      : base::IntToString(id_);
+  return IsForeign() ? (session_tag_ + kIdSeparator + base::NumberToString(id_))
+                     : base::NumberToString(id_);
 }
 
 }  // namespace extensions
diff --git a/chrome/browser/extensions/api/sessions/sessions_api.cc b/chrome/browser/extensions/api/sessions/sessions_api.cc
index 493e7383..0fc0594 100644
--- a/chrome/browser/extensions/api/sessions/sessions_api.cc
+++ b/chrome/browser/extensions/api/sessions/sessions_api.cc
@@ -146,7 +146,7 @@
     const sessions::TabRestoreService::Tab& tab,
     bool active) {
   return CreateTabModelHelper(tab.navigations[tab.current_navigation_index],
-                              base::IntToString(tab.id.id()),
+                              base::NumberToString(tab.id.id()),
                               tab.tabstrip_index, tab.pinned, active,
                               extension());
 }
@@ -162,7 +162,7 @@
         CreateTabModel(*tab, tab->tabstrip_index == window.selected_tab_index));
 
   return CreateWindowModelHelper(
-      std::move(tabs), base::IntToString(window.id.id()),
+      std::move(tabs), base::NumberToString(window.id.id()),
       api::windows::WINDOW_TYPE_NORMAL, api::windows::WINDOW_STATE_NORMAL);
 }
 
diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc
index 3ce3534b..ad9590a9 100644
--- a/chrome/browser/extensions/api/settings_private/prefs_util.cc
+++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc
@@ -382,8 +382,6 @@
   // Google Assistant.
   (*s_whitelist)[arc::prefs::kVoiceInteractionActivityControlAccepted] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
-  (*s_whitelist)[arc::prefs::kArcVoiceInteractionValuePropAccepted] =
-      settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[arc::prefs::kVoiceInteractionEnabled] =
       settings_api::PrefType::PREF_TYPE_BOOLEAN;
   (*s_whitelist)[arc::prefs::kVoiceInteractionContextEnabled] =
diff --git a/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper.cc b/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper.cc
index 7a76483..4ad8cf0 100644
--- a/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper.cc
+++ b/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper.cc
@@ -60,7 +60,7 @@
   const base::Value *out_value;
 
   do {
-    string_value = base::IntToString(rand_value);
+    string_value = base::NumberToString(rand_value);
     rand_value++;
   } while (mapping.Get(string_value, &out_value));
 
diff --git a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
index fe929f59..2f191e72 100644
--- a/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
+++ b/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc
@@ -518,6 +518,14 @@
     NOTIMPLEMENTED();
     return std::unique_ptr<net::SSLClientSocket>();
   }
+  std::unique_ptr<net::SSLClientSocket> CreateSSLClientSocket(
+      std::unique_ptr<net::StreamSocket>,
+      const net::HostPortPair&,
+      const net::SSLConfig&,
+      const net::SSLClientSocketContext&) override {
+    NOTIMPLEMENTED();
+    return std::unique_ptr<net::SSLClientSocket>();
+  }
   std::unique_ptr<net::ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<net::ClientSocketHandle> transport_socket,
       const std::string& user_agent,
diff --git a/chrome/browser/extensions/api/storage/settings_sync_unittest.cc b/chrome/browser/extensions/api/storage/settings_sync_unittest.cc
index 7dcda11..d46189d 100644
--- a/chrome/browser/extensions/api/storage/settings_sync_unittest.cc
+++ b/chrome/browser/extensions/api/storage/settings_sync_unittest.cc
@@ -1427,7 +1427,7 @@
   // permission can't apply to sync.
   std::unique_ptr<base::Value> kilobyte = util::CreateKilobyte();
   for (int i = 0; i < 100; ++i) {
-    sync_storage->Set(ValueStore::DEFAULTS, base::IntToString(i), *kilobyte);
+    sync_storage->Set(ValueStore::DEFAULTS, base::NumberToString(i), *kilobyte);
   }
 
   EXPECT_FALSE(sync_storage->Set(ValueStore::DEFAULTS, "WillError", *kilobyte)
@@ -1439,7 +1439,8 @@
   // Local storage should never run out.
   std::unique_ptr<base::Value> megabyte = util::CreateMegabyte();
   for (int i = 0; i < 7; ++i) {
-    local_storage->Set(ValueStore::DEFAULTS, base::IntToString(i), *megabyte);
+    local_storage->Set(ValueStore::DEFAULTS, base::NumberToString(i),
+                       *megabyte);
   }
 
   EXPECT_TRUE(local_storage->Set(ValueStore::DEFAULTS, "WontError", *megabyte)
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index 0fbe38a..edfe45a3 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -187,7 +187,7 @@
 
   if (error_message) {
     *error_message = ErrorUtils::FormatErrorMessage(
-        tabs_constants::kTabNotFoundError, base::IntToString(tab_id));
+        tabs_constants::kTabNotFoundError, base::NumberToString(tab_id));
   }
 
   return false;
@@ -1242,7 +1242,7 @@
   // Make sure the index is in range.
   if (!tabstrip->ContainsIndex(index)) {
     *error = ErrorUtils::FormatErrorMessage(
-        tabs_constants::kTabIndexNotFoundError, base::IntToString(index));
+        tabs_constants::kTabIndexNotFoundError, base::NumberToString(index));
     return false;
   }
 
@@ -1341,7 +1341,8 @@
       !chrome::SetTabAudioMuted(contents, *params->update_properties.muted,
                                 TabMutedReason::EXTENSION, extension()->id())) {
     return RespondNow(Error(ErrorUtils::FormatErrorMessage(
-        tabs_constants::kCannotUpdateMuteCaptured, base::IntToString(tab_id))));
+        tabs_constants::kCannotUpdateMuteCaptured,
+        base::NumberToString(tab_id))));
   }
 
   if (params->update_properties.opener_tab_id.get()) {
@@ -1353,7 +1354,7 @@
                                       include_incognito_information(), nullptr,
                                       nullptr, &opener_contents, nullptr)) {
       return RespondNow(Error(ErrorUtils::FormatErrorMessage(
-          tabs_constants::kTabNotFoundError, base::IntToString(opener_id))));
+          tabs_constants::kTabNotFoundError, base::NumberToString(opener_id))));
     }
 
     if (tab_strip->GetIndexOfWebContents(opener_contents) ==
@@ -1541,7 +1542,7 @@
           source_tab_strip->DetachWebContentsAt(tab_index);
       if (!web_contents) {
         *error = ErrorUtils::FormatErrorMessage(
-            tabs_constants::kTabNotFoundError, base::IntToString(tab_id));
+            tabs_constants::kTabNotFoundError, base::NumberToString(tab_id));
         return false;
       }
 
@@ -1945,9 +1946,9 @@
   content::RenderFrameHost* rfh =
       ExtensionApiFrameIdMap::GetRenderFrameHostById(contents, frame_id);
   if (!rfh) {
-    *error = ErrorUtils::FormatErrorMessage(tabs_constants::kFrameNotFoundError,
-                                            base::IntToString(frame_id),
-                                            base::IntToString(execute_tab_id_));
+    *error = ErrorUtils::FormatErrorMessage(
+        tabs_constants::kFrameNotFoundError, base::NumberToString(frame_id),
+        base::NumberToString(execute_tab_id_));
     return false;
   }
 
@@ -2173,11 +2174,11 @@
   }
 
   // Return appropriate error message otherwise.
-  return RespondNow(Error(
-      params->tab_id
-          ? ErrorUtils::FormatErrorMessage(tabs_constants::kCannotDiscardTab,
-                                           base::IntToString(*params->tab_id))
-          : tabs_constants::kCannotFindTabToDiscard));
+  return RespondNow(Error(params->tab_id
+                              ? ErrorUtils::FormatErrorMessage(
+                                    tabs_constants::kCannotDiscardTab,
+                                    base::NumberToString(*params->tab_id))
+                              : tabs_constants::kCannotFindTabToDiscard));
 }
 
 TabsDiscardFunction::TabsDiscardFunction() {}
diff --git a/chrome/browser/extensions/api/tabs/windows_util.cc b/chrome/browser/extensions/api/tabs/windows_util.cc
index 17b7831..684183d 100644
--- a/chrome/browser/extensions/api/tabs/windows_util.cc
+++ b/chrome/browser/extensions/api/tabs/windows_util.cc
@@ -60,7 +60,7 @@
     if (!(*browser)) {
       *error = extensions::ErrorUtils::FormatErrorMessage(
           extensions::tabs_constants::kWindowNotFoundError,
-          base::IntToString(window_id));
+          base::NumberToString(window_id));
       return false;
     }
   }
diff --git a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
index bcc9c3de..04714c4 100644
--- a/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
+++ b/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc
@@ -361,7 +361,7 @@
   const extensions::Extension* extension =
       service->GetExtensionById(last_loaded_extension_id(), false);
   GURL url = extension->GetResourceURL(
-      "a.html?" + base::IntToString(embedded_test_server()->port()));
+      "a.html?" + base::NumberToString(embedded_test_server()->port()));
 
   // Register an observer for the navigation in the subframe, so the test
   // can wait until it is fully complete. Otherwise the context menu
diff --git a/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc b/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc
index a8204b98..b2ab47ed3 100644
--- a/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc
+++ b/chrome/browser/extensions/api/webrtc_audio_private/webrtc_audio_private_api.cc
@@ -169,7 +169,7 @@
                                     nullptr, &contents, nullptr)) {
     error_ = extensions::ErrorUtils::FormatErrorMessage(
         extensions::tabs_constants::kTabNotFoundError,
-        base::IntToString(tab_id));
+        base::NumberToString(tab_id));
     return nullptr;
   }
   GURL expected_origin = contents->GetLastCommittedURL().GetOrigin();
diff --git a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
index 159e5fc..53de044f 100644
--- a/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
+++ b/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc
@@ -78,7 +78,7 @@
 namespace {
 std::string HashIdWithOrigin(const std::string& security_origin,
                              const std::string& log_id) {
-  return base::UintToString(base::Hash(security_origin + log_id));
+  return base::NumberToString(base::Hash(security_origin + log_id));
 }
 }  // namespace
 
@@ -154,7 +154,7 @@
                                     nullptr, &contents, nullptr)) {
     SetError(extensions::ErrorUtils::FormatErrorMessage(
         extensions::tabs_constants::kTabNotFoundError,
-        base::IntToString(tab_id)));
+        base::NumberToString(tab_id)));
     return nullptr;
   }
   if (!contents) {
diff --git a/chrome/browser/extensions/content_capabilities_browsertest.cc b/chrome/browser/extensions/content_capabilities_browsertest.cc
index 89d1b0e..d635c6c8 100644
--- a/chrome/browser/extensions/content_capabilities_browsertest.cc
+++ b/chrome/browser/extensions/content_capabilities_browsertest.cc
@@ -98,7 +98,7 @@
   }
 
   GURL GetTestURLFor(const std::string& host) {
-    std::string port = base::UintToString(embedded_test_server()->port());
+    std::string port = base::NumberToString(embedded_test_server()->port());
     GURL::Replacements replacements;
     replacements.SetHostStr(host);
     replacements.SetPortStr(port);
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index 1e8145ac..4475ef6 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -251,8 +251,8 @@
 
  private:
   friend class ::ExtensionServiceTest;
+  friend class BookmarkAppInstallFinalizerTest;
   friend class ExtensionUpdaterTest;
-  friend class BookmarkAppInstallerTest;
 
   CrxInstaller(base::WeakPtr<ExtensionService> service_weak,
                std::unique_ptr<ExtensionInstallPrompt> client,
diff --git a/chrome/browser/extensions/execute_script_apitest.cc b/chrome/browser/extensions/execute_script_apitest.cc
index 19e98f7..d42bee15 100644
--- a/chrome/browser/extensions/execute_script_apitest.cc
+++ b/chrome/browser/extensions/execute_script_apitest.cc
@@ -146,9 +146,9 @@
   bool RunSubtest(const std::string& test_host) {
     return RunExtensionSubtest(
         "executescript/destructive",
-        "test.html?" + test_host +
-        "#bucketcount=" + base::IntToString(kDestructiveScriptTestBucketCount) +
-        "&bucketindex=" + base::IntToString(GetParam()));
+        "test.html?" + test_host + "#bucketcount=" +
+            base::NumberToString(kDestructiveScriptTestBucketCount) +
+            "&bucketindex=" + base::NumberToString(GetParam()));
   }
 };
 
diff --git a/chrome/browser/extensions/extension_action_manager_unittest.cc b/chrome/browser/extensions/extension_action_manager_unittest.cc
index 9b5efa0..f106f04 100644
--- a/chrome/browser/extensions/extension_action_manager_unittest.cc
+++ b/chrome/browser/extensions/extension_action_manager_unittest.cc
@@ -75,7 +75,7 @@
     std::unique_ptr<base::DictionaryValue> extension_icons,
     std::unique_ptr<base::DictionaryValue> action,
     const char* action_type) {
-  std::string id = base::IntToString(curr_id_++);
+  std::string id = base::NumberToString(curr_id_++);
   scoped_refptr<const Extension> extension =
       ExtensionBuilder()
           .SetManifest(
diff --git a/chrome/browser/extensions/extension_action_storage_manager.cc b/chrome/browser/extensions/extension_action_storage_manager.cc
index 2842143..bc5a9ac 100644
--- a/chrome/browser/extensions/extension_action_storage_manager.cc
+++ b/chrome/browser/extensions/extension_action_storage_manager.cc
@@ -184,7 +184,7 @@
     std::vector<gfx::ImageSkiaRep> image_reps = icon.image_reps();
     for (const gfx::ImageSkiaRep& rep : image_reps) {
       int size = static_cast<int>(rep.scale() * icon.width());
-      std::string size_string = base::IntToString(size);
+      std::string size_string = base::NumberToString(size);
       icon_value->SetString(size_string, BitmapToString(rep.GetBitmap()));
     }
     dict->Set(kIconStorageKey, std::move(icon_value));
diff --git a/chrome/browser/extensions/extension_bindings_apitest.cc b/chrome/browser/extensions/extension_bindings_apitest.cc
index aa3d6a5..9582e83 100644
--- a/chrome/browser/extensions/extension_bindings_apitest.cc
+++ b/chrome/browser/extensions/extension_bindings_apitest.cc
@@ -17,6 +17,8 @@
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/render_view_host.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/browser_test_utils.h"
 #include "content/public/test/test_navigation_observer.h"
 #include "extensions/browser/event_router.h"
@@ -28,28 +30,78 @@
 #include "extensions/test/test_extension_dir.h"
 #include "net/dns/mock_host_resolver.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "third_party/blink/public/platform/web_mouse_event.h"
 
 namespace extensions {
 namespace {
 
-enum BindingsType { NATIVE_BINDINGS, JAVASCRIPT_BINDINGS };
+void MouseDownInWebContents(content::WebContents* web_contents) {
+  blink::WebMouseEvent mouse_event(
+      blink::WebInputEvent::kMouseDown, blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::GetStaticTimeStampForTests());
+  mouse_event.button = blink::WebMouseEvent::Button::kLeft;
+  mouse_event.SetPositionInWidget(10, 10);
+  mouse_event.click_count = 1;
+  web_contents->GetRenderViewHost()->GetWidget()->ForwardMouseEvent(
+      mouse_event);
+}
+
+void MouseUpInWebContents(content::WebContents* web_contents) {
+  blink::WebMouseEvent mouse_event(
+      blink::WebInputEvent::kMouseUp, blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::GetStaticTimeStampForTests());
+  mouse_event.button = blink::WebMouseEvent::Button::kLeft;
+  mouse_event.SetPositionInWidget(10, 10);
+  mouse_event.click_count = 1;
+  web_contents->GetRenderViewHost()->GetWidget()->ForwardMouseEvent(
+      mouse_event);
+}
+
+enum BindingsFeatureType {
+  kNativeBindings,
+  kJavaScriptBindings,
+};
+
+enum UserActivationType {
+  kUserActivationV1,
+  kUserActivationV2,
+};
+
+struct BindingsApiTestConfig {
+  BindingsFeatureType bindings_type;
+  base::Optional<UserActivationType> user_activation_type;
+};
 
 class ExtensionBindingsApiTest
     : public ExtensionApiTest,
-      public ::testing::WithParamInterface<BindingsType> {
+      public ::testing::WithParamInterface<BindingsApiTestConfig> {
  public:
   ExtensionBindingsApiTest() {}
   ~ExtensionBindingsApiTest() override {}
 
   void SetUp() override {
-    if (GetParam() == NATIVE_BINDINGS) {
-      scoped_feature_list_.InitAndEnableFeature(
-          extensions_features::kNativeCrxBindings);
+    BindingsApiTestConfig config = GetParam();
+    std::vector<base::Feature> enable_features;
+    std::vector<base::Feature> disable_features;
+
+    if (config.bindings_type == kNativeBindings) {
+      enable_features.push_back(extensions_features::kNativeCrxBindings);
     } else {
-      DCHECK_EQ(JAVASCRIPT_BINDINGS, GetParam());
-      scoped_feature_list_.InitAndDisableFeature(
-          extensions_features::kNativeCrxBindings);
+      DCHECK_EQ(kJavaScriptBindings, config.bindings_type);
+      disable_features.push_back(extensions_features::kNativeCrxBindings);
     }
+
+    if (config.user_activation_type.has_value()) {
+      if (*config.user_activation_type == kUserActivationV2) {
+        enable_features.push_back(features::kUserActivationV2);
+      } else {
+        DCHECK_EQ(kUserActivationV1, *config.user_activation_type);
+        disable_features.push_back(features::kUserActivationV2);
+      }
+    }
+
+    scoped_feature_list_.InitWithFeatures(enable_features, disable_features);
+
     ExtensionApiTest::SetUp();
   }
 
@@ -525,8 +577,11 @@
   EXPECT_EQ(SessionTabHelper::IdForTab(new_tab).id(), result_tab_id);
 }
 
+// Alias purely for test instantiation purposes.
+using ExtensionBindingsUserGestureTest = ExtensionBindingsApiTest;
+
 // Verifies that user gestures are carried through extension messages.
-IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
+IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
                        UserGestureFromExtensionMessageTest) {
   TestExtensionDir test_dir;
   test_dir.WriteManifest(
@@ -591,7 +646,7 @@
 
 // Verifies that user gestures from API calls are active when the callback is
 // triggered.
-IN_PROC_BROWSER_TEST_P(ExtensionBindingsApiTest,
+IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
                        UserGestureInExtensionAPICallback) {
   TestExtensionDir test_dir;
   test_dir.WriteManifest(
@@ -638,6 +693,121 @@
   }
 }
 
+// Tests that a web page can consume a user gesture after an extension sends and
+// receives a reply during the same user gesture.
+// Regression test for https://crbug.com/921141.
+IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
+                       WebUserGestureAfterMessagingCallback) {
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(
+      R"({
+           "name": "User Gesture Messaging Test",
+           "version": "0.1",
+           "manifest_version": 2,
+           "content_scripts": [{
+             "matches": ["*://*/*"],
+             "js": ["content_script.js"],
+             "run_at": "document_start"
+           }],
+           "background": {
+             "scripts": ["background.js"]
+           }
+         })");
+  test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"),
+                     R"(window.addEventListener('mousedown', () => {
+           chrome.runtime.sendMessage('hello', () => {
+             let message = chrome.test.isProcessingUserGesture() ?
+                 'got reply' : 'no user gesture';
+             chrome.test.sendMessage(message);
+           });
+         });)");
+  test_dir.WriteFile(
+      FILE_PATH_LITERAL("background.js"),
+      R"(chrome.runtime.onMessage.addListener((message, sender, respond) => {
+           respond('reply');
+         });
+         chrome.test.sendMessage('ready');)");
+
+  const Extension* extension = nullptr;
+  {
+    ExtensionTestMessageListener listener("ready", false);
+    extension = LoadExtension(test_dir.UnpackedPath());
+    ASSERT_TRUE(extension);
+    EXPECT_TRUE(listener.WaitUntilSatisfied());
+  }
+
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(
+                     "/extensions/api_test/bindings/user_gesture_test.html"));
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+
+  {
+    ExtensionTestMessageListener listener("got reply", false);
+    listener.set_failure_message("no user gesture");
+    MouseDownInWebContents(web_contents);
+    EXPECT_TRUE(listener.WaitUntilSatisfied());
+  }
+
+  MouseUpInWebContents(web_contents);
+
+  EXPECT_EQ("success",
+            content::EvalJs(web_contents, "window.getEnteredFullscreen",
+                            content::EXECUTE_SCRIPT_NO_USER_GESTURE));
+}
+
+// Tests that a web page can consume a user gesture after an extension calls a
+// method and receives the response in the callback.
+// Regression test for https://crbug.com/921141.
+IN_PROC_BROWSER_TEST_P(ExtensionBindingsUserGestureTest,
+                       WebUserGestureAfterApiCallback) {
+  TestExtensionDir test_dir;
+  test_dir.WriteManifest(
+      R"({
+           "name": "User Gesture Messaging Test",
+           "version": "0.1",
+           "manifest_version": 2,
+           "content_scripts": [{
+             "matches": ["*://*/*"],
+             "js": ["content_script.js"],
+             "run_at": "document_start"
+           }],
+           "permissions": ["storage"]
+         })");
+  test_dir.WriteFile(FILE_PATH_LITERAL("content_script.js"),
+                     R"(window.addEventListener('mousedown', () => {
+           chrome.storage.local.get('foo', () => {
+             let message = chrome.test.isProcessingUserGesture() ?
+                 'got reply' : 'no user gesture';
+             chrome.test.sendMessage(message);
+           });
+         });)");
+
+  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
+  ASSERT_TRUE(extension);
+
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(
+                     "/extensions/api_test/bindings/user_gesture_test.html"));
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ASSERT_TRUE(web_contents);
+
+  {
+    ExtensionTestMessageListener listener("got reply", false);
+    listener.set_failure_message("no user gesture");
+    MouseDownInWebContents(web_contents);
+    EXPECT_TRUE(listener.WaitUntilSatisfied());
+  }
+
+  MouseUpInWebContents(web_contents);
+
+  EXPECT_EQ("success",
+            content::EvalJs(web_contents, "window.getEnteredFullscreen",
+                            content::EXECUTE_SCRIPT_NO_USER_GESTURE));
+}
+
 // Tests that bindings are properly instantiated for a window navigated to an
 // extension URL after being opened with an undefined URL.
 // Regression test for https://crbug.com/925118.
@@ -713,19 +883,29 @@
 // Run core bindings API tests with both native and JS-based bindings. This
 // ensures we have some minimum level of coverage while in the experimental
 // phase, when native bindings may be enabled on trunk but not at 100% stable.
-INSTANTIATE_TEST_SUITE_P(Native,
-                         ExtensionBindingsApiTest,
-                         ::testing::Values(NATIVE_BINDINGS));
-INSTANTIATE_TEST_SUITE_P(JavaScript,
-                         ExtensionBindingsApiTest,
-                         ::testing::Values(JAVASCRIPT_BINDINGS));
+constexpr BindingsApiTestConfig kBindingsConfigs[] = {
+    {kNativeBindings, base::nullopt},
+    {kJavaScriptBindings, base::nullopt},
+};
 
-INSTANTIATE_TEST_SUITE_P(Native,
+// Run user-gesture related tests with combinations of native and JS-based
+// bindings as well as UAv1 and UAv2.
+constexpr BindingsApiTestConfig kBindingsAndUserGestureConfigs[] = {
+    {kNativeBindings, kUserActivationV1},
+    {kNativeBindings, kUserActivationV2},
+    {kJavaScriptBindings, kUserActivationV1},
+    {kJavaScriptBindings, kUserActivationV2},
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         ExtensionBindingsApiTest,
+                         ::testing::ValuesIn(kBindingsConfigs));
+INSTANTIATE_TEST_SUITE_P(,
                          FramesExtensionBindingsApiTest,
-                         ::testing::Values(NATIVE_BINDINGS));
-INSTANTIATE_TEST_SUITE_P(JavaScript,
-                         FramesExtensionBindingsApiTest,
-                         ::testing::Values(JAVASCRIPT_BINDINGS));
+                         ::testing::ValuesIn(kBindingsConfigs));
+INSTANTIATE_TEST_SUITE_P(,
+                         ExtensionBindingsUserGestureTest,
+                         ::testing::ValuesIn(kBindingsAndUserGestureConfigs));
 
 }  // namespace
 }  // namespace extensions
diff --git a/chrome/browser/extensions/extension_install_prompt.cc b/chrome/browser/extensions/extension_install_prompt.cc
index 465f5a4..c445a39 100644
--- a/chrome/browser/extensions/extension_install_prompt.cc
+++ b/chrome/browser/extensions/extension_install_prompt.cc
@@ -394,7 +394,7 @@
 base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
   CHECK(AllowWebstoreData(type_));
   return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT,
-                                    base::IntToString16(rating_count_));
+                                    base::NumberToString16(rating_count_));
 }
 
 base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
diff --git a/chrome/browser/extensions/extension_message_bubble_controller.cc b/chrome/browser/extensions/extension_message_bubble_controller.cc
index 4034bbb..b321c50 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller.cc
@@ -152,8 +152,8 @@
     int old_size = extension_list.size();
     extension_list.erase(extension_list.begin() + kMaxExtensionsToShow,
                          extension_list.end());
-    extension_list.push_back(delegate_->GetOverflowText(base::IntToString16(
-        old_size - kMaxExtensionsToShow)));
+    extension_list.push_back(delegate_->GetOverflowText(
+        base::NumberToString16(old_size - kMaxExtensionsToShow)));
   }
   const base::char16 bullet_point = 0x2022;
   base::string16 prefix = bullet_point + base::ASCIIToUTF16(" ");
diff --git a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
index 7deff738..be310c0 100644
--- a/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
+++ b/chrome/browser/extensions/extension_message_bubble_controller_unittest.cc
@@ -1188,7 +1188,7 @@
 void SetInstallTime(const std::string& extension_id,
                     const base::Time& time,
                     ExtensionPrefs* prefs) {
-  std::string time_str = base::Int64ToString(time.ToInternalValue());
+  std::string time_str = base::NumberToString(time.ToInternalValue());
   prefs->UpdateExtensionPref(extension_id, "install_time",
                              std::make_unique<base::Value>(time_str));
 }
diff --git a/chrome/browser/extensions/extension_messages_apitest.cc b/chrome/browser/extensions/extension_messages_apitest.cc
index a2d7726..043747c4 100644
--- a/chrome/browser/extensions/extension_messages_apitest.cc
+++ b/chrome/browser/extensions/extension_messages_apitest.cc
@@ -371,7 +371,7 @@
   }
 
   GURL GetURLForPath(const std::string& host, const std::string& path) {
-    std::string port = base::UintToString(embedded_test_server()->port());
+    std::string port = base::NumberToString(embedded_test_server()->port());
     GURL::Replacements replacements;
     replacements.SetHostStr(host);
     replacements.SetPortStr(port);
@@ -669,13 +669,13 @@
   ui_test_utils::NavigateToURL(browser(), google_com_url());
   // The extension requests the TLS channel ID, but it doesn't get it for a
   // site that can't connect to it, regardless of whether the page asks for it.
-  EXPECT_EQ(base::IntToString(NAMESPACE_NOT_DEFINED),
+  EXPECT_EQ(base::NumberToString(NAMESPACE_NOT_DEFINED),
             GetTlsChannelIdFromPortConnect(chromium_connectable.get(), false));
-  EXPECT_EQ(base::IntToString(NAMESPACE_NOT_DEFINED),
+  EXPECT_EQ(base::NumberToString(NAMESPACE_NOT_DEFINED),
             GetTlsChannelIdFromSendMessage(chromium_connectable.get(), true));
-  EXPECT_EQ(base::IntToString(NAMESPACE_NOT_DEFINED),
+  EXPECT_EQ(base::NumberToString(NAMESPACE_NOT_DEFINED),
             GetTlsChannelIdFromPortConnect(chromium_connectable.get(), false));
-  EXPECT_EQ(base::IntToString(NAMESPACE_NOT_DEFINED),
+  EXPECT_EQ(base::NumberToString(NAMESPACE_NOT_DEFINED),
             GetTlsChannelIdFromSendMessage(chromium_connectable.get(), true));
 }
 
diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc
index ba6b127..4b7ef8df 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/extension_prefs_unittest.cc
@@ -384,7 +384,7 @@
 
     // Install some extensions.
     for (int i = 0; i < 5; i++) {
-      std::string name = "test" + base::IntToString(i);
+      std::string name = "test" + base::NumberToString(i);
       extensions_.push_back(prefs_.AddExtension(name));
     }
     EXPECT_EQ(NULL,
@@ -447,10 +447,11 @@
   void SetIdleInfo(const std::string& id, int num) {
     base::DictionaryValue manifest;
     manifest.SetString(manifest_keys::kName, "test");
-    manifest.SetString(manifest_keys::kVersion, "1." + base::IntToString(num));
+    manifest.SetString(manifest_keys::kVersion,
+                       "1." + base::NumberToString(num));
     manifest.SetInteger(manifest_keys::kManifestVersion, 2);
     base::FilePath path =
-        prefs_.extensions_dir().AppendASCII(base::IntToString(num));
+        prefs_.extensions_dir().AppendASCII(base::NumberToString(num));
     std::string errors;
     scoped_refptr<Extension> extension = Extension::Create(
         path, Manifest::INTERNAL, manifest, Extension::NO_FLAGS, id, &errors);
@@ -471,8 +472,8 @@
     ASSERT_TRUE(info);
     std::string version;
     ASSERT_TRUE(info->extension_manifest->GetString("version", &version));
-    ASSERT_EQ("1." + base::IntToString(num), version);
-    ASSERT_EQ(base::IntToString(num),
+    ASSERT_EQ("1." + base::NumberToString(num), version);
+    ASSERT_EQ(base::NumberToString(num),
               info->extension_path.BaseName().MaybeAsASCII());
   }
 
diff --git a/chrome/browser/extensions/extension_service_test_base.cc b/chrome/browser/extensions/extension_service_test_base.cc
index fe669f3..31a5fbd 100644
--- a/chrome/browser/extensions/extension_service_test_base.cc
+++ b/chrome/browser/extensions/extension_service_test_base.cc
@@ -242,9 +242,9 @@
     const std::string& extension_id,
     const std::string& pref_path,
     int expected_val) {
-  std::string msg = base::StringPrintf("while checking: %s %s == %s",
-                                       extension_id.c_str(), pref_path.c_str(),
-                                       base::IntToString(expected_val).c_str());
+  std::string msg = base::StringPrintf(
+      "while checking: %s %s == %s", extension_id.c_str(), pref_path.c_str(),
+      base::NumberToString(expected_val).c_str());
 
   PrefService* prefs = profile()->GetPrefs();
   const base::DictionaryValue* dict =
diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc
index ec17434..30fd86c1 100644
--- a/chrome/browser/extensions/extension_service_unittest.cc
+++ b/chrome/browser/extensions/extension_service_unittest.cc
@@ -696,7 +696,7 @@
     msg += " ";
     msg += pref_path;
     msg += " = ";
-    msg += base::IntToString(value);
+    msg += base::NumberToString(value);
 
     SetPref(extension_id, pref_path, std::make_unique<base::Value>(value), msg);
   }
diff --git a/chrome/browser/extensions/extension_storage_monitor.cc b/chrome/browser/extensions/extension_storage_monitor.cc
index 61ab8bb..50f7a416 100644
--- a/chrome/browser/extensions/extension_storage_monitor.cc
+++ b/chrome/browser/extensions/extension_storage_monitor.cc
@@ -427,7 +427,7 @@
       l10n_util::GetStringFUTF16(
           IDS_EXTENSION_STORAGE_MONITOR_TEXT,
           base::UTF8ToUTF16(extension->name()),
-          base::Int64ToString16(current_usage / kMBytes)),
+          base::NumberToString16(current_usage / kMBytes)),
       notification_image, base::string16() /* display source */, GURL(),
       message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
                                  kSystemNotifierId),
@@ -574,7 +574,7 @@
   extension_prefs_->UpdateExtensionPref(
       extension_id, kPrefNextStorageThreshold,
       next_threshold > 0
-          ? std::make_unique<base::Value>(base::Int64ToString(next_threshold))
+          ? std::make_unique<base::Value>(base::NumberToString(next_threshold))
           : nullptr);
 }
 
diff --git a/chrome/browser/extensions/extension_storage_monitor_browsertest.cc b/chrome/browser/extensions/extension_storage_monitor_browsertest.cc
index 29596dd..2f7c189 100644
--- a/chrome/browser/extensions/extension_storage_monitor_browsertest.cc
+++ b/chrome/browser/extensions/extension_storage_monitor_browsertest.cc
@@ -254,7 +254,7 @@
     ASSERT_TRUE(launched_listener.WaitUntilSatisfied());
 
     // Instruct the app to write |num_bytes| of data.
-    launched_listener.Reply(base::IntToString(num_bytes));
+    launched_listener.Reply(base::NumberToString(num_bytes));
     ASSERT_TRUE(write_complete_listener.WaitUntilSatisfied());
   }
 
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index 306050ea..b08a21c 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -81,7 +81,7 @@
 
   if (error_message)
     *error_message = ErrorUtils::FormatErrorMessage(
-        tabs_constants::kWindowNotFoundError, base::IntToString(window_id));
+        tabs_constants::kWindowNotFoundError, base::NumberToString(window_id));
 
   return nullptr;
 }
@@ -172,7 +172,7 @@
             &opener_browser, nullptr, &opener, nullptr)) {
       if (error) {
         *error = ErrorUtils::FormatErrorMessage(
-            tabs_constants::kTabNotFoundError, base::IntToString(opener_id));
+            tabs_constants::kTabNotFoundError, base::NumberToString(opener_id));
       }
       return nullptr;
     }
diff --git a/chrome/browser/extensions/install_signer.cc b/chrome/browser/extensions/install_signer.cc
index 57125cb6..b4f6108e 100644
--- a/chrome/browser/extensions/install_signer.cc
+++ b/chrome/browser/extensions/install_signer.cc
@@ -176,7 +176,7 @@
   value->SetString(kSaltKey, salt_base64);
   value->SetString(kSignatureKey, signature_base64);
   value->SetString(kTimestampKey,
-                   base::Int64ToString(timestamp.ToInternalValue()));
+                   base::NumberToString(timestamp.ToInternalValue()));
 }
 
 // static
diff --git a/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc b/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
index 602b808..86d1373 100644
--- a/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
+++ b/chrome/browser/extensions/system_display/display_info_provider_chromeos_unittest.cc
@@ -489,7 +489,7 @@
 
   ASSERT_EQ(1u, result.size());
 
-  EXPECT_EQ(base::Int64ToString(display_id), result[0].id);
+  EXPECT_EQ(base::NumberToString(display_id), result[0].id);
   EXPECT_EQ("0,0 600x500", SystemInfoDisplayBoundsToString(result[0].bounds));
   EXPECT_EQ(270, result[0].rotation);
 
@@ -501,7 +501,7 @@
 
   ASSERT_EQ(1u, result.size());
 
-  EXPECT_EQ(base::Int64ToString(display_id), result[0].id);
+  EXPECT_EQ(base::NumberToString(display_id), result[0].id);
   EXPECT_EQ("0,0 500x600", SystemInfoDisplayBoundsToString(result[0].bounds));
   EXPECT_EQ(180, result[0].rotation);
 
@@ -513,7 +513,7 @@
 
   ASSERT_EQ(1u, result.size());
 
-  EXPECT_EQ(base::Int64ToString(display_id), result[0].id);
+  EXPECT_EQ(base::NumberToString(display_id), result[0].id);
   EXPECT_EQ("0,0 500x600", SystemInfoDisplayBoundsToString(result[0].bounds));
   EXPECT_EQ(0, result[0].rotation);
 }
@@ -573,7 +573,7 @@
 
   ASSERT_EQ(2u, result.size());
 
-  EXPECT_EQ(base::Int64ToString(display_id), result[1].id);
+  EXPECT_EQ(base::NumberToString(display_id), result[1].id);
   EXPECT_EQ("304,0 310x450", SystemInfoDisplayBoundsToString(result[1].bounds));
   EXPECT_EQ("20,30,50,60", SystemInfoDisplayInsetsToString(result[1].overscan));
 
@@ -591,7 +591,7 @@
 
   ASSERT_EQ(2u, result.size());
 
-  EXPECT_EQ(base::Int64ToString(display_id), result[0].id);
+  EXPECT_EQ(base::NumberToString(display_id), result[0].id);
   EXPECT_EQ("0,0 260x320", SystemInfoDisplayBoundsToString(result[0].bounds));
   EXPECT_EQ("10,20,30,40", SystemInfoDisplayInsetsToString(result[0].overscan));
 }
@@ -626,8 +626,8 @@
   result = GetAllDisplaysInfo();
 
   ASSERT_EQ(1u, result.size());
-  EXPECT_EQ(base::Int64ToString(display_id_primary), result[0].id);
-  EXPECT_EQ(base::Int64ToString(display_id_primary),
+  EXPECT_EQ(base::NumberToString(display_id_primary), result[0].id);
+  EXPECT_EQ(base::NumberToString(display_id_primary),
             result[0].mirroring_source_id);
 
   GetDisplayManager()->SetMirrorMode(display::MirrorMode::kOff, base::nullopt);
@@ -636,9 +636,9 @@
   result = GetAllDisplaysInfo();
 
   ASSERT_EQ(2u, result.size());
-  EXPECT_EQ(base::Int64ToString(display_id_primary), result[0].id);
+  EXPECT_EQ(base::NumberToString(display_id_primary), result[0].id);
   EXPECT_TRUE(result[0].mirroring_source_id.empty());
-  EXPECT_EQ(base::Int64ToString(display_id_secondary), result[1].id);
+  EXPECT_EQ(base::NumberToString(display_id_secondary), result[1].id);
   EXPECT_TRUE(result[1].mirroring_source_id.empty());
 }
 
@@ -814,7 +814,7 @@
   // called first.
   info.is_unified = std::make_unique<bool>(true);
   EXPECT_FALSE(CallSetDisplayUnitInfo(
-      base::Int64ToString(
+      base::NumberToString(
           display::Screen::GetScreen()->GetPrimaryDisplay().id()),
       info));
   base::RunLoop().RunUntilIdle();
@@ -831,7 +831,7 @@
   // enabled.
   info.is_unified = std::make_unique<bool>(false);
   EXPECT_TRUE(CallSetDisplayUnitInfo(
-      base::Int64ToString(
+      base::NumberToString(
           display::Screen::GetScreen()->GetPrimaryDisplay().id()),
       info));
   EXPECT_FALSE(GetDisplayManager()->IsInUnifiedMode());
@@ -840,7 +840,7 @@
   // EnableUnifiedDesktop.
   info.is_unified = std::make_unique<bool>(true);
   EXPECT_TRUE(CallSetDisplayUnitInfo(
-      base::Int64ToString(
+      base::NumberToString(
           display::Screen::GetScreen()->GetPrimaryDisplay().id()),
       info));
   EXPECT_TRUE(GetDisplayManager()->IsInUnifiedMode());
@@ -906,7 +906,7 @@
   info.bounds_origin_y.reset(new int(50));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("-520,50 520x400", secondary.bounds().ToString());
 }
@@ -920,7 +920,7 @@
   info.bounds_origin_y.reset(new int(100));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,100 520x400", secondary.bounds().ToString());
 }
@@ -934,7 +934,7 @@
   info.bounds_origin_y.reset(new int(-400));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1100,-400 520x400", secondary.bounds().ToString());
 }
@@ -948,7 +948,7 @@
   info.bounds_origin_y.reset(new int(600));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("-350,600 520x400", secondary.bounds().ToString());
 }
@@ -962,7 +962,7 @@
   info.bounds_origin_y.reset(new int(100));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,100 520x400", secondary.bounds().ToString());
 }
@@ -976,7 +976,7 @@
   info.bounds_origin_y.reset(new int(100));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("-520,100 520x400", secondary.bounds().ToString());
 }
@@ -990,7 +990,7 @@
   info.bounds_origin_y.reset(new int(-301));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("-360,-400 520x400", secondary.bounds().ToString());
 }
@@ -1005,7 +1005,7 @@
   info.bounds_origin_y.reset(new int(700));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("-650,600 1000x100", secondary.bounds().ToString());
 }
@@ -1019,7 +1019,7 @@
   info.bounds_origin_y.reset(new int(-150));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("850,-100 1000x100", secondary.bounds().ToString());
 }
@@ -1033,7 +1033,7 @@
   info.bounds_origin_y.reset(new int(-650));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("-100,-650 100x1000", secondary.bounds().ToString());
 }
@@ -1048,7 +1048,7 @@
   info.bounds_origin_y.reset(new int(450));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,450 100x1000", secondary.bounds().ToString());
 }
@@ -1062,7 +1062,7 @@
   info.bounds_origin_y.reset(new int(-100));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("600,-100 500x500", secondary.bounds().ToString());
 }
@@ -1076,7 +1076,7 @@
   info.bounds_origin_y.reset(new int(-100));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("450,-500 300x500", secondary.bounds().ToString());
 }
@@ -1090,7 +1090,7 @@
   info.bounds_origin_y.reset(new int(-100));
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString());
 }
@@ -1104,7 +1104,7 @@
   info.bounds_origin_y.reset(new int(-0x200001));
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString());
 }
@@ -1118,7 +1118,7 @@
   info.bounds_origin_y.reset(new int(10));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,10 300x500", secondary.bounds().ToString());
 }
@@ -1132,7 +1132,7 @@
   info.is_primary.reset(new bool(true));
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString());
   // The operation failed because the primary property would be set before
@@ -1151,10 +1151,10 @@
   api::system_display::DisplayProperties info;
   info.bounds_origin_x.reset(new int(300));
   info.mirroring_source_id.reset(
-      new std::string(base::Int64ToString(primary.id())));
+      new std::string(base::NumberToString(primary.id())));
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 }
 
 TEST_F(DisplayInfoProviderChromeosTest, SetRotation) {
@@ -1165,14 +1165,14 @@
   info.rotation.reset(new int(90));
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 500x300", secondary.bounds().ToString());
   EXPECT_EQ(display::Display::ROTATE_90, secondary.rotation());
 
   info.rotation.reset(new int(270));
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 500x300", secondary.bounds().ToString());
   EXPECT_EQ(display::Display::ROTATE_270, secondary.rotation());
@@ -1181,7 +1181,7 @@
   // Switch primary display.
   info.is_primary.reset(new bool(true));
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("0,0 300x500", secondary.bounds().ToString());
   EXPECT_EQ(display::Display::ROTATE_180, secondary.rotation());
@@ -1190,7 +1190,7 @@
 
   info.rotation.reset(new int(0));
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("0,0 300x500", secondary.bounds().ToString());
   EXPECT_EQ(display::Display::ROTATE_0, secondary.rotation());
@@ -1207,7 +1207,7 @@
   info.rotation.reset(new int(90));
 
   EXPECT_TRUE(CallSetDisplayUnitInfo(
-      base::Int64ToString(display::Display::InternalDisplayId()), info));
+      base::NumberToString(display::Display::InternalDisplayId()), info));
   EXPECT_FALSE(screen_orientation_controller->rotation_locked());
 
   // Entering tablet mode enables accelerometer screen rotations.
@@ -1244,7 +1244,7 @@
   info.rotation.reset(new int(90));
 
   EXPECT_TRUE(CallSetDisplayUnitInfo(
-      base::Int64ToString(display::Display::InternalDisplayId()), info));
+      base::NumberToString(display::Display::InternalDisplayId()), info));
   EXPECT_TRUE(
       ash::Shell::Get()->screen_orientation_controller()->rotation_locked());
   EXPECT_TRUE(ash::Shell::Get()
@@ -1260,7 +1260,7 @@
   info.rotation.reset(new int(91));
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 }
 
 TEST_F(DisplayInfoProviderChromeosTest, SetNegativeOverscan) {
@@ -1272,7 +1272,7 @@
   info.overscan->left = -10;
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString());
 
@@ -1280,7 +1280,7 @@
   info.overscan->right = -200;
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString());
 
@@ -1288,7 +1288,7 @@
   info.overscan->top = -300;
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString());
 
@@ -1296,7 +1296,7 @@
   info.overscan->top = -1000;
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString());
 
@@ -1304,7 +1304,7 @@
   info.overscan->top = 0;
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 300x500", secondary.bounds().ToString());
 }
@@ -1322,7 +1322,7 @@
   info.overscan->bottom = 20;
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 }
 
 TEST_F(DisplayInfoProviderChromeosTest, SetOverscanLargerThanVerticalBounds) {
@@ -1338,7 +1338,7 @@
   info.overscan->bottom = 251;
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 }
 
 TEST_F(DisplayInfoProviderChromeosTest, SetOverscan) {
@@ -1353,7 +1353,7 @@
   info.overscan->bottom = 51;
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(secondary.id()), info));
+      CallSetDisplayUnitInfo(base::NumberToString(secondary.id()), info));
 
   EXPECT_EQ("1200,0 150x250", secondary.bounds().ToString());
   const gfx::Insets overscan =
@@ -1380,7 +1380,7 @@
   info.overscan->bottom = 20;
 
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(internal_display_id), info));
+      CallSetDisplayUnitInfo(base::NumberToString(internal_display_id), info));
 }
 
 TEST_F(DisplayInfoProviderChromeosTest, DisplayMode) {
@@ -1425,7 +1425,7 @@
   info.display_mode =
       api::system_display::DisplayMode::FromValue(*other_mode->ToValue());
 
-  EXPECT_TRUE(CallSetDisplayUnitInfo(base::Int64ToString(id), info));
+  EXPECT_TRUE(CallSetDisplayUnitInfo(base::NumberToString(id), info));
 
   // Verify that other_mode now matches the active mode.
   EXPECT_TRUE(GetDisplayManager()->GetActiveModeForDisplayId(id, &active_mode));
@@ -1445,9 +1445,9 @@
   DisplayUnitInfoList displays = GetAllDisplaysInfo();
 
   for (const auto& display : displays) {
-    if (display.id == base::Int64ToString(display_id_list[0]))
+    if (display.id == base::NumberToString(display_id_list[0]))
       EXPECT_EQ(display.display_zoom_factor, zoom_factor_1);
-    if (display.id == base::Int64ToString(display_id_list[1]))
+    if (display.id == base::NumberToString(display_id_list[1]))
       EXPECT_EQ(display.display_zoom_factor, zoom_factor_2);
   }
 }
@@ -1474,7 +1474,7 @@
   info.display_zoom_factor = std::make_unique<double>(zoom_factor_1);
 
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info));
+      CallSetDisplayUnitInfo(base::NumberToString(display_id_list[0]), info));
 
   EXPECT_EQ(GetDisplayZoom(display_id_list[0]), final_zoom_factor_1);
   // Display 2 has not been updated yet, so it will still have the old zoom
@@ -1483,7 +1483,7 @@
 
   info.display_zoom_factor = std::make_unique<double>(zoom_factor_2);
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[1]), info));
+      CallSetDisplayUnitInfo(base::NumberToString(display_id_list[1]), info));
 
   // Both displays should now have the correct zoom factor set.
   EXPECT_EQ(GetDisplayZoom(display_id_list[0]), final_zoom_factor_1);
@@ -1494,14 +1494,14 @@
   float invalid_zoom_factor_1 = 0.285f;
   info.display_zoom_factor = std::make_unique<double>(invalid_zoom_factor_1);
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info));
+      CallSetDisplayUnitInfo(base::NumberToString(display_id_list[0]), info));
 
   // This zoom factor when applied to the display with width 1200, will result
   // in an effective width greater less than 640, which is out of range.
   float invalid_zoom_factor_2 = 1.88f;
   info.display_zoom_factor = std::make_unique<double>(invalid_zoom_factor_2);
   EXPECT_FALSE(
-      CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info));
+      CallSetDisplayUnitInfo(base::NumberToString(display_id_list[0]), info));
 
   // Initialize displays that have bounds outside the valid width range of 640px
   // to 4096px.
@@ -1515,7 +1515,7 @@
   float valid_zoom_factor_1 = 0.8f;
   info.display_zoom_factor = std::make_unique<double>(valid_zoom_factor_1);
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info));
+      CallSetDisplayUnitInfo(base::NumberToString(display_id_list[0]), info));
 
   // Results in a logical width of 4200px. This is above the 4096px threshold
   // but is valid because the initial width was 4500px, so logical width of up
@@ -1523,17 +1523,17 @@
   float valid_zoom_factor_2 = 1.07f;
   info.display_zoom_factor = std::make_unique<double>(valid_zoom_factor_2);
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[1]), info));
+      CallSetDisplayUnitInfo(base::NumberToString(display_id_list[1]), info));
 
   float valid_zoom_factor_3 = 0.5f;
   info.display_zoom_factor = std::make_unique<double>(valid_zoom_factor_3);
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[0]), info));
+      CallSetDisplayUnitInfo(base::NumberToString(display_id_list[0]), info));
 
   float valid_zoom_factor_4 = 2.f;
   info.display_zoom_factor = std::make_unique<double>(valid_zoom_factor_4);
   EXPECT_TRUE(
-      CallSetDisplayUnitInfo(base::Int64ToString(display_id_list[1]), info));
+      CallSetDisplayUnitInfo(base::NumberToString(display_id_list[1]), info));
 }
 
 class DisplayInfoProviderChromeosTouchviewTest
@@ -1643,7 +1643,7 @@
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
     info.mirroring_source_id.reset(
-        new std::string(base::Int64ToString(id_list[0])));
+        new std::string(base::NumberToString(id_list[0])));
     info.mirroring_destination_ids.reset(new std::vector<std::string>());
     EXPECT_FALSE(SetMirrorMode(info));
   }
@@ -1653,10 +1653,10 @@
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
     info.mirroring_source_id.reset(
-        new std::string(base::Int64ToString(id_list[0])));
+        new std::string(base::NumberToString(id_list[0])));
     info.mirroring_destination_ids.reset(new std::vector<std::string>());
     info.mirroring_destination_ids->emplace_back(
-        base::Int64ToString(display::kInvalidDisplayId));
+        base::NumberToString(display::kInvalidDisplayId));
     EXPECT_FALSE(SetMirrorMode(info));
   }
 
@@ -1665,10 +1665,10 @@
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
     info.mirroring_source_id.reset(
-        new std::string(base::Int64ToString(id_list[0])));
+        new std::string(base::NumberToString(id_list[0])));
     info.mirroring_destination_ids.reset(new std::vector<std::string>());
     info.mirroring_destination_ids->emplace_back(
-        base::Int64ToString(id_list[0]));
+        base::NumberToString(id_list[0]));
     EXPECT_FALSE(SetMirrorMode(info));
   }
 
@@ -1678,10 +1678,10 @@
     api::system_display::MirrorModeInfo info;
     info.mode = api::system_display::MIRROR_MODE_MIXED;
     info.mirroring_source_id.reset(
-        new std::string(base::Int64ToString(id_list[0])));
+        new std::string(base::NumberToString(id_list[0])));
     info.mirroring_destination_ids.reset(new std::vector<std::string>());
     info.mirroring_destination_ids->emplace_back(
-        base::Int64ToString(id_list[1]));
+        base::NumberToString(id_list[1]));
     EXPECT_TRUE(SetMirrorMode(info));
     EXPECT_TRUE(display_manager()->IsInSoftwareMirrorMode());
     EXPECT_EQ(id_list[0], display_manager()->mirroring_source_id());
diff --git a/chrome/browser/extensions/system_display/display_info_provider_win.cc b/chrome/browser/extensions/system_display/display_info_provider_win.cc
index 9f195b91..065fef7 100644
--- a/chrome/browser/extensions/system_display/display_info_provider_win.cc
+++ b/chrome/browser/extensions/system_display/display_info_provider_win.cc
@@ -43,7 +43,7 @@
     return FALSE;
 
   unit.id =
-      base::Int64ToString(base::Hash(base::WideToUTF8(monitor_info.szDevice)));
+      base::NumberToString(base::Hash(base::WideToUTF8(monitor_info.szDevice)));
   unit.name = base::WideToUTF8(device.DeviceString);
   all_displays->push_back(std::move(unit));
 
diff --git a/chrome/browser/extensions/updater/extension_updater_unittest.cc b/chrome/browser/extensions/updater/extension_updater_unittest.cc
index 2d8d76f..011f106 100644
--- a/chrome/browser/extensions/updater/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/updater/extension_updater_unittest.cc
@@ -2029,14 +2029,14 @@
     if (rollcall_ping_days != 0) {
       ASSERT_TRUE(base::ContainsKey(url1_params, "r"));
       ASSERT_EQ(1u, url1_params["r"].size());
-      EXPECT_EQ(base::IntToString(rollcall_ping_days),
+      EXPECT_EQ(base::NumberToString(rollcall_ping_days),
                 *url1_params["r"].begin());
       did_rollcall = true;
     }
     if (active_bit && active_ping_days != 0 && did_rollcall) {
       ASSERT_TRUE(base::ContainsKey(url1_params, "a"));
       ASSERT_EQ(1u, url1_params["a"].size());
-      EXPECT_EQ(base::IntToString(active_ping_days),
+      EXPECT_EQ(base::NumberToString(active_ping_days),
                 *url1_params["a"].begin());
     }
 
diff --git a/chrome/browser/feature_engagement/bookmark/bookmark_tracker_unittest.cc b/chrome/browser/feature_engagement/bookmark/bookmark_tracker_unittest.cc
index e49ef4d..a35725ed 100644
--- a/chrome/browser/feature_engagement/bookmark/bookmark_tracker_unittest.cc
+++ b/chrome/browser/feature_engagement/bookmark/bookmark_tracker_unittest.cc
@@ -149,7 +149,7 @@
         "name:bookmark_clicked;comparator:any;window:3650;storage:3650";
     bookmark_params["session_rate"] = "<=3";
     bookmark_params["availability"] = "any";
-    bookmark_params["x_date_released_in_seconds"] = base::Int64ToString(
+    bookmark_params["x_date_released_in_seconds"] = base::NumberToString(
         first_run::GetFirstRunSentinelCreationTime().ToDoubleT());
 
     SetFeatureParams(kIPHBookmarkFeature, bookmark_params);
diff --git a/chrome/browser/feature_engagement/feature_tracker_unittest.cc b/chrome/browser/feature_engagement/feature_tracker_unittest.cc
index b5538960..72ac55b 100644
--- a/chrome/browser/feature_engagement/feature_tracker_unittest.cc
+++ b/chrome/browser/feature_engagement/feature_tracker_unittest.cc
@@ -265,7 +265,7 @@
 TEST_F(FeatureTrackerParamsTest, TestIsNewUser_DefaultTime) {
   // Setting the experiment timestamp equal to the first run sentinel timestamp.
   std::map<std::string, std::string> new_tab_params;
-  new_tab_params["x_date_released_in_seconds"] = base::Int64ToString(
+  new_tab_params["x_date_released_in_seconds"] = base::NumberToString(
       first_run::GetFirstRunSentinelCreationTime().ToDoubleT());
   SetFeatureParams(kIPHNewTabFeature, new_tab_params);
 
@@ -282,7 +282,7 @@
   // Setting the experiment timestamp equal to one second older than what is
   // considered a new user.
   std::map<std::string, std::string> new_tab_params;
-  new_tab_params["x_date_released_in_seconds"] = base::Int64ToString(
+  new_tab_params["x_date_released_in_seconds"] = base::NumberToString(
       first_run::GetFirstRunSentinelCreationTime().ToDoubleT() +
       base::TimeDelta::FromHours(24).InSeconds() + 1);
   SetFeatureParams(kIPHNewTabFeature, new_tab_params);
@@ -303,7 +303,7 @@
 
   // Setting the experiment timestamp equal to the limit of what is considered a
   // new user.
-  new_tab_params["x_date_released_in_seconds"] = base::Int64ToString(
+  new_tab_params["x_date_released_in_seconds"] = base::NumberToString(
       first_run::GetFirstRunSentinelCreationTime().ToDoubleT());
   SetFeatureParams(kIPHNewTabFeature, new_tab_params);
 
@@ -323,7 +323,7 @@
 
   // Setting the experiment timestamp equal to one second older than what is
   // considered a new user.
-  new_tab_params["x_date_released_in_seconds"] = base::Int64ToString(
+  new_tab_params["x_date_released_in_seconds"] = base::NumberToString(
       first_run::GetFirstRunSentinelCreationTime().ToDoubleT() +
       base::TimeDelta::FromHours(28).InSeconds() + 1);
   SetFeatureParams(kIPHNewTabFeature, new_tab_params);
diff --git a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_unittest.cc b/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_unittest.cc
index 7b4f4cb..056f3b62 100644
--- a/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_unittest.cc
+++ b/chrome/browser/feature_engagement/incognito_window/incognito_window_tracker_unittest.cc
@@ -152,8 +152,9 @@
         "name:incognito_window_clicked;comparator:any;window:3650;storage:3650";
     incognito_window_params["session_rate"] = "<=3";
     incognito_window_params["availability"] = "any";
-    incognito_window_params["x_date_released_in_seconds"] = base::Int64ToString(
-        first_run::GetFirstRunSentinelCreationTime().ToDoubleT());
+    incognito_window_params["x_date_released_in_seconds"] =
+        base::NumberToString(
+            first_run::GetFirstRunSentinelCreationTime().ToDoubleT());
     SetFeatureParams(kIPHIncognitoWindowFeature, incognito_window_params);
 
     // Start the DesktopSessionDurationTracker to track active session time.
diff --git a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc
index 22d2eaf..b0e75048 100644
--- a/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc
+++ b/chrome/browser/feature_engagement/new_tab/new_tab_tracker_unittest.cc
@@ -159,7 +159,7 @@
         "name:new_tab_clicked;comparator:any;window:3650;storage:3650";
     new_tab_params["session_rate"] = "<=3";
     new_tab_params["availability"] = "any";
-    new_tab_params["x_date_released_in_seconds"] = base::Int64ToString(
+    new_tab_params["x_date_released_in_seconds"] = base::NumberToString(
         first_run::GetFirstRunSentinelCreationTime().ToDoubleT());
 
     SetFeatureParams(kIPHNewTabFeature, new_tab_params);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 6dd51bf..ca74b55 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -174,16 +174,6 @@
     "expiry_milestone": -1
   },
   {
-    "name": "ash-enable-notification-expansion-animation",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
-    "name": "ash-enable-notification-scroll-bar",
-    // "owners": [ "your-team" ],
-    "expiry_milestone": 76
-  },
-  {
     "name": "ash-enable-overview-rounded-corners",
     "owners": [ "sammiequon" ],
     "expiry_milestone": 78
@@ -2307,6 +2297,11 @@
     "expiry_milestone": 76
   },
   {
+    "name": "fill-on-account-select-http",
+    "owners": [ "jdoerrie" ],
+    "expiry_milestone": 76
+  },
+  {
     "name": "force-color-profile",
     // "owners": [ "your-team" ],
     "expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 69d8618..bb495f09 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -1070,6 +1070,12 @@
     "Filling of passwords when an account is explicitly selected by the user "
     "rather than autofilling credentials on page load.";
 
+const char kFillOnAccountSelectHttpName[] =
+    "Fill passwords on account selection on HTTP origins";
+const char kFillOnAccountSelectHttpDescription[] =
+    "Filling of passwords when an account is explicitly selected by the user "
+    "rather than autofilling credentials on page load on HTTP origins.";
+
 const char kForceTextDirectionName[] = "Force text direction";
 const char kForceTextDirectionDescription[] =
     "Explicitly force the per-character directionality of UI text to "
@@ -1248,10 +1254,6 @@
     "Have the Media Router connect to Cast devices on all IP addresses, not "
     "just RFC1918/RFC4193 private addresses.";
 
-const char kMemoryCoordinatorName[] = "Memory coordinator";
-const char kMemoryCoordinatorDescription[] =
-    "Enable memory coordinator instead of memory pressure listeners.";
-
 const char kMessageCenterNewStyleNotificationName[] = "New style notification";
 const char kMessageCenterNewStyleNotificationDescription[] =
     "Enables the experiment style of material-design notification";
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 5302c37..baaac56 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -640,6 +640,9 @@
 extern const char kFillOnAccountSelectName[];
 extern const char kFillOnAccountSelectDescription[];
 
+extern const char kFillOnAccountSelectHttpName[];
+extern const char kFillOnAccountSelectHttpDescription[];
+
 extern const char kForceTextDirectionName[];
 extern const char kForceTextDirectionDescription[];
 extern const char kForceDirectionLtr[];
@@ -754,9 +757,6 @@
 extern const char kMediaRouterCastAllowAllIPsName[];
 extern const char kMediaRouterCastAllowAllIPsDescription[];
 
-extern const char kMemoryCoordinatorName[];
-extern const char kMemoryCoordinatorDescription[];
-
 extern const char kMessageCenterNewStyleNotificationName[];
 extern const char kMessageCenterNewStyleNotificationDescription[];
 
diff --git a/chrome/browser/geolocation/geolocation_browsertest.cc b/chrome/browser/geolocation/geolocation_browsertest.cc
index 2d51206..3de86fc 100644
--- a/chrome/browser/geolocation/geolocation_browsertest.cc
+++ b/chrome/browser/geolocation/geolocation_browsertest.cc
@@ -45,7 +45,7 @@
 namespace {
 
 std::string GetErrorCodePermissionDenied() {
-  return base::IntToString(static_cast<int>(
+  return base::NumberToString(static_cast<int>(
       device::mojom::Geoposition::ErrorCode::PERMISSION_DENIED));
 }
 
diff --git a/chrome/browser/image_decoder_browsertest.cc b/chrome/browser/image_decoder_browsertest.cc
index 41546bb..8f898c1 100644
--- a/chrome/browser/image_decoder_browsertest.cc
+++ b/chrome/browser/image_decoder_browsertest.cc
@@ -99,47 +99,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestImageRequest);
 };
 
-class KillProcessObserver : public content::BrowserChildProcessObserver {
- public:
-  KillProcessObserver()
-      : did_kill_(false),
-        utility_process_name_(
-            l10n_util::GetStringUTF16(IDS_UTILITY_PROCESS_IMAGE_DECODER_NAME)) {
-    Add(this);
-  }
-
-  ~KillProcessObserver() override {
-    Remove(this);
-  }
-
-  bool did_kill() const { return did_kill_; }
-
- private:
-  void BrowserChildProcessHostConnected(
-      const content::ChildProcessData& data) override {
-    DCHECK_CURRENTLY_ON(BrowserThread::UI);
-    if (!data.GetProcess().IsValid() || data.name != utility_process_name_) {
-      return;
-    }
-
-    ASSERT_FALSE(did_kill_);
-
-    // Use a non-zero exit code so it counts as a crash.
-    // Don't wait for the process after sending the termination signal
-    // (SIGTERM). According to POSIX, doing so causes the resulting zombie to be
-    // removed from the process table. However, Chromium treats an error on
-    // |waitpid| (in this case, ECHILD) as a "normal" termination and doesn't
-    // invoke the process host delegate's OnProcessCrashed().
-    EXPECT_TRUE(data.GetProcess().Terminate(1, false));
-    did_kill_ = true;
-  }
-
-  bool did_kill_;
-  const base::string16 utility_process_name_;
-
-  DISALLOW_COPY_AND_ASSIGN(KillProcessObserver);
-};
-
 }  // namespace
 
 class ImageDecoderBrowserTest : public InProcessBrowserTest {
@@ -245,23 +204,3 @@
   test_request.reset();
   runner->Run();
 }
-
-// Killing the utility process counts as a crash. Thus the request fails.
-// If ImageDecoder did not handle the crash properly, the request never finishes
-// and this test would hang.
-// Note: This test is inherently racy because KillProcessObserver lives on the
-// UI thread but ImageDecoder does its work mainly on the IO thread. So the test
-// checks for both possible valid outcomes.
-IN_PROC_BROWSER_TEST_F(ImageDecoderBrowserTest, StartAndKillProcess) {
-  KillProcessObserver observer;
-  scoped_refptr<content::MessageLoopRunner> runner =
-      new content::MessageLoopRunner;
-  TestImageRequest test_request(runner->QuitClosure());
-  ImageDecoder::Start(&test_request, GetValidPngData());
-  runner->Run();
-  if (!test_request.decode_succeeded()) {
-    // The UI thread won the race. Make sure the utility process did get killed.
-    EXPECT_TRUE(observer.did_kill());
-  }
-  // Else the IO thread won the race and the image got decoded. Oh well.
-}
diff --git a/chrome/browser/installable/installable_manager.cc b/chrome/browser/installable/installable_manager.cc
index 20a20c53..d018075 100644
--- a/chrome/browser/installable/installable_manager.cc
+++ b/chrome/browser/installable/installable_manager.cc
@@ -562,10 +562,9 @@
   DCHECK(!manifest().IsEmpty());
   DCHECK(manifest().start_url.is_valid());
 
-  // Check to see if there is a single service worker controlling this page
-  // and the manifest's start url.
+  // Check to see if there is a service worker for the manifest's start url.
   service_worker_context_->CheckHasServiceWorker(
-      GetWebContents()->GetLastCommittedURL(), manifest().start_url,
+      manifest().start_url,
       base::Bind(&InstallableManager::OnDidCheckHasServiceWorker,
                  weak_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/installable/installable_manager_browsertest.cc b/chrome/browser/installable/installable_manager_browsertest.cc
index 86a8c24..6c41f7f 100644
--- a/chrome/browser/installable/installable_manager_browsertest.cc
+++ b/chrome/browser/installable/installable_manager_browsertest.cc
@@ -1332,6 +1332,30 @@
   EXPECT_EQ(NOT_OFFLINE_CAPABLE, tester->error_code());
 }
 
+IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest,
+                       CheckPageWithNestedServiceWorkerCanBeInstalled) {
+  base::RunLoop run_loop;
+  std::unique_ptr<CallbackTester> tester(
+      new CallbackTester(run_loop.QuitClosure()));
+
+  NavigateAndRunInstallableManager(browser(), tester.get(), GetWebAppParams(),
+                                   "/banners/nested_sw_test_page.html");
+
+  RunInstallableManager(browser(), tester.get(), GetWebAppParams());
+  run_loop.Run();
+
+  EXPECT_FALSE(tester->manifest().IsEmpty());
+  EXPECT_FALSE(tester->manifest_url().is_empty());
+
+  EXPECT_FALSE(tester->primary_icon_url().is_empty());
+  EXPECT_NE(nullptr, tester->primary_icon());
+  EXPECT_TRUE(tester->valid_manifest());
+  EXPECT_TRUE(tester->has_worker());
+  EXPECT_TRUE(tester->badge_icon_url().is_empty());
+  EXPECT_EQ(nullptr, tester->badge_icon());
+  EXPECT_EQ(NO_ERROR_DETECTED, tester->error_code());
+}
+
 IN_PROC_BROWSER_TEST_F(InstallableManagerBrowserTest, CheckDataUrlIcon) {
   // Verify that InstallableManager can handle data URL icons.
   base::RunLoop run_loop;
diff --git a/chrome/browser/internal_auth.cc b/chrome/browser/internal_auth.cc
index cdbff42f7c..74d9c74 100644
--- a/chrome/browser/internal_auth.cc
+++ b/chrome/browser/internal_auth.cc
@@ -161,7 +161,7 @@
   blob = domain + kItemSeparator;
   std::string tmp;
   ConvertVarValueMapToBlob(map, &tmp);
-  blob += tmp + kItemSeparator + base::Int64ToString(tick);
+  blob += tmp + kItemSeparator + base::NumberToString(tick);
 
   std::string hmac;
   unsigned char* hmac_data = reinterpret_cast<unsigned char*>(
@@ -179,7 +179,7 @@
   DCHECK(hmac_base64.size() < result.size());
   std::copy(hmac_base64.begin(), hmac_base64.end(), result.begin());
 
-  std::string tick_decimal = base::Int64ToString(tick);
+  std::string tick_decimal = base::NumberToString(tick);
   DCHECK(tick_decimal.size() <= kTickStringLength);
   std::copy(
       tick_decimal.begin(),
diff --git a/chrome/browser/lifetime/browser_shutdown.cc b/chrome/browser/lifetime/browser_shutdown.cc
index 5035fcda..df66836 100644
--- a/chrome/browser/lifetime/browser_shutdown.cc
+++ b/chrome/browser/lifetime/browser_shutdown.cc
@@ -267,7 +267,7 @@
     // We can't use prefs since all services are shutdown at this point.
     TimeDelta shutdown_delta = base::Time::Now() - *g_shutdown_started;
     std::string shutdown_ms =
-        base::Int64ToString(shutdown_delta.InMilliseconds());
+        base::NumberToString(shutdown_delta.InMilliseconds());
     int len = static_cast<int>(shutdown_ms.length()) + 1;
     base::FilePath shutdown_ms_file = GetShutdownMsPath();
     // Note: ReadLastShutdownFile() is done as a BLOCK_SHUTDOWN task so there's
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
index b228e65..8821b9d 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate.cc
@@ -331,17 +331,17 @@
   if (!request->is_pending()) {
     net::HttpRequestHeaders headers;
     headers.CopyFrom(request->extra_request_headers());
+    request->SetExtraRequestHeaders(headers);
     bool is_off_the_record = io_data->IsOffTheRecord();
     bool is_signed_in =
         !is_off_the_record &&
         !io_data->google_services_account_id()->GetValue().empty();
-    variations::AppendVariationHeaders(
+    variations::AppendVariationsHeader(
         request->url(),
         is_off_the_record ? variations::InIncognito::kYes
                           : variations::InIncognito::kNo,
         is_signed_in ? variations::SignedIn::kYes : variations::SignedIn::kNo,
-        &headers);
-    request->SetExtraRequestHeaders(headers);
+        request);
   }
 
   signin::ChromeRequestAdapter signin_request_adapter(request);
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc
index 657a80a..98dcfba 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_browsertest.cc
@@ -368,7 +368,7 @@
   // Note we need to replace the port of the redirect's URL.
   base::StringPairs replacement_text;
   replacement_text.push_back(std::make_pair(
-      "{{PORT}}", base::UintToString(embedded_test_server()->port())));
+      "{{PORT}}", base::NumberToString(embedded_test_server()->port())));
   std::string replacement_path = net::test_server::GetFilePathWithReplacements(
       "/mirror_request_header/http.www.google.com.html", replacement_text);
   all_tests.push_back(
@@ -386,7 +386,7 @@
   // First one should have the header, but not transfered to second one.
   replacement_text.clear();
   replacement_text.push_back(
-      std::make_pair("{{PORT}}", base::UintToString(https_server.port())));
+      std::make_pair("{{PORT}}", base::NumberToString(https_server.port())));
   replacement_path = net::test_server::GetFilePathWithReplacements(
       "/mirror_request_header/https.www.google.com.html", replacement_text);
   all_tests.push_back({https_server.GetURL("www.google.com", replacement_path),
diff --git a/chrome/browser/mac/bluetooth_utility.h b/chrome/browser/mac/bluetooth_utility.h
index df54d35e..8f4f559 100644
--- a/chrome/browser/mac/bluetooth_utility.h
+++ b/chrome/browser/mac/bluetooth_utility.h
@@ -17,6 +17,7 @@
   // On OSX 10.6, if the Link Manager Protocol version supports Low Energy,
   // there is no further indication of whether Low Energy is supported.
   BLUETOOTH_AVAILABLE_LE_UNKNOWN = 4,
+  BLUETOOTH_NOT_SUPPORTED = 5,
   BLUETOOTH_AVAILABILITY_COUNT,
 };
 
diff --git a/chrome/browser/mac/keystone_glue.mm b/chrome/browser/mac/keystone_glue.mm
index 9058d97..c3cd1c2 100644
--- a/chrome/browser/mac/keystone_glue.mm
+++ b/chrome/browser/mac/keystone_glue.mm
@@ -1007,18 +1007,16 @@
     // It's possible to get an OS-provided error string for this return code
     // using base::mac::DescriptionFromOSStatus, but most of those strings are
     // not useful/actionable for users, so we stick with the error code instead.
-    NSString* errorMessage =
-        l10n_util::GetNSStringFWithFixup(IDS_PROMOTE_PREFLIGHT_LAUNCH_ERROR,
-                                         base::IntToString16(status));
+    NSString* errorMessage = l10n_util::GetNSStringFWithFixup(
+        IDS_PROMOTE_PREFLIGHT_LAUNCH_ERROR, base::NumberToString16(status));
     [self updateStatus:kAutoupdatePromoteFailed
                version:nil
                  error:errorMessage];
     return;
   }
   if (exit_status != 0) {
-    NSString* errorMessage =
-        l10n_util::GetNSStringFWithFixup(IDS_PROMOTE_PREFLIGHT_SCRIPT_ERROR,
-                                         base::IntToString16(status));
+    NSString* errorMessage = l10n_util::GetNSStringFWithFixup(
+        IDS_PROMOTE_PREFLIGHT_SCRIPT_ERROR, base::NumberToString16(status));
     [self updateStatus:kAutoupdatePromoteFailed
                version:nil
                  error:errorMessage];
diff --git a/chrome/browser/media/encrypted_media_browsertest.cc b/chrome/browser/media/encrypted_media_browsertest.cc
index fd2c1c0a..be4fc9e 100644
--- a/chrome/browser/media/encrypted_media_browsertest.cc
+++ b/chrome/browser/media/encrypted_media_browsertest.cc
@@ -356,7 +356,7 @@
     // Override enabled CDM interface version for testing.
     command_line->AppendSwitchASCII(
         switches::kOverrideEnabledCdmInterfaceVersion,
-        base::IntToString(GetCdmInterfaceVersion()));
+        base::NumberToString(GetCdmInterfaceVersion()));
   }
 };
 
@@ -423,7 +423,7 @@
     query_params.emplace_back("keySystem", CurrentKeySystem());
     query_params.emplace_back(
         "configChangeType",
-        base::IntToString(static_cast<int>(config_change_type)));
+        base::NumberToString(static_cast<int>(config_change_type)));
     RunEncryptedMediaTestPage("mse_config_change.html", CurrentKeySystem(),
                               query_params, media::kEnded);
   }
diff --git a/chrome/browser/media/media_engagement_preloaded_list.cc b/chrome/browser/media/media_engagement_preloaded_list.cc
index 2d12fbb..d43d579 100644
--- a/chrome/browser/media/media_engagement_preloaded_list.cc
+++ b/chrome/browser/media/media_engagement_preloaded_list.cc
@@ -111,7 +111,7 @@
   if (origin.port() != url::DefaultPortForScheme(origin.scheme().data(),
                                                  origin.scheme().length())) {
     location.push_back(':');
-    std::string port(base::UintToString(origin.port()));
+    std::string port(base::NumberToString(origin.port()));
     location.append(std::move(port));
   }
 
diff --git a/chrome/browser/media/router/discovery/dial/dial_registry.cc b/chrome/browser/media/router/discovery/dial/dial_registry.cc
index e8abdc1..f71e64e0 100644
--- a/chrome/browser/media/router/discovery/dial/dial_registry.cc
+++ b/chrome/browser/media/router/discovery/dial/dial_registry.cc
@@ -292,7 +292,7 @@
 
 std::string DialRegistry::NextLabel() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
-  return base::IntToString(++label_count_);
+  return base::NumberToString(++label_count_);
 }
 
 void DialRegistry::OnDiscoveryRequest(DialService* service) {
diff --git a/chrome/browser/media/webrtc/audio_debug_recordings_handler.cc b/chrome/browser/media/webrtc/audio_debug_recordings_handler.cc
index 62ad5a85..fc8575b 100644
--- a/chrome/browser/media/webrtc/audio_debug_recordings_handler.cc
+++ b/chrome/browser/media/webrtc/audio_debug_recordings_handler.cc
@@ -37,7 +37,7 @@
     uint64_t audio_debug_recordings_id) {
   static const char kAudioDebugRecordingsFilePrefix[] = "AudioDebugRecordings.";
   return directory.AppendASCII(kAudioDebugRecordingsFilePrefix +
-                               base::Int64ToString(audio_debug_recordings_id));
+                               base::NumberToString(audio_debug_recordings_id));
 }
 
 base::FilePath GetLogDirectoryAndEnsureExists(
diff --git a/chrome/browser/media/webrtc/fake_desktop_media_list.cc b/chrome/browser/media/webrtc/fake_desktop_media_list.cc
index 24c2b42..7d5d9a4 100644
--- a/chrome/browser/media/webrtc/fake_desktop_media_list.cc
+++ b/chrome/browser/media/webrtc/fake_desktop_media_list.cc
@@ -23,7 +23,7 @@
     content::DesktopMediaID media_id) {
   Source source;
   source.id = media_id;
-  source.name = base::Int64ToString16(media_id.id);
+  source.name = base::NumberToString16(media_id.id);
 
   sources_.push_back(source);
   observer_->OnSourceAdded(this, sources_.size() - 1);
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager_local.cc b/chrome/browser/media/webrtc/webrtc_event_log_manager_local.cc
index 65e0f9dd..4e99b80 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager_local.cc
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager_local.cc
@@ -12,9 +12,9 @@
 #include "content/public/browser/browser_thread.h"
 
 #if defined(OS_WIN)
-#define IntToStringType base::IntToString16
+#define NumberToStringType base::NumberToString16
 #else
-#define IntToStringType base::IntToString
+#define NumberToStringType base::NumberToString
 #endif
 
 namespace webrtc_event_logging {
@@ -191,9 +191,9 @@
   } else if (unique_number != 0) {
     // The filename is taken, but a unique number was found.
     // TODO(crbug.com/785333): Fix the way the unique number is used.
-    file_path = file_path.InsertBeforeExtension(FILE_PATH_LITERAL(" (") +
-                                                IntToStringType(unique_number) +
-                                                FILE_PATH_LITERAL(")"));
+    file_path = file_path.InsertBeforeExtension(
+        FILE_PATH_LITERAL(" (") + NumberToStringType(unique_number) +
+        FILE_PATH_LITERAL(")"));
   }
 
   auto log_file =
diff --git a/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc b/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
index 0f5b847..4e2cac9 100644
--- a/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
+++ b/chrome/browser/media/webrtc/webrtc_event_log_manager_unittest.cc
@@ -60,9 +60,9 @@
 namespace webrtc_event_logging {
 
 #if defined(OS_WIN)
-#define IntToStringType base::IntToString16
+#define NumberToStringType base::NumberToString16
 #else
-#define IntToStringType base::IntToString
+#define NumberToStringType base::NumberToString
 #endif
 
 using ::testing::_;
@@ -1827,8 +1827,8 @@
   base::FilePath expected_path = local_logs_base_path;
   expected_path = local_logs_base_path.InsertBeforeExtension(
       FILE_PATH_LITERAL("_") + date + FILE_PATH_LITERAL("_") + time +
-      FILE_PATH_LITERAL("_") + IntToStringType(rph_->GetID()) +
-      FILE_PATH_LITERAL("_") + IntToStringType(kLid));
+      FILE_PATH_LITERAL("_") + NumberToStringType(rph_->GetID()) +
+      FILE_PATH_LITERAL("_") + NumberToStringType(kLid));
   expected_path = expected_path.AddExtension(local_log_extension_);
 
   EXPECT_EQ(file_path, expected_path);
@@ -1874,8 +1874,8 @@
   base::FilePath expected_path_1 = local_logs_base_path;
   expected_path_1 = local_logs_base_path.InsertBeforeExtension(
       FILE_PATH_LITERAL("_") + date + FILE_PATH_LITERAL("_") + time +
-      FILE_PATH_LITERAL("_") + IntToStringType(rph_->GetID()) +
-      FILE_PATH_LITERAL("_") + IntToStringType(kLid));
+      FILE_PATH_LITERAL("_") + NumberToStringType(rph_->GetID()) +
+      FILE_PATH_LITERAL("_") + NumberToStringType(kLid));
   expected_path_1 = expected_path_1.AddExtension(local_log_extension_);
 
   ASSERT_EQ(file_path_1, expected_path_1);
diff --git a/chrome/browser/media/webrtc/webrtc_log_uploader.cc b/chrome/browser/media/webrtc/webrtc_log_uploader.cc
index 6666449..b77b6c7 100644
--- a/chrome/browser/media/webrtc/webrtc_log_uploader.cc
+++ b/chrome/browser/media/webrtc/webrtc_log_uploader.cc
@@ -671,10 +671,11 @@
         base::UmaHistogramSparse("WebRtcTextLogging.UploadFailureNetErrorCode",
                                  network_error_code);
       }
-      error_message = base::StrCat(
-          {"Uploading failed, response code: ",
-           response_code.has_value() ? base::IntToString(response_code.value())
-                                     : "<no value>"});
+      error_message =
+          base::StrCat({"Uploading failed, response code: ",
+                        response_code.has_value()
+                            ? base::NumberToString(response_code.value())
+                            : "<no value>"});
     }
     base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
                              base::BindOnce(upload_done_data.callback, success,
diff --git a/chrome/browser/media/webrtc/webrtc_rtp_dump_handler.cc b/chrome/browser/media/webrtc/webrtc_rtp_dump_handler.cc
index 98b5191..0900ab0b 100644
--- a/chrome/browser/media/webrtc/webrtc_rtp_dump_handler.cc
+++ b/chrome/browser/media/webrtc/webrtc_rtp_dump_handler.cc
@@ -96,7 +96,7 @@
   if ((DumpTypeContainsIncoming(type) && incoming_state_ != STATE_NONE) ||
       (DumpTypeContainsOutgoing(type) && outgoing_state_ != STATE_NONE)) {
     *error_message =
-        "RTP dump already started for type " + base::IntToString(type);
+        "RTP dump already started for type " + base::NumberToString(type);
     return false;
   }
 
@@ -149,10 +149,9 @@
       (DumpTypeContainsOutgoing(type) && outgoing_state_ != STATE_STARTED)) {
     if (!callback.is_null()) {
       FireGenericDoneCallback(
-          callback,
-          false,
+          callback, false,
           "RTP dump not started or already stopped for type " +
-              base::IntToString(type));
+              base::NumberToString(type));
     }
     return;
   }
diff --git a/chrome/browser/media/webrtc/webrtc_text_log_handler.cc b/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
index f6d5c88..2a29f43 100644
--- a/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
+++ b/chrome/browser/media/webrtc/webrtc_text_log_handler.cc
@@ -52,7 +52,7 @@
 #include "chromeos/system/statistics_provider.h"
 #endif
 
-using base::IntToString;
+using base::NumberToString;
 using content::BrowserThread;
 
 namespace {
@@ -473,10 +473,10 @@
   // CPU
   base::CPU cpu;
   LogToCircularBuffer(
-      "Cpu: " + IntToString(cpu.family()) + "." + IntToString(cpu.model()) +
-      "." + IntToString(cpu.stepping()) + ", x" +
-      IntToString(base::SysInfo::NumberOfProcessors()) + ", " +
-      IntToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB");
+      "Cpu: " + NumberToString(cpu.family()) + "." +
+      NumberToString(cpu.model()) + "." + NumberToString(cpu.stepping()) +
+      ", x" + NumberToString(base::SysInfo::NumberOfProcessors()) + ", " +
+      NumberToString(base::SysInfo::AmountOfPhysicalMemoryMB()) + "MB");
   LogToCircularBuffer("Cpu brand: " + cpu.cpu_brand());
 
   // Computer model
@@ -495,8 +495,8 @@
   LogToCircularBuffer(
       "Gpu: machine-model-name=" + gpu_info.machine_model_name +
       ", machine-model-version=" + gpu_info.machine_model_version +
-      ", vendor-id=" + base::UintToString(active_gpu.vendor_id) +
-      ", device-id=" + base::UintToString(active_gpu.device_id) +
+      ", vendor-id=" + base::NumberToString(active_gpu.vendor_id) +
+      ", device-id=" + base::NumberToString(active_gpu.device_id) +
       ", driver-vendor=" + active_gpu.driver_vendor +
       ", driver-version=" + active_gpu.driver_version);
   LogToCircularBuffer("OpenGL: gl-vendor=" + gpu_info.gl_vendor +
diff --git a/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc b/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc
index d4e932c..3262cab 100644
--- a/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc
+++ b/chrome/browser/media/webrtc/webrtc_video_quality_browsertest.cc
@@ -233,9 +233,9 @@
     compare_command.AppendArg("--frame_analyzer");
     compare_command.AppendArgPath(path_to_analyzer);
     compare_command.AppendArg("--yuv_frame_width");
-    compare_command.AppendArg(base::IntToString(width));
+    compare_command.AppendArg(base::NumberToString(width));
     compare_command.AppendArg("--yuv_frame_height");
-    compare_command.AppendArg(base::IntToString(height));
+    compare_command.AppendArg(base::NumberToString(height));
     compare_command.AppendArg("--zxing_path");
     compare_command.AppendArgPath(path_to_zxing);
     compare_command.AppendArg("--ffmpeg_path");
diff --git a/chrome/browser/media_galleries/fileapi/device_media_async_file_util.cc b/chrome/browser/media_galleries/fileapi/device_media_async_file_util.cc
index 3434f1bd..e31571aa 100644
--- a/chrome/browser/media_galleries/fileapi/device_media_async_file_util.cc
+++ b/chrome/browser/media_galleries/fileapi/device_media_async_file_util.cc
@@ -619,7 +619,7 @@
     return;
   }
 
-  delegate->AddWatcher(url.origin(), url.path(), recursive, callback,
+  delegate->AddWatcher(url.origin().GetURL(), url.path(), recursive, callback,
                        notification_callback);
 }
 
@@ -634,7 +634,8 @@
     return;
   }
 
-  delegate->RemoveWatcher(url.origin(), url.path(), recursive, callback);
+  delegate->RemoveWatcher(url.origin().GetURL(), url.path(), recursive,
+                          callback);
 }
 
 DeviceMediaAsyncFileUtil::DeviceMediaAsyncFileUtil(
diff --git a/chrome/browser/media_galleries/media_galleries_test_util.cc b/chrome/browser/media_galleries/media_galleries_test_util.cc
index aa6c1e1..e09b9da9 100644
--- a/chrome/browser/media_galleries/media_galleries_test_util.cc
+++ b/chrome/browser/media_galleries/media_galleries_test_util.cc
@@ -105,21 +105,21 @@
   // a CHECK crash.
   music_override_.reset();
   std::string music_path_string("music");
-  music_path_string.append(base::IntToString(times_overrides_changed_));
+  music_path_string.append(base::NumberToString(times_overrides_changed_));
   music_override_.reset(new base::ScopedPathOverride(
       chrome::DIR_USER_MUSIC,
       fake_dir_.GetPath().AppendASCII(music_path_string)));
 
   pictures_override_.reset();
   std::string pictures_path_string("pictures");
-  pictures_path_string.append(base::IntToString(times_overrides_changed_));
+  pictures_path_string.append(base::NumberToString(times_overrides_changed_));
   pictures_override_.reset(new base::ScopedPathOverride(
       chrome::DIR_USER_PICTURES,
       fake_dir_.GetPath().AppendASCII(pictures_path_string)));
 
   video_override_.reset();
   std::string videos_path_string("videos");
-  videos_path_string.append(base::IntToString(times_overrides_changed_));
+  videos_path_string.append(base::NumberToString(times_overrides_changed_));
   video_override_.reset(new base::ScopedPathOverride(
       chrome::DIR_USER_VIDEOS,
       fake_dir_.GetPath().AppendASCII(videos_path_string)));
diff --git a/chrome/browser/memory/chrome_memory_coordinator_delegate.cc b/chrome/browser/memory/chrome_memory_coordinator_delegate.cc
deleted file mode 100644
index f5c77c32..0000000
--- a/chrome/browser/memory/chrome_memory_coordinator_delegate.cc
+++ /dev/null
@@ -1,38 +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/memory/chrome_memory_coordinator_delegate.h"
-
-#include "base/memory/ptr_util.h"
-#include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/resource_coordinator/lifecycle_unit_state.mojom.h"
-
-#if !defined(OS_ANDROID)
-#include "chrome/browser/resource_coordinator/tab_manager.h"
-#endif
-
-namespace memory {
-
-// static
-std::unique_ptr<content::MemoryCoordinatorDelegate>
-ChromeMemoryCoordinatorDelegate::Create() {
-  return base::WrapUnique(new ChromeMemoryCoordinatorDelegate);
-}
-
-ChromeMemoryCoordinatorDelegate::ChromeMemoryCoordinatorDelegate() {}
-
-ChromeMemoryCoordinatorDelegate::~ChromeMemoryCoordinatorDelegate() {}
-
-void ChromeMemoryCoordinatorDelegate::DiscardTab(bool skip_unload_handlers) {
-#if !defined(OS_ANDROID)
-  if (g_browser_process->GetTabManager()) {
-    g_browser_process->GetTabManager()->DiscardTab(
-        skip_unload_handlers ? mojom::LifecycleUnitDiscardReason::URGENT
-                             : mojom::LifecycleUnitDiscardReason::PROACTIVE);
-  }
-#endif
-}
-
-}  // namespace memory
diff --git a/chrome/browser/memory/chrome_memory_coordinator_delegate.h b/chrome/browser/memory/chrome_memory_coordinator_delegate.h
deleted file mode 100644
index 0976564d..0000000
--- a/chrome/browser/memory/chrome_memory_coordinator_delegate.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CHROME_BROWSER_MEMORY_CHROME_MEMORY_COORDINATOR_DELEGATE_H_
-#define CHROME_BROWSER_MEMORY_CHROME_MEMORY_COORDINATOR_DELEGATE_H_
-
-#include "base/memory/weak_ptr.h"
-#include "content/public/browser/memory_coordinator_delegate.h"
-
-namespace memory {
-
-// A MemoryCoordinatorDelegate which uses TabManager internally.
-class ChromeMemoryCoordinatorDelegate
-    : public content::MemoryCoordinatorDelegate {
- public:
-  static std::unique_ptr<content::MemoryCoordinatorDelegate> Create();
-
-  ~ChromeMemoryCoordinatorDelegate() override;
-
-  // MemoryCoordinatorDelegate implementation.
-  void DiscardTab(bool skip_unload_handlers) override;
-
- private:
-  ChromeMemoryCoordinatorDelegate();
-
-  DISALLOW_COPY_AND_ASSIGN(ChromeMemoryCoordinatorDelegate);
-};
-
-}  // namespace memory
-
-#endif  // CHROME_BROWSER_MEMORY_CHROME_MEMORY_COORDINATOR_DELEGATE_H_
diff --git a/chrome/browser/metrics/bluetooth_available_utility.cc b/chrome/browser/metrics/bluetooth_available_utility.cc
new file mode 100644
index 0000000..beecea5
--- /dev/null
+++ b/chrome/browser/metrics/bluetooth_available_utility.cc
@@ -0,0 +1,77 @@
+// 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/metrics/bluetooth_available_utility.h"
+
+#include "base/bind.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task/post_task.h"
+#include "build/build_config.h"
+#include "chrome/browser/mac/bluetooth_utility.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_factory.h"
+
+#if defined(OS_LINUX)
+#include "device/bluetooth/dbus/bluez_dbus_manager.h"
+#endif  // defined(OS_LINUX)
+
+namespace bluetooth_utility {
+
+void ReportAvailability(BluetoothAvailability availability) {
+  UMA_HISTOGRAM_ENUMERATION("Bluetooth.Availability", availability,
+                            BLUETOOTH_AVAILABILITY_COUNT);
+}
+
+void OnGetAdapter(scoped_refptr<device::BluetoothAdapter> adapter) {
+  if (!adapter->IsPresent()) {
+    ReportAvailability(BLUETOOTH_NOT_AVAILABLE);
+    return;
+  }
+
+  if (!device::BluetoothAdapterFactory::Get().IsLowEnergySupported()) {
+    ReportAvailability(BLUETOOTH_AVAILABLE_WITHOUT_LE);
+    return;
+  }
+
+  ReportAvailability(BLUETOOTH_AVAILABLE_WITH_LE);
+}
+
+void ReportBluetoothAvailability() {
+#if defined(OS_MACOSX)
+  // TODO(kenrb): This is separate from other platforms because we get a
+  // little bit of extra information from the Mac-specific code. It might not
+  // be worth having the extra code path, and we should consider whether to
+  // combine them (https://crbug.com/907279).
+  bluetooth_utility::BluetoothAvailability availability =
+      bluetooth_utility::GetBluetoothAvailability();
+  UMA_HISTOGRAM_ENUMERATION("Bluetooth.Availability", availability,
+                            bluetooth_utility::BLUETOOTH_AVAILABILITY_COUNT);
+  return;
+#endif  // defined(OS_MACOSX)
+
+  // GetAdapter must be called on the UI thread, because it creates a
+  // WeakPtr, which is checked from that thread on future calls.
+  if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) {
+    base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI},
+                             base::BindOnce(&ReportBluetoothAvailability));
+    return;
+  }
+
+#if defined(OS_LINUX)
+  // This is for tests that have not initialized bluez or dbus thread manager.
+  // Outside of tests these are initialized earlier during browser startup.
+  if (!bluez::BluezDBusManager::IsInitialized())
+    return;
+#endif  // defined(OS_LINUX)
+
+  if (!device::BluetoothAdapterFactory::Get().IsBluetoothSupported())
+    ReportAvailability(BLUETOOTH_NOT_SUPPORTED);
+
+  device::BluetoothAdapterFactory::Get().GetAdapter(
+      base::BindOnce(&OnGetAdapter));
+}
+
+}  // namespace bluetooth_utility
diff --git a/chrome/browser/metrics/bluetooth_available_utility.h b/chrome/browser/metrics/bluetooth_available_utility.h
new file mode 100644
index 0000000..dfa79b4
--- /dev/null
+++ b/chrome/browser/metrics/bluetooth_available_utility.h
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_METRICS_BLUETOOTH_AVAILABLE_UTILITY_H_
+#define CHROME_BROWSER_METRICS_BLUETOOTH_AVAILABLE_UTILITY_H_
+
+namespace bluetooth_utility {
+
+// Reports the bluetooth availability of the system's hardware.
+// This currently only works on ChromeOS and Windows. For Bluetooth
+// availability on OS X, see chrome/browser/mac/bluetooth_utility.h.
+void ReportBluetoothAvailability();
+
+}  // namespace bluetooth_utility
+
+#endif  // CHROME_BROWSER_METRICS_BLUETOOTH_AVAILABLE_UTILITY_H_
diff --git a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
index 85b818a..72257ad 100644
--- a/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
+++ b/chrome/browser/metrics/chrome_browser_main_extra_parts_metrics.cc
@@ -21,7 +21,7 @@
 #include "chrome/browser/about_flags.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_browser_main.h"
-#include "chrome/browser/mac/bluetooth_utility.h"
+#include "chrome/browser/metrics/bluetooth_available_utility.h"
 #include "chrome/browser/shell_integration.h"
 #include "chrome/browser/vr/service/xr_runtime_manager.h"
 #include "components/flags_ui/pref_service_flags_storage.h"
@@ -201,13 +201,7 @@
                         base::TimeTicks::IsHighResolution());
 #endif  // defined(OS_WIN)
 
-#if defined(OS_MACOSX)
-  bluetooth_utility::BluetoothAvailability availability =
-      bluetooth_utility::GetBluetoothAvailability();
-  UMA_HISTOGRAM_ENUMERATION("OSX.BluetoothAvailability",
-                            availability,
-                            bluetooth_utility::BLUETOOTH_AVAILABILITY_COUNT);
-#endif  // defined(OS_MACOSX)
+  bluetooth_utility::ReportBluetoothAvailability();
 
   // Record whether Chrome is the default browser or not.
   shell_integration::DefaultWebClientState default_state =
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.cc b/chrome/browser/metrics/chrome_metrics_service_client.cc
index 682e884..a728cff 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_service_client.cc
@@ -530,8 +530,8 @@
 
 std::unique_ptr<metrics::MetricsLogUploader>
 ChromeMetricsServiceClient::CreateUploader(
-    base::StringPiece server_url,
-    base::StringPiece insecure_server_url,
+    const GURL& server_url,
+    const GURL& insecure_server_url,
     base::StringPiece mime_type,
     metrics::MetricsLogUploader::MetricServiceType service_type,
     const metrics::MetricsLogUploader::UploadCallback& on_upload_complete) {
diff --git a/chrome/browser/metrics/chrome_metrics_service_client.h b/chrome/browser/metrics/chrome_metrics_service_client.h
index 5805443..cdaf5c90 100644
--- a/chrome/browser/metrics/chrome_metrics_service_client.h
+++ b/chrome/browser/metrics/chrome_metrics_service_client.h
@@ -73,8 +73,8 @@
   void OnLogCleanShutdown() override;
   void CollectFinalMetricsForLog(const base::Closure& done_callback) override;
   std::unique_ptr<metrics::MetricsLogUploader> CreateUploader(
-      base::StringPiece server_url,
-      base::StringPiece insecure_server_url,
+      const GURL& server_url,
+      const GURL& insecure_server_url,
       base::StringPiece mime_type,
       metrics::MetricsLogUploader::MetricServiceType service_type,
       const metrics::MetricsLogUploader::UploadCallback& on_upload_complete)
diff --git a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
index c6d289f..c4a62565 100644
--- a/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
+++ b/chrome/browser/metrics/chrome_metrics_services_manager_client.cc
@@ -81,7 +81,7 @@
                               int rate,
                               base::FieldTrial* trial) {
   std::map<std::string, std::string> params = {
-      {kRateParamName, base::IntToString(rate)}};
+      {kRateParamName, base::NumberToString(rate)}};
   variations::AssociateVariationParams(trial->trial_name(), group_name, params);
   trial->AppendGroup(group_name, rate);
 }
diff --git a/chrome/browser/nacl_host/test/gdb_debug_stub_browsertest.cc b/chrome/browser/nacl_host/test/gdb_debug_stub_browsertest.cc
index 8c2017a..49f2b5f 100644
--- a/chrome/browser/nacl_host/test/gdb_debug_stub_browsertest.cc
+++ b/chrome/browser/nacl_host/test/gdb_debug_stub_browsertest.cc
@@ -45,7 +45,7 @@
   base::PathService::Get(chrome::DIR_TEST_DATA, &script);
   script = script.AppendASCII("nacl/debug_stub_browser_tests.py");
   cmd.AppendArgPath(script);
-  cmd.AppendArg(base::IntToString(debug_stub_port));
+  cmd.AppendArg(base::NumberToString(debug_stub_port));
   cmd.AppendArg(test_name);
   LOG(INFO) << cmd.GetCommandLineString();
   *test_process = base::LaunchProcess(cmd, base::LaunchOptions());
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
index 55e591fb..98d2aaf 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_unittest.cc
@@ -115,11 +115,11 @@
     std::map<std::string, std::string> params;
     if (preconnect_origin_score_threshold.has_value()) {
       params["preconnect_origin_score_threshold"] =
-          base::IntToString(preconnect_origin_score_threshold.value());
+          base::NumberToString(preconnect_origin_score_threshold.value());
     }
     if (prefetch_url_score_threshold.has_value()) {
       params["prefetch_url_score_threshold"] =
-          base::IntToString(prefetch_url_score_threshold.value());
+          base::NumberToString(prefetch_url_score_threshold.value());
     }
     scoped_feature_list.InitAndEnableFeatureWithParameters(
         blink::features::kNavigationPredictor, params);
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index ef6e1f4..494cd4d 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -263,7 +263,7 @@
 void ChromeNetworkDelegate::OnBeforeRedirect(net::URLRequest* request,
                                              const GURL& new_location) {
   extensions_delegate_->NotifyBeforeRedirect(request, new_location);
-  variations::StripVariationHeaderIfNeeded(new_location, request);
+  variations::StripVariationsHeaderIfNeeded(new_location, request);
 }
 
 void ChromeNetworkDelegate::OnResponseStarted(net::URLRequest* request,
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 543a638..ca0cea0 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -13,7 +13,6 @@
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/task/post_task.h"
-#include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h"
@@ -42,7 +41,6 @@
 #include "content/public/common/url_constants.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
 #include "net/http/http_util.h"
-#include "net/net_buildflags.h"
 #include "services/network/public/cpp/cors/origin_access_list.h"
 #include "services/network/public/cpp/features.h"
 
@@ -53,6 +51,10 @@
 #include "components/user_manager/user.h"
 #endif
 
+#if BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
+#include "chrome/browser/net/trial_comparison_cert_verifier_controller.h"
+#endif
+
 #if BUILDFLAG(ENABLE_EXTENSIONS)
 #include "extensions/common/constants.h"
 #endif
@@ -466,6 +468,32 @@
   network_context_params->enable_certificate_reporting = true;
   network_context_params->enable_expect_ct_reporting = true;
 
+#if BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
+  if (!in_memory &&
+      TrialComparisonCertVerifierController::MaybeAllowedForProfile(profile_)) {
+    network::mojom::TrialComparisonCertVerifierConfigClientPtr config_client;
+    auto config_client_request = mojo::MakeRequest(&config_client);
+
+    network_context_params->trial_comparison_cert_verifier_params =
+        network::mojom::TrialComparisonCertVerifierParams::New();
+
+    if (!trial_comparison_cert_verifier_controller_) {
+      trial_comparison_cert_verifier_controller_ =
+          std::make_unique<TrialComparisonCertVerifierController>(profile_);
+    }
+    trial_comparison_cert_verifier_controller_->AddClient(
+        std::move(config_client),
+        mojo::MakeRequest(
+            &network_context_params->trial_comparison_cert_verifier_params
+                 ->report_client));
+    network_context_params->trial_comparison_cert_verifier_params
+        ->initial_allowed =
+        trial_comparison_cert_verifier_controller_->IsAllowed();
+    network_context_params->trial_comparison_cert_verifier_params
+        ->config_client_request = std::move(config_client_request);
+  }
+#endif
+
   if (domain_reliability::DomainReliabilityServiceFactory::
           ShouldCreateService()) {
     network_context_params->enable_domain_reliability = true;
diff --git a/chrome/browser/net/profile_network_context_service.h b/chrome/browser/net/profile_network_context_service.h
index 0c91b1a..637aec067 100644
--- a/chrome/browser/net/profile_network_context_service.h
+++ b/chrome/browser/net/profile_network_context_service.h
@@ -5,19 +5,23 @@
 #ifndef CHROME_BROWSER_NET_PROFILE_NETWORK_CONTEXT_SERVICE_H_
 #define CHROME_BROWSER_NET_PROFILE_NETWORK_CONTEXT_SERVICE_H_
 
+#include <memory>
 #include <utility>
 
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/timer/timer.h"
+#include "build/build_config.h"
 #include "chrome/browser/net/proxy_config_monitor.h"
 #include "components/content_settings/core/browser/content_settings_observer.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "components/prefs/pref_change_registrar.h"
 #include "components/prefs/pref_member.h"
+#include "net/net_buildflags.h"
 #include "services/network/public/mojom/network_context.mojom.h"
 
 class Profile;
+class TrialComparisonCertVerifierController;
 
 namespace user_prefs {
 class PrefRegistrySyncable;
@@ -152,6 +156,13 @@
   // Used to post schedule CT policy updates
   base::OneShotTimer ct_policy_update_timer_;
 
+#if BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
+  // Controls the cert verification trial. May be null if the trial is disabled
+  // or not allowed for this profile.
+  std::unique_ptr<TrialComparisonCertVerifierController>
+      trial_comparison_cert_verifier_controller_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(ProfileNetworkContextService);
 };
 
diff --git a/chrome/browser/net/trial_comparison_cert_verifier.cc b/chrome/browser/net/trial_comparison_cert_verifier.cc
deleted file mode 100644
index 352d1c9..0000000
--- a/chrome/browser/net/trial_comparison_cert_verifier.cc
+++ /dev/null
@@ -1,620 +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/net/trial_comparison_cert_verifier.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/location.h"
-#include "base/metrics/field_trial_params.h"
-#include "base/metrics/histogram_macros.h"
-#include "base/task/post_task.h"
-#include "build/build_config.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/profiles/profile_manager.h"
-#include "chrome/browser/safe_browsing/certificate_reporting_service.h"
-#include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
-#include "chrome/browser/ssl/certificate_error_report.h"
-#include "chrome/common/channel_info.h"
-#include "chrome/common/chrome_features.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/browser_thread.h"
-#include "crypto/sha2.h"
-#include "net/base/net_errors.h"
-#include "net/cert/cert_verify_proc.h"
-#include "net/cert/cert_verify_result.h"
-#include "net/cert/ev_root_ca_metadata.h"
-#include "net/cert/internal/cert_errors.h"
-#include "net/cert/internal/parsed_certificate.h"
-#include "net/cert/multi_threaded_cert_verifier.h"
-#include "net/cert/x509_util.h"
-#include "net/log/net_log.h"
-#include "net/log/net_log_event_type.h"
-#include "net/log/net_log_source_type.h"
-#include "net/log/net_log_with_source.h"
-
-// Certificate reports are only sent from official builds, but this flag can be
-// set by tests.
-static bool g_is_fake_official_build_for_cert_verifier_testing = false;
-
-namespace {
-
-bool CheckTrialEligibility(void* profile_id) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  // g_browser_process is valid until after all threads are stopped. So it must
-  // be valid if the CheckTrialEligibility task got to run.
-  if (!g_browser_process->profile_manager()->IsValidProfile(profile_id))
-    return false;
-  const Profile* profile = reinterpret_cast<const Profile*>(profile_id);
-  const PrefService& prefs = *profile->GetPrefs();
-
-  // Only allow on non-incognito profiles which have SBER opt-in set.
-  // See design doc for more details:
-  // https://docs.google.com/document/d/1AM1CD42bC6LHWjKg-Hkid_RLr2DH6OMzstH9-pGSi-g
-  return !profile->IsOffTheRecord() &&
-         safe_browsing::IsExtendedReportingEnabled(prefs);
-}
-
-void SendTrialVerificationReport(void* profile_id,
-                                 const net::CertVerifier::Config& config,
-                                 const net::CertVerifier::RequestParams& params,
-                                 const net::CertVerifyResult& primary_result,
-                                 const net::CertVerifyResult& trial_result) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-  if (!g_browser_process->profile_manager()->IsValidProfile(profile_id))
-    return;
-  Profile* profile = reinterpret_cast<Profile*>(profile_id);
-
-  CertificateErrorReport report(params.hostname(), *params.certificate(),
-                                config, primary_result, trial_result);
-
-  report.AddNetworkTimeInfo(g_browser_process->network_time_tracker());
-  report.AddChromeChannel(chrome::GetChannel());
-
-  std::string serialized_report;
-  if (!report.Serialize(&serialized_report))
-    return;
-
-  CertificateReportingServiceFactory::GetForBrowserContext(profile)->Send(
-      serialized_report);
-}
-
-std::unique_ptr<base::Value> TrialVerificationJobResultCallback(
-    bool trial_success,
-    net::NetLogCaptureMode capture_mode) {
-  std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue());
-  results->SetKey("trial_success", base::Value(trial_success));
-  return std::move(results);
-}
-
-bool CertVerifyResultEqual(const net::CertVerifyResult& a,
-                           const net::CertVerifyResult& b) {
-  return std::tie(a.cert_status, a.is_issued_by_known_root) ==
-             std::tie(b.cert_status, b.is_issued_by_known_root) &&
-         (!!a.verified_cert == !!b.verified_cert) &&
-         (!a.verified_cert ||
-          a.verified_cert->EqualsIncludingChain(b.verified_cert.get()));
-}
-
-scoped_refptr<net::ParsedCertificate> ParsedCertificateFromBuffer(
-    CRYPTO_BUFFER* cert_handle,
-    net::CertErrors* errors) {
-  return net::ParsedCertificate::Create(
-      bssl::UpRef(cert_handle),
-      net::x509_util::DefaultParseCertificateOptions(), errors);
-}
-
-net::ParsedCertificateList ParsedCertificateListFromX509Certificate(
-    const net::X509Certificate* cert) {
-  net::CertErrors parsing_errors;
-
-  net::ParsedCertificateList certs;
-  scoped_refptr<net::ParsedCertificate> target =
-      ParsedCertificateFromBuffer(cert->cert_buffer(), &parsing_errors);
-  if (!target)
-    return {};
-  certs.push_back(target);
-
-  for (const auto& buf : cert->intermediate_buffers()) {
-    scoped_refptr<net::ParsedCertificate> intermediate =
-        ParsedCertificateFromBuffer(buf.get(), &parsing_errors);
-    if (!intermediate)
-      return {};
-    certs.push_back(intermediate);
-  }
-
-  return certs;
-}
-
-// Tests whether cert has multiple EV policies, and at least one matches the
-// root. This is not a complete test of EV, but just enough to give a possible
-// explanation as to why the platform verifier did not validate as EV while
-// builtin did. (Since only the builtin verifier correctly handles multiple
-// candidate EV policies.)
-bool CertHasMultipleEVPoliciesAndOneMatchesRoot(
-    const net::X509Certificate* cert) {
-  if (cert->intermediate_buffers().empty())
-    return false;
-
-  net::ParsedCertificateList certs =
-      ParsedCertificateListFromX509Certificate(cert);
-  if (certs.empty())
-    return false;
-
-  net::ParsedCertificate* leaf = certs.front().get();
-  net::ParsedCertificate* root = certs.back().get();
-
-  if (!leaf->has_policy_oids())
-    return false;
-
-  const net::EVRootCAMetadata* ev_metadata =
-      net::EVRootCAMetadata::GetInstance();
-  std::set<net::der::Input> candidate_oids;
-  for (const net::der::Input& oid : leaf->policy_oids()) {
-    if (ev_metadata->IsEVPolicyOIDGivenBytes(oid))
-      candidate_oids.insert(oid);
-  }
-
-  if (candidate_oids.size() <= 1)
-    return false;
-
-  net::SHA256HashValue root_fingerprint;
-  crypto::SHA256HashString(root->der_cert().AsStringPiece(),
-                           root_fingerprint.data,
-                           sizeof(root_fingerprint.data));
-
-  for (const net::der::Input& oid : candidate_oids) {
-    if (ev_metadata->HasEVPolicyOIDGivenBytes(root_fingerprint, oid))
-      return true;
-  }
-
-  return false;
-}
-
-}  // namespace
-
-class TrialComparisonCertVerifier::TrialVerificationJob {
- public:
-  TrialVerificationJob(const net::CertVerifier::Config& config,
-                       const net::CertVerifier::RequestParams& params,
-                       const net::NetLogWithSource& source_net_log,
-                       TrialComparisonCertVerifier* cert_verifier,
-                       int primary_error,
-                       const net::CertVerifyResult& primary_result,
-                       void* profile_id)
-      : config_(config),
-        config_changed_(false),
-        params_(params),
-        net_log_(net::NetLogWithSource::Make(
-            source_net_log.net_log(),
-            net::NetLogSourceType::TRIAL_CERT_VERIFIER_JOB)),
-        profile_id_(profile_id),
-        cert_verifier_(cert_verifier),
-        primary_error_(primary_error),
-        primary_result_(primary_result) {
-    net_log_.BeginEvent(net::NetLogEventType::TRIAL_CERT_VERIFIER_JOB);
-    source_net_log.AddEvent(
-        net::NetLogEventType::TRIAL_CERT_VERIFIER_JOB_COMPARISON_STARTED,
-        net_log_.source().ToEventParametersCallback());
-  }
-
-  ~TrialVerificationJob() {
-    if (cert_verifier_) {
-      net_log_.AddEvent(net::NetLogEventType::CANCELLED);
-      net_log_.EndEvent(net::NetLogEventType::TRIAL_CERT_VERIFIER_JOB);
-    }
-  }
-
-  void Start() {
-    // Unretained is safe because trial_request_ will cancel the callback on
-    // destruction.
-    int rv = cert_verifier_->trial_verifier()->Verify(
-        params_, &trial_result_,
-        base::BindOnce(&TrialVerificationJob::OnJobCompleted,
-                       base::Unretained(this)),
-        &trial_request_, net_log_);
-    if (rv != net::ERR_IO_PENDING)
-      OnJobCompleted(rv);
-  }
-
-  void OnConfigChanged() { config_changed_ = true; }
-
-  void Finish(bool is_success, TrialComparisonResult result_code) {
-    TrialComparisonCertVerifier* cert_verifier = cert_verifier_;
-    cert_verifier_ = nullptr;
-
-    UMA_HISTOGRAM_ENUMERATION("Net.CertVerifier_TrialComparisonResult",
-                              result_code);
-
-    net_log_.EndEvent(
-        net::NetLogEventType::TRIAL_CERT_VERIFIER_JOB,
-        base::BindRepeating(&TrialVerificationJobResultCallback, is_success));
-
-    if (!is_success &&
-        !base::GetFieldTrialParamByFeatureAsBool(
-            features::kCertDualVerificationTrialFeature, "uma_only", false)) {
-      base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI})
-          ->PostTask(FROM_HERE, base::BindOnce(&SendTrialVerificationReport,
-                                               profile_id_, config_, params_,
-                                               primary_result_, trial_result_));
-    }
-
-    // |this| is deleted after RemoveJob returns.
-    cert_verifier->RemoveJob(this);
-  }
-
-  void FinishSuccess(TrialComparisonResult result_code) {
-    Finish(true /* is_success */, result_code);
-  }
-
-  void FinishWithError() {
-    DCHECK(trial_error_ != primary_error_ ||
-           !CertVerifyResultEqual(trial_result_, primary_result_));
-
-    TrialComparisonResult result_code = kInvalid;
-
-    if (primary_error_ == net::OK && trial_error_ == net::OK) {
-      result_code = kBothValidDifferentDetails;
-    } else if (primary_error_ == net::OK) {
-      result_code = kPrimaryValidSecondaryError;
-    } else if (trial_error_ == net::OK) {
-      result_code = kPrimaryErrorSecondaryValid;
-    } else {
-      result_code = kBothErrorDifferentDetails;
-    }
-    Finish(false /* is_success */, result_code);
-  }
-
-  void OnJobCompleted(int trial_result_error) {
-    DCHECK(primary_result_.verified_cert);
-    DCHECK(trial_result_.verified_cert);
-
-    trial_error_ = trial_result_error;
-
-    bool errors_equal = trial_result_error == primary_error_;
-    bool details_equal = CertVerifyResultEqual(trial_result_, primary_result_);
-    bool trial_success = errors_equal && details_equal;
-
-    if (trial_success) {
-      FinishSuccess(kEqual);
-      return;
-    }
-
-#if defined(OS_MACOSX)
-    if (primary_error_ == net::ERR_CERT_REVOKED &&
-        !config_.enable_rev_checking &&
-        !(primary_result_.cert_status &
-          net::CERT_STATUS_REV_CHECKING_ENABLED) &&
-        !(trial_result_.cert_status &
-          (net::CERT_STATUS_REVOKED | net::CERT_STATUS_REV_CHECKING_ENABLED))) {
-      if (config_changed_) {
-        FinishSuccess(kIgnoredConfigurationChanged);
-        return;
-      }
-      // CertVerifyProcMac does some revocation checking even if we didn't want
-      // it. Try verifying with the trial verifier with revocation checking
-      // enabled, see if it then returns REVOKED.
-
-      int rv = cert_verifier_->revocation_trial_verifier()->Verify(
-          params_, &reverification_result_,
-          base::BindOnce(
-              &TrialVerificationJob::OnMacRevcheckingReverificationJobCompleted,
-              base::Unretained(this)),
-          &reverification_request_, net_log_);
-      if (rv != net::ERR_IO_PENDING)
-        OnMacRevcheckingReverificationJobCompleted(rv);
-      return;
-    }
-#endif
-
-    const bool chains_equal =
-        primary_result_.verified_cert->EqualsIncludingChain(
-            trial_result_.verified_cert.get());
-
-    if (!chains_equal &&
-        (trial_error_ == net::OK || primary_error_ != net::OK)) {
-      if (config_changed_) {
-        FinishSuccess(kIgnoredConfigurationChanged);
-        return;
-      }
-      // Chains were different, reverify the trial_result_.verified_cert chain
-      // using the platform verifier and compare results again.
-      RequestParams reverification_params(trial_result_.verified_cert,
-                                          params_.hostname(), params_.flags(),
-                                          params_.ocsp_response());
-
-      int rv = cert_verifier_->primary_reverifier()->Verify(
-          reverification_params, &reverification_result_,
-          base::BindOnce(&TrialVerificationJob::
-                             OnPrimaryReverifiyWithSecondaryChainCompleted,
-                         base::Unretained(this)),
-          &reverification_request_, net_log_);
-      if (rv != net::ERR_IO_PENDING)
-        OnPrimaryReverifiyWithSecondaryChainCompleted(rv);
-      return;
-    }
-
-    TrialComparisonResult ignorable_difference =
-        IsSynchronouslyIgnorableDifference(primary_error_, primary_result_,
-                                           trial_error_, trial_result_);
-    if (ignorable_difference != kInvalid) {
-      FinishSuccess(ignorable_difference);
-      return;
-    }
-
-    FinishWithError();
-  }
-
-  // Check if the differences between the primary and trial verifiers can be
-  // ignored. This only handles differences that can be checked synchronously.
-  // If the difference is ignorable, returns the relevant TrialComparisonResult,
-  // otherwise returns kInvalid.
-  static TrialComparisonResult IsSynchronouslyIgnorableDifference(
-      int primary_error,
-      const net::CertVerifyResult& primary_result,
-      int trial_error,
-      const net::CertVerifyResult& trial_result) {
-    DCHECK(primary_result.verified_cert);
-    DCHECK(trial_result.verified_cert);
-
-    if (primary_error == net::OK &&
-        primary_result.verified_cert->intermediate_buffers().empty()) {
-      // Platform may support trusting a leaf certificate directly. Builtin
-      // verifier does not. See https://crbug.com/814994.
-      return kIgnoredLocallyTrustedLeaf;
-    }
-
-    const bool chains_equal =
-        primary_result.verified_cert->EqualsIncludingChain(
-            trial_result.verified_cert.get());
-
-    if (chains_equal && (trial_result.cert_status & net::CERT_STATUS_IS_EV) &&
-        !(primary_result.cert_status & net::CERT_STATUS_IS_EV) &&
-        (primary_error == trial_error)) {
-      // The platform CertVerifyProc impls only check a single potential EV
-      // policy from the leaf.  If the leaf had multiple policies, builtin
-      // verifier may verify it as EV when the platform verifier did not.
-      if (CertHasMultipleEVPoliciesAndOneMatchesRoot(
-              trial_result.verified_cert.get())) {
-        return kIgnoredMultipleEVPoliciesAndOneMatchesRoot;
-      }
-    }
-    return kInvalid;
-  }
-
-#if defined(OS_MACOSX)
-  void OnMacRevcheckingReverificationJobCompleted(int reverification_error) {
-    if (reverification_error == net::ERR_CERT_REVOKED) {
-      FinishSuccess(kIgnoredMacUndesiredRevocationChecking);
-      return;
-    }
-    FinishWithError();
-  }
-#endif
-
-  void OnPrimaryReverifiyWithSecondaryChainCompleted(int reverification_error) {
-    if (reverification_error == trial_error_ &&
-        CertVerifyResultEqual(reverification_result_, trial_result_)) {
-      // The new result matches the builtin verifier, so this was just
-      // a difference in the platform's path-building ability.
-      // Ignore the difference.
-      FinishSuccess(kIgnoredDifferentPathReVerifiesEquivalent);
-      return;
-    }
-
-    if (IsSynchronouslyIgnorableDifference(reverification_error,
-                                           reverification_result_, trial_error_,
-                                           trial_result_) != kInvalid) {
-      // The new result matches if ignoring differences. Still use the
-      // |kIgnoredDifferentPathReVerifiesEquivalent| code rather than the
-      // result of IsSynchronouslyIgnorableDifference, since it's the higher
-      // level description of what the difference is in this case.
-      FinishSuccess(kIgnoredDifferentPathReVerifiesEquivalent);
-      return;
-    }
-
-    FinishWithError();
-  }
-
- private:
-  const net::CertVerifier::Config config_;
-  bool config_changed_;
-  const net::CertVerifier::RequestParams params_;
-  const net::NetLogWithSource net_log_;
-  void* profile_id_;
-  TrialComparisonCertVerifier* cert_verifier_;  // Non-owned.
-
-  // Results from the trial verification.
-  int trial_error_;
-  net::CertVerifyResult trial_result_;
-  std::unique_ptr<net::CertVerifier::Request> trial_request_;
-
-  // Saved results of the primary verification.
-  int primary_error_;
-  const net::CertVerifyResult primary_result_;
-
-  // Results from re-verification attempt.
-  net::CertVerifyResult reverification_result_;
-  std::unique_ptr<net::CertVerifier::Request> reverification_request_;
-
-  DISALLOW_COPY_AND_ASSIGN(TrialVerificationJob);
-};
-
-TrialComparisonCertVerifier::TrialComparisonCertVerifier(
-    void* profile_id,
-    scoped_refptr<net::CertVerifyProc> primary_verify_proc,
-    scoped_refptr<net::CertVerifyProc> trial_verify_proc)
-    : profile_id_(profile_id),
-      config_id_(0),
-      primary_verifier_(
-          net::MultiThreadedCertVerifier::CreateForDualVerificationTrial(
-              primary_verify_proc,
-              // Unretained is safe since the callback won't be called after
-              // |primary_verifier_| is destroyed.
-              base::BindRepeating(
-                  &TrialComparisonCertVerifier::OnPrimaryVerifierComplete,
-                  base::Unretained(this)),
-              true /* should_record_histograms */)),
-      primary_reverifier_(std::make_unique<net::MultiThreadedCertVerifier>(
-          primary_verify_proc)),
-      trial_verifier_(
-          net::MultiThreadedCertVerifier::CreateForDualVerificationTrial(
-              trial_verify_proc,
-              // Unretained is safe since the callback won't be called after
-              // |trial_verifier_| is destroyed.
-              base::BindRepeating(
-                  &TrialComparisonCertVerifier::OnTrialVerifierComplete,
-                  base::Unretained(this)),
-              false /* should_record_histograms */)),
-      revocation_trial_verifier_(
-          net::MultiThreadedCertVerifier::CreateForDualVerificationTrial(
-              trial_verify_proc,
-              // Unretained is safe since the callback won't be called after
-              // |trial_verifier_| is destroyed.
-              base::BindRepeating(
-                  &TrialComparisonCertVerifier::OnTrialVerifierComplete,
-                  base::Unretained(this)),
-              false /* should_record_histograms */)),
-      weak_ptr_factory_(this) {
-  net::CertVerifier::Config config;
-  config.enable_rev_checking = true;
-  revocation_trial_verifier_->SetConfig(config);
-}
-
-TrialComparisonCertVerifier::~TrialComparisonCertVerifier() = default;
-
-// static
-void TrialComparisonCertVerifier::SetFakeOfficialBuildForTesting() {
-  g_is_fake_official_build_for_cert_verifier_testing = true;
-}
-
-int TrialComparisonCertVerifier::Verify(const RequestParams& params,
-                                        net::CertVerifyResult* verify_result,
-                                        net::CompletionOnceCallback callback,
-                                        std::unique_ptr<Request>* out_req,
-                                        const net::NetLogWithSource& net_log) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  return primary_verifier_->Verify(params, verify_result, std::move(callback),
-                                   out_req, net_log);
-}
-
-void TrialComparisonCertVerifier::SetConfig(const Config& config) {
-  config_ = config;
-  config_id_++;
-
-  primary_verifier_->SetConfig(config);
-  primary_reverifier_->SetConfig(config);
-  trial_verifier_->SetConfig(config);
-
-  // Always enable revocation checking for the revocation trial verifier.
-  net::CertVerifier::Config config_with_revocation = config;
-  config_with_revocation.enable_rev_checking = true;
-  revocation_trial_verifier_->SetConfig(config_with_revocation);
-
-  // Notify all in-process jobs that the underlying configuration has changed.
-  for (auto& job : jobs_) {
-    job->OnConfigChanged();
-  }
-}
-
-void TrialComparisonCertVerifier::OnPrimaryVerifierComplete(
-    const RequestParams& params,
-    const net::NetLogWithSource& net_log,
-    int primary_error,
-    const net::CertVerifyResult& primary_result,
-    base::TimeDelta primary_latency,
-    bool is_first_job) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  bool is_official_build = g_is_fake_official_build_for_cert_verifier_testing;
-#if defined(OFFICIAL_BUILD) && defined(GOOGLE_CHROME_BUILD)
-  is_official_build = true;
-#endif
-  if (!is_official_build || !base::FeatureList::IsEnabled(
-                                features::kCertDualVerificationTrialFeature)) {
-    return;
-  }
-
-  base::PostTaskAndReplyWithResult(
-      base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI})
-          .get(),
-      FROM_HERE, base::BindOnce(CheckTrialEligibility, profile_id_),
-      base::BindOnce(&TrialComparisonCertVerifier::MaybeDoTrialVerification,
-                     weak_ptr_factory_.GetWeakPtr(), params, net_log,
-                     primary_error, primary_result, primary_latency,
-                     is_first_job, config_id_, profile_id_));
-}
-
-void TrialComparisonCertVerifier::OnTrialVerifierComplete(
-    const RequestParams& params,
-    const net::NetLogWithSource& net_log,
-    int trial_error,
-    const net::CertVerifyResult& trial_result,
-    base::TimeDelta latency,
-    bool is_first_job) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency_TrialSecondary",
-                             latency, base::TimeDelta::FromMilliseconds(1),
-                             base::TimeDelta::FromMinutes(10), 100);
-  if (is_first_job) {
-    UMA_HISTOGRAM_CUSTOM_TIMES(
-        "Net.CertVerifier_First_Job_Latency_TrialSecondary", latency,
-        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
-        100);
-  }
-}
-
-void TrialComparisonCertVerifier::MaybeDoTrialVerification(
-    const RequestParams& params,
-    const net::NetLogWithSource& net_log,
-    int primary_error,
-    const net::CertVerifyResult& primary_result,
-    base::TimeDelta primary_latency,
-    bool is_first_job,
-    uint32_t config_id,
-    void* profile_id,
-    bool trial_allowed) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-
-  // If the trial is not allowed, or the configuration has changed while
-  // determining if the trial is allowed, no need to continue.
-  if (!trial_allowed || config_id != config_id_)
-    return;
-
-  // Only record the TrialPrimary histograms for the same set of requests
-  // that TrialSecondary histograms will be recorded for, in order to get a
-  // direct comparison.
-  UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency_TrialPrimary",
-                             primary_latency,
-                             base::TimeDelta::FromMilliseconds(1),
-                             base::TimeDelta::FromMinutes(10), 100);
-  if (is_first_job) {
-    UMA_HISTOGRAM_CUSTOM_TIMES(
-        "Net.CertVerifier_First_Job_Latency_TrialPrimary", primary_latency,
-        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
-        100);
-  }
-
-  std::unique_ptr<TrialVerificationJob> job =
-      std::make_unique<TrialVerificationJob>(config_, params, net_log, this,
-                                             primary_error, primary_result,
-                                             profile_id);
-  TrialVerificationJob* job_ptr = job.get();
-  jobs_.insert(std::move(job));
-  job_ptr->Start();
-}
-
-void TrialComparisonCertVerifier::RemoveJob(TrialVerificationJob* job_ptr) {
-  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  auto it = jobs_.find(job_ptr);
-  DCHECK(it != jobs_.end());
-  jobs_.erase(it);
-}
diff --git a/chrome/browser/net/trial_comparison_cert_verifier.h b/chrome/browser/net/trial_comparison_cert_verifier.h
deleted file mode 100644
index 8f71de6..0000000
--- a/chrome/browser/net/trial_comparison_cert_verifier.h
+++ /dev/null
@@ -1,125 +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_NET_TRIAL_COMPARISON_CERT_VERIFIER_H_
-#define CHROME_BROWSER_NET_TRIAL_COMPARISON_CERT_VERIFIER_H_
-
-#include <stdint.h>
-
-#include "base/containers/unique_ptr_adapters.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/threading/thread_checker.h"
-#include "net/base/net_export.h"
-#include "net/cert/cert_verifier.h"
-
-namespace net {
-class CertVerifyProc;
-}
-
-class TrialComparisonCertVerifier : public net::CertVerifier {
- public:
-  // These values are persisted to logs. Entries should not be renumbered and
-  // numeric values should never be reused.
-  enum TrialComparisonResult {
-    kInvalid = 0,
-    kEqual = 1,
-    kPrimaryValidSecondaryError = 2,
-    kPrimaryErrorSecondaryValid = 3,
-    kBothValidDifferentDetails = 4,
-    kBothErrorDifferentDetails = 5,
-    kIgnoredMacUndesiredRevocationChecking = 6,
-    kIgnoredMultipleEVPoliciesAndOneMatchesRoot = 7,
-    kIgnoredDifferentPathReVerifiesEquivalent = 8,
-    kIgnoredLocallyTrustedLeaf = 9,
-    kIgnoredConfigurationChanged = 10,
-    kMaxValue = kIgnoredConfigurationChanged
-  };
-
-  TrialComparisonCertVerifier(
-      void* profile_id,
-      scoped_refptr<net::CertVerifyProc> primary_verify_proc,
-      scoped_refptr<net::CertVerifyProc> trial_verify_proc);
-
-  ~TrialComparisonCertVerifier() override;
-
-  // This method can be called by tests to fake an official build (reports are
-  // only sent from official builds).
-  static void SetFakeOfficialBuildForTesting();
-
-  // CertVerifier implementation
-  int Verify(const RequestParams& params,
-             net::CertVerifyResult* verify_result,
-             net::CompletionOnceCallback callback,
-             std::unique_ptr<Request>* out_req,
-             const net::NetLogWithSource& net_log) override;
-  void SetConfig(const Config& config) override;
-
-  // Returns a CertVerifier using the primary CertVerifyProc, which will not
-  // cause OnPrimaryVerifierComplete to be called. This can be used to
-  // attempt to re-verify a cert with different chain or flags without
-  // messing up the stats or potentially causing an infinite loop.
-  net::CertVerifier* primary_reverifier() const {
-    return primary_reverifier_.get();
-  }
-  net::CertVerifier* trial_verifier() const { return trial_verifier_.get(); }
-  net::CertVerifier* revocation_trial_verifier() const {
-    return revocation_trial_verifier_.get();
-  }
-
- private:
-  class TrialVerificationJob;
-
-  void OnPrimaryVerifierComplete(const RequestParams& params,
-                                 const net::NetLogWithSource& net_log,
-                                 int primary_error,
-                                 const net::CertVerifyResult& primary_result,
-                                 base::TimeDelta primary_latency,
-                                 bool is_first_job);
-  void OnTrialVerifierComplete(const RequestParams& params,
-                               const net::NetLogWithSource& net_log,
-                               int trial_error,
-                               const net::CertVerifyResult& trial_result,
-                               base::TimeDelta latency,
-                               bool is_first_job);
-  void MaybeDoTrialVerification(const RequestParams& params,
-                                const net::NetLogWithSource& net_log,
-                                int primary_error,
-                                const net::CertVerifyResult& primary_result,
-                                base::TimeDelta primary_latency,
-                                bool is_first_job,
-                                uint32_t config_id,
-                                void* profile_id,
-                                bool trial_allowed);
-
-  void RemoveJob(TrialVerificationJob* job_ptr);
-
-  // The profile this verifier is associated with. Stored as a void* to avoid
-  // accidentally using it on IO thread.
-  void* profile_id_;
-
-  // Unique identifier for the current configuration, to determine if a
-  // configuration has changed in between primary and trial verifications.
-  uint32_t config_id_;
-  net::CertVerifier::Config config_;
-
-  std::unique_ptr<net::CertVerifier> primary_verifier_;
-  std::unique_ptr<net::CertVerifier> primary_reverifier_;
-  std::unique_ptr<net::CertVerifier> trial_verifier_;
-  // Similar to |trial_verifier_|, except configured to always check
-  // revocation information.
-  std::unique_ptr<net::CertVerifier> revocation_trial_verifier_;
-
-  std::set<std::unique_ptr<TrialVerificationJob>, base::UniquePtrComparator>
-      jobs_;
-
-  THREAD_CHECKER(thread_checker_);
-
-  base::WeakPtrFactory<TrialComparisonCertVerifier> weak_ptr_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(TrialComparisonCertVerifier);
-};
-
-#endif  // CHROME_BROWSER_NET_TRIAL_COMPARISON_CERT_VERIFIER_H_
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc b/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc
new file mode 100644
index 0000000..e04a943
--- /dev/null
+++ b/chrome/browser/net/trial_comparison_cert_verifier_browsertest.cc
@@ -0,0 +1,88 @@
+// 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/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/metrics/subprocess_metrics_provider.h"
+#include "chrome/browser/net/trial_comparison_cert_verifier_controller.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "net/cert/trial_comparison_cert_verifier.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+class TrialComparisonCertVerifierTest : public InProcessBrowserTest {
+ public:
+  TrialComparisonCertVerifierTest()
+      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+
+ protected:
+  net::EmbeddedTestServer https_test_server_;
+};
+
+IN_PROC_BROWSER_TEST_F(TrialComparisonCertVerifierTest, TrialDisabled) {
+  ASSERT_TRUE(https_test_server_.Start());
+  base::HistogramTester histograms;
+  ui_test_utils::NavigateToURL(browser(),
+                               https_test_server_.GetURL("/title1.html"));
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary", 0);
+  histograms.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
+}
+
+class TrialComparisonCertVerifierFeatureEnabledTest
+    : public TrialComparisonCertVerifierTest {
+ public:
+  TrialComparisonCertVerifierFeatureEnabledTest() {
+    TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(true);
+    scoped_feature_ = std::make_unique<base::test::ScopedFeatureList>();
+    // None of these tests should generate a report, but set the trial to
+    // uma_only mode anyway just to be safe.
+    scoped_feature_->InitAndEnableFeatureWithParameters(
+        features::kCertDualVerificationTrialFeature, {{"uma_only", "true"}});
+  }
+
+  ~TrialComparisonCertVerifierFeatureEnabledTest() override {
+    TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(
+        false);
+  }
+
+ protected:
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_;
+};
+
+IN_PROC_BROWSER_TEST_F(TrialComparisonCertVerifierFeatureEnabledTest,
+                       TrialEnabledPrefDisabled) {
+  ASSERT_TRUE(https_test_server_.Start());
+  base::HistogramTester histograms;
+  ui_test_utils::NavigateToURL(browser(),
+                               https_test_server_.GetURL("/title1.html"));
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary", 0);
+  histograms.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
+}
+
+IN_PROC_BROWSER_TEST_F(TrialComparisonCertVerifierFeatureEnabledTest,
+                       TrialEnabledPrefEnabled) {
+  safe_browsing::SetExtendedReportingPref(browser()->profile()->GetPrefs(),
+                                          true);
+
+  ASSERT_TRUE(https_test_server_.Start());
+  base::HistogramTester histograms;
+  ui_test_utils::NavigateToURL(browser(),
+                               https_test_server_.GetURL("/title1.html"));
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary", 1);
+  histograms.ExpectUniqueSample("Net.CertVerifier_TrialComparisonResult",
+                                net::TrialComparisonCertVerifier::kEqual, 1);
+}
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_controller.cc b/chrome/browser/net/trial_comparison_cert_verifier_controller.cc
new file mode 100644
index 0000000..a203a05
--- /dev/null
+++ b/chrome/browser/net/trial_comparison_cert_verifier_controller.cc
@@ -0,0 +1,133 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/net/trial_comparison_cert_verifier_controller.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/location.h"
+#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task/post_task.h"
+#include "build/build_config.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chrome/browser/safe_browsing/certificate_reporting_service.h"
+#include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
+#include "chrome/browser/ssl/certificate_error_report.h"
+#include "chrome/common/channel_info.h"
+#include "chrome/common/chrome_features.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
+
+namespace {
+
+// Certificate reports are only sent from official builds, but this flag can be
+// set by tests.
+bool g_is_fake_official_build_for_cert_verifier_testing = false;
+
+}  // namespace
+
+TrialComparisonCertVerifierController::TrialComparisonCertVerifierController(
+    Profile* profile)
+    : profile_(profile) {
+  if (!MaybeAllowedForProfile(profile_)) {
+    // Don't bother registering pref change notifier if the trial could never be
+    // enabled.
+    return;
+  }
+
+  pref_change_registrar_.Init(profile_->GetPrefs());
+  pref_change_registrar_.Add(
+      prefs::kSafeBrowsingScoutReportingEnabled,
+      base::BindRepeating(&TrialComparisonCertVerifierController::RefreshState,
+                          base::Unretained(this)));
+}
+
+TrialComparisonCertVerifierController::
+    ~TrialComparisonCertVerifierController() = default;
+
+// static
+bool TrialComparisonCertVerifierController::MaybeAllowedForProfile(
+    Profile* profile) {
+  bool is_official_build = g_is_fake_official_build_for_cert_verifier_testing;
+#if defined(OFFICIAL_BUILD) && defined(GOOGLE_CHROME_BUILD)
+  is_official_build = true;
+#endif
+  return is_official_build &&
+         base::FeatureList::IsEnabled(
+             features::kCertDualVerificationTrialFeature) &&
+         !profile->IsOffTheRecord();
+}
+
+void TrialComparisonCertVerifierController::AddClient(
+    network::mojom::TrialComparisonCertVerifierConfigClientPtr config_client,
+    network::mojom::TrialComparisonCertVerifierReportClientRequest
+        report_client_request) {
+  binding_set_.AddBinding(this, std::move(report_client_request));
+  config_client_set_.AddPtr(std::move(config_client));
+}
+
+bool TrialComparisonCertVerifierController::IsAllowed() const {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  // Only allow on non-incognito profiles which have SBER opt-in set.
+  // See design doc for more details:
+  // https://docs.google.com/document/d/1AM1CD42bC6LHWjKg-Hkid_RLr2DH6OMzstH9-pGSi-g
+
+  if (!MaybeAllowedForProfile(profile_))
+    return false;
+
+  const PrefService& prefs = *profile_->GetPrefs();
+
+  return safe_browsing::IsExtendedReportingEnabled(prefs);
+}
+
+void TrialComparisonCertVerifierController::SendTrialReport(
+    const std::string& hostname,
+    const scoped_refptr<net::X509Certificate>& unverified_cert,
+    bool enable_rev_checking,
+    bool require_rev_checking_local_anchors,
+    bool enable_sha1_local_anchors,
+    bool disable_symantec_enforcement,
+    const net::CertVerifyResult& primary_result,
+    const net::CertVerifyResult& trial_result) {
+  if (!IsAllowed() ||
+      base::GetFieldTrialParamByFeatureAsBool(
+          features::kCertDualVerificationTrialFeature, "uma_only", false)) {
+    return;
+  }
+
+  CertificateErrorReport report(
+      hostname, *unverified_cert, enable_rev_checking,
+      require_rev_checking_local_anchors, enable_sha1_local_anchors,
+      disable_symantec_enforcement, primary_result, trial_result);
+
+  report.AddNetworkTimeInfo(g_browser_process->network_time_tracker());
+  report.AddChromeChannel(chrome::GetChannel());
+
+  std::string serialized_report;
+  if (!report.Serialize(&serialized_report))
+    return;
+
+  CertificateReportingServiceFactory::GetForBrowserContext(profile_)->Send(
+      serialized_report);
+}
+
+// static
+void TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(
+    bool fake_official_build) {
+  g_is_fake_official_build_for_cert_verifier_testing = fake_official_build;
+}
+
+void TrialComparisonCertVerifierController::RefreshState() {
+  const bool is_allowed = IsAllowed();
+  config_client_set_.ForAllPtrs(
+      [is_allowed](
+          network::mojom::TrialComparisonCertVerifierConfigClient* client) {
+        client->OnTrialConfigUpdated(is_allowed);
+      });
+}
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_controller.h b/chrome/browser/net/trial_comparison_cert_verifier_controller.h
new file mode 100644
index 0000000..c6adfb2
--- /dev/null
+++ b/chrome/browser/net/trial_comparison_cert_verifier_controller.h
@@ -0,0 +1,77 @@
+// 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_NET_TRIAL_COMPARISON_CERT_VERIFIER_CONTROLLER_H_
+#define CHROME_BROWSER_NET_TRIAL_COMPARISON_CERT_VERIFIER_CONTROLLER_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/containers/unique_ptr_adapters.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "components/prefs/pref_change_registrar.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "net/base/net_export.h"
+#include "net/cert/cert_verifier.h"
+#include "services/network/public/mojom/trial_comparison_cert_verifier.mojom.h"
+
+class Profile;
+
+class TrialComparisonCertVerifierController
+    : public network::mojom::TrialComparisonCertVerifierReportClient {
+ public:
+  // Creates a TrialComparisonCertVerifierController using |profile| for
+  // preferences and reporting.
+  // |profile| must outlive the TrialComparisonCertVerifierController.
+  explicit TrialComparisonCertVerifierController(Profile* profile);
+  ~TrialComparisonCertVerifierController() override;
+
+  // Returns true if the trial could potentially be enabled for |profile|;
+  static bool MaybeAllowedForProfile(Profile* profile);
+
+  // Adds a client to the controller, sending trial configuration updates to
+  // |config_client|, and receiving trial reports from |report_client_request|.
+  void AddClient(
+      network::mojom::TrialComparisonCertVerifierConfigClientPtr config_client,
+      network::mojom::TrialComparisonCertVerifierReportClientRequest
+          report_client_request);
+
+  // Returns true if the trial is enabled and SBER flag is set for this
+  // profile.
+  bool IsAllowed() const;
+
+  // TrialComparisonCertVerifierReportClient implementation:
+  void SendTrialReport(
+      const std::string& hostname,
+      const scoped_refptr<net::X509Certificate>& unverified_cert,
+      bool enable_rev_checking,
+      bool require_rev_checking_local_anchors,
+      bool enable_sha1_local_anchors,
+      bool disable_symantec_enforcement,
+      const net::CertVerifyResult& primary_result,
+      const net::CertVerifyResult& trial_result) override;
+
+  static void SetFakeOfficialBuildForTesting(bool fake_official_build);
+
+ private:
+  void RefreshState();
+
+  Profile* profile_;
+  PrefChangeRegistrar pref_change_registrar_;
+
+  mojo::BindingSet<network::mojom::TrialComparisonCertVerifierReportClient>
+      binding_set_;
+
+  mojo::InterfacePtrSet<network::mojom::TrialComparisonCertVerifierConfigClient>
+      config_client_set_;
+
+  DISALLOW_COPY_AND_ASSIGN(TrialComparisonCertVerifierController);
+};
+
+#endif  // CHROME_BROWSER_NET_TRIAL_COMPARISON_CERT_VERIFIER_CONTROLLER_H_
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_controller_unittest.cc b/chrome/browser/net/trial_comparison_cert_verifier_controller_unittest.cc
new file mode 100644
index 0000000..6dcef07
--- /dev/null
+++ b/chrome/browser/net/trial_comparison_cert_verifier_controller_unittest.cc
@@ -0,0 +1,528 @@
+// 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/net/trial_comparison_cert_verifier_controller.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/run_loop.h"
+#include "base/task/post_task.h"
+#include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
+#include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
+#include "chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h"
+#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
+#include "chrome/browser/ssl/cert_logger.pb.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/test/base/testing_browser_process.h"
+#include "chrome/test/base/testing_profile.h"
+#include "chrome/test/base/testing_profile_manager.h"
+#include "components/safe_browsing/common/safe_browsing_prefs.h"
+#include "components/sync_preferences/testing_pref_service_syncable.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "content/public/test/test_utils.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/gtest_util.h"
+#include "net/test/test_data_directory.h"
+#include "net/url_request/url_request_test_util.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/mojom/trial_comparison_cert_verifier.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using certificate_reporting_test_utils::CertificateReportingServiceTestHelper;
+using certificate_reporting_test_utils::ReportExpectation;
+using certificate_reporting_test_utils::RetryStatus;
+using net::test::IsError;
+using testing::_;
+using testing::Mock;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::StrictMock;
+
+namespace {
+
+MATCHER_P(CertChainMatches, expected_cert, "") {
+  net::CertificateList actual_certs =
+      net::X509Certificate::CreateCertificateListFromBytes(
+          arg.data(), arg.size(),
+          net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+  if (actual_certs.empty()) {
+    *result_listener << "failed to parse arg";
+    return false;
+  }
+  std::vector<std::string> actual_der_certs;
+  for (const auto& cert : actual_certs) {
+    actual_der_certs.emplace_back(
+        net::x509_util::CryptoBufferAsStringPiece(cert->cert_buffer()));
+  }
+
+  std::vector<std::string> expected_der_certs;
+  expected_der_certs.emplace_back(
+      net::x509_util::CryptoBufferAsStringPiece(expected_cert->cert_buffer()));
+  for (const auto& buffer : expected_cert->intermediate_buffers()) {
+    expected_der_certs.emplace_back(
+        net::x509_util::CryptoBufferAsStringPiece(buffer.get()));
+  }
+
+  return actual_der_certs == expected_der_certs;
+}
+
+}  // namespace
+
+class MockTrialComparisonCertVerifierConfigClient
+    : public network::mojom::TrialComparisonCertVerifierConfigClient {
+ public:
+  MockTrialComparisonCertVerifierConfigClient(
+      network::mojom::TrialComparisonCertVerifierConfigClientRequest
+          config_client_request)
+      : binding_(this, std::move(config_client_request)) {}
+
+  MOCK_METHOD1(OnTrialConfigUpdated, void(bool allowed));
+
+ private:
+  mojo::Binding<network::mojom::TrialComparisonCertVerifierConfigClient>
+      binding_;
+};
+
+class TrialComparisonCertVerifierControllerTest : public testing::Test {
+ public:
+  void SetUp() override {
+    cert_chain_1_ = CreateCertificateChainFromFile(
+        net::GetTestCertsDirectory(), "multi-root-chain1.pem",
+        net::X509Certificate::FORMAT_AUTO);
+    ASSERT_TRUE(cert_chain_1_);
+    leaf_cert_1_ = net::X509Certificate::CreateFromBuffer(
+        bssl::UpRef(cert_chain_1_->cert_buffer()), {});
+    ASSERT_TRUE(leaf_cert_1_);
+    cert_chain_2_ = CreateCertificateChainFromFile(
+        net::GetTestCertsDirectory(), "multi-root-chain2.pem",
+        net::X509Certificate::FORMAT_AUTO);
+    ASSERT_TRUE(cert_chain_2_);
+
+    ok_result_.verified_cert = cert_chain_1_;
+    bad_result_.verified_cert = cert_chain_2_;
+    bad_result_.cert_status = net::CERT_STATUS_DATE_INVALID;
+
+    reporting_service_test_helper_ =
+        base::MakeRefCounted<CertificateReportingServiceTestHelper>();
+    CertificateReportingServiceFactory::GetInstance()
+        ->SetReportEncryptionParamsForTesting(
+            reporting_service_test_helper()->server_public_key(),
+            reporting_service_test_helper()->server_public_key_version());
+    CertificateReportingServiceFactory::GetInstance()
+        ->SetURLLoaderFactoryForTesting(reporting_service_test_helper_);
+    reporting_service_test_helper()->SetFailureMode(
+        certificate_reporting_test_utils::REPORTS_SUCCESSFUL);
+
+    if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+      system_request_context_getter_ =
+          base::MakeRefCounted<net::TestURLRequestContextGetter>(
+              base::CreateSingleThreadTaskRunnerWithTraits(
+                  {content::BrowserThread::IO}));
+      TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(
+          system_request_context_getter_.get());
+    }
+
+    // Creating the profile before the SafeBrowsingService ensures the
+    // ServiceManagerConnection is initialized.
+    profile_manager_ = std::make_unique<TestingProfileManager>(
+        TestingBrowserProcess::GetGlobal());
+    ASSERT_TRUE(profile_manager_->SetUp());
+    ASSERT_TRUE(g_browser_process->profile_manager());
+    profile_ = profile_manager_->CreateTestingProfile("profile1");
+
+    sb_service_ =
+        base::MakeRefCounted<safe_browsing::TestSafeBrowsingService>();
+    TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(
+        sb_service_.get());
+    g_browser_process->safe_browsing_service()->Initialize();
+
+    // Initialize CertificateReportingService for |profile_|.
+    ASSERT_TRUE(reporting_service());
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void CreateController(Profile* profile) {
+    network::mojom::TrialComparisonCertVerifierConfigClientPtr config_client;
+    auto config_client_request = mojo::MakeRequest(&config_client);
+
+    trial_controller_ =
+        std::make_unique<TrialComparisonCertVerifierController>(profile);
+    trial_controller_->AddClient(std::move(config_client),
+                                 mojo::MakeRequest(&report_client_));
+
+    mock_config_client_ = std::make_unique<
+        StrictMock<MockTrialComparisonCertVerifierConfigClient>>(
+        std::move(config_client_request));
+  }
+
+  void CreateController() { CreateController(profile()); }
+
+  void TearDown() override {
+    // Ensure any in-flight mojo calls get run.
+    base::RunLoop().RunUntilIdle();
+
+    // Ensure mock expectations are checked.
+    mock_config_client_ = nullptr;
+
+    if (TestingBrowserProcess::GetGlobal()->safe_browsing_service()) {
+      TestingBrowserProcess::GetGlobal()->safe_browsing_service()->ShutDown();
+      TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(nullptr);
+    }
+    TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(nullptr);
+    system_request_context_getter_ = nullptr;
+
+    TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(
+        false);
+  }
+
+  TestingProfile* profile() { return profile_; }
+  sync_preferences::TestingPrefServiceSyncable* pref_service() {
+    return profile_->GetTestingPrefService();
+  }
+  TrialComparisonCertVerifierController& trial_controller() {
+    return *trial_controller_;
+  }
+  network::mojom::TrialComparisonCertVerifierReportClientPtr& report_client() {
+    return report_client_;
+  }
+  MockTrialComparisonCertVerifierConfigClient& mock_config_client() {
+    return *mock_config_client_;
+  }
+  CertificateReportingServiceTestHelper* reporting_service_test_helper() {
+    return reporting_service_test_helper_.get();
+  }
+  CertificateReportingService* reporting_service() const {
+    return CertificateReportingServiceFactory::GetForBrowserContext(profile_);
+  }
+
+ protected:
+  scoped_refptr<net::X509Certificate> cert_chain_1_;
+  scoped_refptr<net::X509Certificate> cert_chain_2_;
+  scoped_refptr<net::X509Certificate> leaf_cert_1_;
+  net::CertVerifyResult ok_result_;
+  net::CertVerifyResult bad_result_;
+
+  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_;
+
+ private:
+  scoped_refptr<CertificateReportingServiceTestHelper>
+      reporting_service_test_helper_;
+  content::TestBrowserThreadBundle thread_bundle_;
+  scoped_refptr<safe_browsing::SafeBrowsingService> sb_service_;
+  scoped_refptr<net::URLRequestContextGetter> system_request_context_getter_;
+  std::unique_ptr<TestingProfileManager> profile_manager_;
+  TestingProfile* profile_;
+
+  network::mojom::TrialComparisonCertVerifierReportClientPtr report_client_;
+  std::unique_ptr<TrialComparisonCertVerifierController> trial_controller_;
+  std::unique_ptr<StrictMock<MockTrialComparisonCertVerifierConfigClient>>
+      mock_config_client_;
+};
+
+TEST_F(TrialComparisonCertVerifierControllerTest, NothingEnabled) {
+  CreateController();
+
+  // Trial should not be allowed.
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Enable the SBER pref, shouldn't matter since it's a non-official build and
+  // field trial isn't enabled.
+  safe_browsing::SetExtendedReportingPref(pref_service(), true);
+
+  // Trial still not allowed, and OnTrialConfigUpdated should not be called
+  // either.
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Attempting to send a report should also do nothing.
+  report_client()->SendTrialReport("hostname", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, ok_result_);
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+  // Expect no report since the trial is not allowed.
+  reporting_service_test_helper()->ExpectNoRequests(reporting_service());
+}
+
+TEST_F(TrialComparisonCertVerifierControllerTest,
+       OfficialBuildTrialNotEnabled) {
+  TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(true);
+  CreateController();
+
+  EXPECT_FALSE(trial_controller().IsAllowed());
+  safe_browsing::SetExtendedReportingPref(pref_service(), true);
+
+  // Trial still not allowed, and OnTrialConfigUpdated should not be called
+  // either.
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Attempting to send a report should do nothing.
+  report_client()->SendTrialReport("hostname", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, ok_result_);
+
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+
+  // Expect no report since the trial is not allowed.
+  reporting_service_test_helper()->ExpectNoRequests(reporting_service());
+}
+
+TEST_F(TrialComparisonCertVerifierControllerTest,
+       NotOfficialBuildTrialEnabled) {
+  scoped_feature_ = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_->InitAndEnableFeature(
+      features::kCertDualVerificationTrialFeature);
+  CreateController();
+
+  EXPECT_FALSE(trial_controller().IsAllowed());
+  safe_browsing::SetExtendedReportingPref(pref_service(), true);
+
+  // Trial still not allowed, and OnTrialConfigUpdated should not be called
+  // either.
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Attempting to send a report should do nothing.
+  report_client()->SendTrialReport("hostname", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, ok_result_);
+
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+
+  // Expect no report since the trial is not allowed.
+  reporting_service_test_helper()->ExpectNoRequests(reporting_service());
+}
+
+TEST_F(TrialComparisonCertVerifierControllerTest, OfficialBuildTrialEnabled) {
+  TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(true);
+  scoped_feature_ = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_->InitAndEnableFeature(
+      features::kCertDualVerificationTrialFeature);
+  CreateController();
+
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Enable the SBER pref, which should trigger the OnTrialConfigUpdated
+  // callback.
+  EXPECT_CALL(mock_config_client(), OnTrialConfigUpdated(true)).Times(1);
+  safe_browsing::SetExtendedReportingPref(pref_service(), true);
+
+  // Trial should now be allowed.
+  EXPECT_TRUE(trial_controller().IsAllowed());
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+  // OnTrialConfigUpdated should have been called.
+  Mock::VerifyAndClear(&mock_config_client());
+
+  // Report should be sent.
+  report_client()->SendTrialReport("127.0.0.1", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, bad_result_);
+
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+
+  // Expect a report.
+  std::vector<std::string> full_reports;
+  reporting_service_test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
+      &full_reports);
+
+  ASSERT_EQ(1U, full_reports.size());
+
+  chrome_browser_ssl::CertLoggerRequest report;
+  ASSERT_TRUE(report.ParseFromString(full_reports[0]));
+
+  EXPECT_EQ(0, report.cert_error_size());
+  EXPECT_EQ(0, report.cert_status_size());
+
+  ASSERT_TRUE(report.has_features_info());
+  ASSERT_TRUE(report.features_info().has_trial_verification_info());
+  const chrome_browser_ssl::TrialVerificationInfo& trial_info =
+      report.features_info().trial_verification_info();
+  ASSERT_EQ(1, trial_info.cert_error_size());
+  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
+            trial_info.cert_error()[0]);
+  EXPECT_EQ(0, trial_info.cert_status_size());
+
+  EXPECT_THAT(report.unverified_cert_chain(), CertChainMatches(leaf_cert_1_));
+  EXPECT_THAT(report.cert_chain(), CertChainMatches(cert_chain_1_));
+  EXPECT_THAT(trial_info.cert_chain(), CertChainMatches(cert_chain_2_));
+
+  // Disable the SBER pref again, which should trigger the OnTrialConfigUpdated
+  // callback.
+  EXPECT_CALL(mock_config_client(), OnTrialConfigUpdated(false)).Times(1);
+  safe_browsing::SetExtendedReportingPref(pref_service(), false);
+
+  // Not allowed now.
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Attempting to send a report should do nothing now.
+  report_client()->SendTrialReport("hostname", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, bad_result_);
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+  // Expect no report since the trial is not allowed.
+  reporting_service_test_helper()->ExpectNoRequests(reporting_service());
+}
+
+TEST_F(TrialComparisonCertVerifierControllerTest,
+       OfficialBuildTrialEnabledTwoClients) {
+  TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(true);
+  scoped_feature_ = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_->InitAndEnableFeature(
+      features::kCertDualVerificationTrialFeature);
+  CreateController();
+
+  network::mojom::TrialComparisonCertVerifierReportClientPtr report_client_2;
+
+  network::mojom::TrialComparisonCertVerifierConfigClientPtr config_client_2;
+  auto config_client_2_request = mojo::MakeRequest(&config_client_2);
+
+  trial_controller().AddClient(std::move(config_client_2),
+                               mojo::MakeRequest(&report_client_2));
+
+  StrictMock<MockTrialComparisonCertVerifierConfigClient> mock_config_client_2(
+      std::move(config_client_2_request));
+
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Enable the SBER pref, which should trigger the OnTrialConfigUpdated
+  // callback.
+  EXPECT_CALL(mock_config_client(), OnTrialConfigUpdated(true)).Times(1);
+  EXPECT_CALL(mock_config_client_2, OnTrialConfigUpdated(true)).Times(1);
+  safe_browsing::SetExtendedReportingPref(pref_service(), true);
+
+  // Trial should now be allowed.
+  EXPECT_TRUE(trial_controller().IsAllowed());
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+  // OnTrialConfigUpdated should have been called.
+  Mock::VerifyAndClear(&mock_config_client());
+  Mock::VerifyAndClear(&mock_config_client_2);
+
+  // Report should be sent.
+  report_client()->SendTrialReport("127.0.0.1", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, bad_result_);
+  report_client_2->SendTrialReport("127.0.0.2", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, bad_result_);
+
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+
+  // Expect a report.
+  std::vector<std::string> full_reports;
+  reporting_service_test_helper()->WaitForRequestsDestroyed(
+      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED},
+                                     {"127.0.0.2", RetryStatus::NOT_RETRIED}}),
+      &full_reports);
+
+  ASSERT_EQ(2U, full_reports.size());
+
+  chrome_browser_ssl::CertLoggerRequest report;
+  for (size_t i = 0; i < 2; ++i) {
+    ASSERT_TRUE(report.ParseFromString(full_reports[i]));
+
+    EXPECT_EQ(0, report.cert_error_size());
+    EXPECT_EQ(0, report.cert_status_size());
+
+    ASSERT_TRUE(report.has_features_info());
+    ASSERT_TRUE(report.features_info().has_trial_verification_info());
+    const chrome_browser_ssl::TrialVerificationInfo& trial_info =
+        report.features_info().trial_verification_info();
+    ASSERT_EQ(1, trial_info.cert_error_size());
+    EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
+              trial_info.cert_error()[0]);
+    EXPECT_EQ(0, trial_info.cert_status_size());
+
+    EXPECT_THAT(report.unverified_cert_chain(), CertChainMatches(leaf_cert_1_));
+    EXPECT_THAT(report.cert_chain(), CertChainMatches(cert_chain_1_));
+    EXPECT_THAT(trial_info.cert_chain(), CertChainMatches(cert_chain_2_));
+  }
+
+  // Disable the SBER pref again, which should trigger the OnTrialConfigUpdated
+  // callback.
+  EXPECT_CALL(mock_config_client(), OnTrialConfigUpdated(false)).Times(1);
+  EXPECT_CALL(mock_config_client_2, OnTrialConfigUpdated(false)).Times(1);
+  safe_browsing::SetExtendedReportingPref(pref_service(), false);
+
+  // Not allowed now.
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Attempting to send a report should do nothing now.
+  report_client()->SendTrialReport("hostname", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, bad_result_);
+  report_client_2->SendTrialReport("hostname2", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, bad_result_);
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+  // Expect no report since the trial is not allowed.
+  reporting_service_test_helper()->ExpectNoRequests(reporting_service());
+}
+
+TEST_F(TrialComparisonCertVerifierControllerTest,
+       OfficialBuildTrialEnabledUmaOnly) {
+  TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(true);
+  scoped_feature_ = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_->InitAndEnableFeatureWithParameters(
+      features::kCertDualVerificationTrialFeature, {{"uma_only", "true"}});
+  CreateController();
+
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Enable the SBER pref, which should trigger the OnTrialConfigUpdated
+  // callback.
+  EXPECT_CALL(mock_config_client(), OnTrialConfigUpdated(true)).Times(1);
+  safe_browsing::SetExtendedReportingPref(pref_service(), true);
+
+  // Trial should now be allowed.
+  EXPECT_TRUE(trial_controller().IsAllowed());
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+  // OnTrialConfigUpdated should have been called.
+  Mock::VerifyAndClear(&mock_config_client());
+
+  // In uma_only mode, the network service will generate a report, but the
+  // trial controller will not send it to the reporting service.
+  report_client()->SendTrialReport("127.0.0.1", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, bad_result_);
+
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+
+  // Expect no reports in uma_only mode.
+  reporting_service_test_helper()->ExpectNoRequests(reporting_service());
+}
+
+TEST_F(TrialComparisonCertVerifierControllerTest,
+       IncognitoOfficialBuildTrialEnabled) {
+  TrialComparisonCertVerifierController::SetFakeOfficialBuildForTesting(true);
+  scoped_feature_ = std::make_unique<base::test::ScopedFeatureList>();
+  scoped_feature_->InitAndEnableFeature(
+      features::kCertDualVerificationTrialFeature);
+  CreateController(profile()->GetOffTheRecordProfile());
+
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Enable the SBER pref, shouldn't matter since it's an incognito profile.
+  safe_browsing::SetExtendedReportingPref(pref_service(), true);
+
+  // Trial still not allowed, and OnTrialConfigUpdated should not be called
+  // either.
+  EXPECT_FALSE(trial_controller().IsAllowed());
+
+  // Attempting to send a report should also do nothing.
+  report_client()->SendTrialReport("hostname", leaf_cert_1_, false, false,
+                                   false, false, ok_result_, ok_result_);
+  // Ensure any in-flight mojo calls get run.
+  base::RunLoop().RunUntilIdle();
+  // Expect no report since the trial is not allowed.
+  reporting_service_test_helper()->ExpectNoRequests(reporting_service());
+}
diff --git a/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc b/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
deleted file mode 100644
index b89f6df..0000000
--- a/chrome/browser/net/trial_comparison_cert_verifier_unittest.cc
+++ /dev/null
@@ -1,1730 +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/net/trial_comparison_cert_verifier.h"
-
-#include "base/bind.h"
-#include "base/run_loop.h"
-#include "base/task/post_task.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "build/build_config.h"
-#include "chrome/browser/safe_browsing/certificate_reporting_service_factory.h"
-#include "chrome/browser/safe_browsing/certificate_reporting_service_test_utils.h"
-#include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
-#include "chrome/browser/ssl/cert_logger.pb.h"
-#include "chrome/common/chrome_features.h"
-#include "chrome/common/chrome_paths.h"
-#include "chrome/test/base/testing_browser_process.h"
-#include "chrome/test/base/testing_profile.h"
-#include "chrome/test/base/testing_profile_manager.h"
-#include "components/safe_browsing/common/safe_browsing_prefs.h"
-#include "components/sync_preferences/testing_pref_service_syncable.h"
-#include "content/public/browser/browser_task_traits.h"
-#include "content/public/test/test_browser_thread_bundle.h"
-#include "content/public/test/test_utils.h"
-#include "crypto/sha2.h"
-#include "net/base/net_errors.h"
-#include "net/base/test_completion_callback.h"
-#include "net/cert/cert_verify_proc.h"
-#include "net/cert/cert_verify_result.h"
-#include "net/cert/ev_root_ca_metadata.h"
-#include "net/cert/x509_certificate.h"
-#include "net/cert/x509_util.h"
-#include "net/log/net_log_with_source.h"
-#include "net/socket/socket_test_util.h"
-#include "net/test/cert_test_util.h"
-#include "net/test/gtest_util.h"
-#include "net/test/test_data_directory.h"
-#include "net/url_request/url_request_filter.h"
-#include "net/url_request/url_request_test_util.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using certificate_reporting_test_utils::CertificateReportingServiceTestHelper;
-using certificate_reporting_test_utils::ReportExpectation;
-using certificate_reporting_test_utils::RetryStatus;
-using net::test::IsError;
-using testing::_;
-using testing::Return;
-using testing::SetArgPointee;
-
-namespace {
-
-MATCHER_P(CertChainMatches, expected_cert, "") {
-  net::CertificateList actual_certs =
-      net::X509Certificate::CreateCertificateListFromBytes(
-          arg.data(), arg.size(),
-          net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
-  if (actual_certs.empty()) {
-    *result_listener << "failed to parse arg";
-    return false;
-  }
-  std::vector<std::string> actual_der_certs;
-  for (const auto& cert : actual_certs) {
-    actual_der_certs.emplace_back(
-        net::x509_util::CryptoBufferAsStringPiece(cert->cert_buffer()));
-  }
-
-  std::vector<std::string> expected_der_certs;
-  expected_der_certs.emplace_back(
-      net::x509_util::CryptoBufferAsStringPiece(expected_cert->cert_buffer()));
-  for (const auto& buffer : expected_cert->intermediate_buffers()) {
-    expected_der_certs.emplace_back(
-        net::x509_util::CryptoBufferAsStringPiece(buffer.get()));
-  }
-
-  return actual_der_certs == expected_der_certs;
-}
-
-// Fake CertVerifyProc that sets the CertVerifyResult to a given value for
-// all certificates that are Verify()'d
-class FakeCertVerifyProc : public net::CertVerifyProc {
- public:
-  FakeCertVerifyProc(const int result_error,
-                     const net::CertVerifyResult& result)
-      : result_error_(result_error), result_(result) {}
-
-  void WaitForVerifyCall() {
-    verify_called_.WaitForResult();
-    // Ensure MultiThreadedCertVerifier OnJobCompleted task has a chance to run.
-    content::RunAllTasksUntilIdle();
-  }
-
-  // CertVerifyProc implementation:
-  bool SupportsAdditionalTrustAnchors() const override { return false; }
-
- protected:
-  ~FakeCertVerifyProc() override = default;
-
- private:
-  int VerifyInternal(net::X509Certificate* cert,
-                     const std::string& hostname,
-                     const std::string& ocsp_response,
-                     int flags,
-                     net::CRLSet* crl_set,
-                     const net::CertificateList& additional_trust_anchors,
-                     net::CertVerifyResult* verify_result) override;
-
-  const int result_error_;
-  const net::CertVerifyResult result_;
-  net::TestClosure verify_called_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeCertVerifyProc);
-};
-
-int FakeCertVerifyProc::VerifyInternal(
-    net::X509Certificate* cert,
-    const std::string& hostname,
-    const std::string& ocsp_response,
-    int flags,
-    net::CRLSet* crl_set,
-    const net::CertificateList& additional_trust_anchors,
-    net::CertVerifyResult* verify_result) {
-  *verify_result = result_;
-  base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI})
-      ->PostTask(FROM_HERE, verify_called_.closure());
-  return result_error_;
-}
-
-// Fake CertVerifyProc that causes a failure if it is called.
-class NotCalledCertVerifyProc : public net::CertVerifyProc {
- public:
-  NotCalledCertVerifyProc() = default;
-
-  // CertVerifyProc implementation:
-  bool SupportsAdditionalTrustAnchors() const override { return false; }
-
- protected:
-  ~NotCalledCertVerifyProc() override = default;
-
- private:
-  int VerifyInternal(net::X509Certificate* cert,
-                     const std::string& hostname,
-                     const std::string& ocsp_response,
-                     int flags,
-                     net::CRLSet* crl_set,
-                     const net::CertificateList& additional_trust_anchors,
-                     net::CertVerifyResult* verify_result) override;
-
-  DISALLOW_COPY_AND_ASSIGN(NotCalledCertVerifyProc);
-};
-
-int NotCalledCertVerifyProc::VerifyInternal(
-    net::X509Certificate* cert,
-    const std::string& hostname,
-    const std::string& ocsp_response,
-    int flags,
-    net::CRLSet* crl_set,
-    const net::CertificateList& additional_trust_anchors,
-    net::CertVerifyResult* verify_result) {
-  ADD_FAILURE() << "NotCalledCertVerifyProc was called!";
-  return net::ERR_UNEXPECTED;
-}
-
-void NotCalledCallback(int error) {
-  ADD_FAILURE() << "NotCalledCallback was called with error code " << error;
-}
-
-class MockCertVerifyProc : public net::CertVerifyProc {
- public:
-  MockCertVerifyProc() = default;
-  // CertVerifyProc implementation:
-  bool SupportsAdditionalTrustAnchors() const override { return false; }
-  MOCK_METHOD7(VerifyInternal,
-               int(net::X509Certificate* cert,
-                   const std::string& hostname,
-                   const std::string& ocsp_response,
-                   int flags,
-                   net::CRLSet* crl_set,
-                   const net::CertificateList& additional_trust_anchors,
-                   net::CertVerifyResult* verify_result));
-
- protected:
-  ~MockCertVerifyProc() override = default;
-
-  DISALLOW_COPY_AND_ASSIGN(MockCertVerifyProc);
-};
-
-}  // namespace
-
-class TrialComparisonCertVerifierTest : public testing::Test {
- public:
-  TrialComparisonCertVerifierTest()
-      // UI and IO message loops run on the same thread for the test. (Makes
-      // the test logic simpler, though doesn't fully exercise the
-      // ThreadCheckers.)
-      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
-
-  void SetUp() override {
-    cert_chain_1_ = CreateCertificateChainFromFile(
-        net::GetTestCertsDirectory(), "multi-root-chain1.pem",
-        net::X509Certificate::FORMAT_AUTO);
-    ASSERT_TRUE(cert_chain_1_);
-    leaf_cert_1_ = net::X509Certificate::CreateFromBuffer(
-        bssl::UpRef(cert_chain_1_->cert_buffer()), {});
-    ASSERT_TRUE(leaf_cert_1_);
-    cert_chain_2_ = CreateCertificateChainFromFile(
-        net::GetTestCertsDirectory(), "multi-root-chain2.pem",
-        net::X509Certificate::FORMAT_AUTO);
-    ASSERT_TRUE(cert_chain_2_);
-
-    reporting_service_test_helper_ =
-        base::MakeRefCounted<CertificateReportingServiceTestHelper>();
-    CertificateReportingServiceFactory::GetInstance()
-        ->SetReportEncryptionParamsForTesting(
-            reporting_service_test_helper()->server_public_key(),
-            reporting_service_test_helper()->server_public_key_version());
-    CertificateReportingServiceFactory::GetInstance()
-        ->SetURLLoaderFactoryForTesting(reporting_service_test_helper_);
-    reporting_service_test_helper()->SetFailureMode(
-        certificate_reporting_test_utils::REPORTS_SUCCESSFUL);
-
-    system_request_context_getter_ =
-        base::MakeRefCounted<net::TestURLRequestContextGetter>(
-            base::CreateSingleThreadTaskRunnerWithTraits(
-                {content::BrowserThread::IO}));
-    TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(
-        system_request_context_getter_.get());
-    sb_service_ =
-        base::MakeRefCounted<safe_browsing::TestSafeBrowsingService>();
-    TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(
-        sb_service_.get());
-    g_browser_process->safe_browsing_service()->Initialize();
-
-    profile_manager_ = std::make_unique<TestingProfileManager>(
-        TestingBrowserProcess::GetGlobal());
-    ASSERT_TRUE(profile_manager_->SetUp());
-    ASSERT_TRUE(g_browser_process->profile_manager());
-    profile_ = profile_manager_->CreateTestingProfile("profile1");
-
-    // Enable feature and SBER pref.
-    TrialComparisonCertVerifier::SetFakeOfficialBuildForTesting();
-    scoped_feature_ = std::make_unique<base::test::ScopedFeatureList>();
-    scoped_feature_->InitAndEnableFeature(
-        features::kCertDualVerificationTrialFeature);
-    safe_browsing::SetExtendedReportingPref(pref_service(), true);
-
-    // Initialize CertificateReportingService
-    ASSERT_TRUE(service());
-    base::RunLoop().RunUntilIdle();
-  }
-
-  void TearDown() override {
-    if (TestingBrowserProcess::GetGlobal()->safe_browsing_service()) {
-      TestingBrowserProcess::GetGlobal()->safe_browsing_service()->ShutDown();
-      TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(nullptr);
-    }
-    TestingBrowserProcess::GetGlobal()->SetSystemRequestContext(nullptr);
-    system_request_context_getter_ = nullptr;
-    net::URLRequestFilter::GetInstance()->ClearHandlers();
-  }
-
-  TestingProfile* profile() { return profile_; }
-  sync_preferences::TestingPrefServiceSyncable* pref_service() {
-    return profile_->GetTestingPrefService();
-  }
-
-  CertificateReportingServiceTestHelper* reporting_service_test_helper() {
-    return reporting_service_test_helper_.get();
-  }
-
-  CertificateReportingService* service() const {
-    return CertificateReportingServiceFactory::GetForBrowserContext(profile_);
-  }
-
- protected:
-  scoped_refptr<net::X509Certificate> cert_chain_1_;
-  scoped_refptr<net::X509Certificate> cert_chain_2_;
-  scoped_refptr<net::X509Certificate> leaf_cert_1_;
-  base::HistogramTester histograms_;
-  std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_;
-
- private:
-  content::TestBrowserThreadBundle thread_bundle_;
-  scoped_refptr<safe_browsing::SafeBrowsingService> sb_service_;
-  scoped_refptr<net::URLRequestContextGetter> system_request_context_getter_;
-  std::unique_ptr<TestingProfileManager> profile_manager_;
-  TestingProfile* profile_;
-
-  scoped_refptr<CertificateReportingServiceTestHelper>
-      reporting_service_test_helper_;
-};
-
-TEST_F(TrialComparisonCertVerifierTest, NotOptedIn) {
-  // Disable SBER pref.
-  safe_browsing::SetExtendedReportingPref(pref_service(), false);
-
-  net::CertVerifyResult dummy_result;
-  dummy_result.verified_cert = cert_chain_1_;
-  TrialComparisonCertVerifier verifier(
-      profile(),
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, dummy_result),
-      base::MakeRefCounted<NotCalledCertVerifyProc>());
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  // Wait for CheckTrialEligibility task to finish.
-  content::RunAllTasksUntilIdle();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  // Primary verifier should have ran, trial verifier should not have.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, FeatureDisabled) {
-  // Disable feature.
-  scoped_feature_.reset();
-
-  net::CertVerifyResult dummy_result;
-  dummy_result.verified_cert = cert_chain_1_;
-  TrialComparisonCertVerifier verifier(
-      profile(),
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, dummy_result),
-      base::MakeRefCounted<NotCalledCertVerifyProc>());
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  // Wait for CheckTrialEligibility task to finish.
-  content::RunAllTasksUntilIdle();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  // Primary verifier should have ran, trial verifier should not have.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, SameResult) {
-  net::CertVerifyResult dummy_result;
-  dummy_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, dummy_result);
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, dummy_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample("Net.CertVerifier_TrialComparisonResult",
-                                 TrialComparisonCertVerifier::kEqual, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, Incognito) {
-  net::CertVerifyResult dummy_result;
-  dummy_result.verified_cert = cert_chain_1_;
-  TrialComparisonCertVerifier verifier(
-      profile()->GetOffTheRecordProfile(),  // Use an incognito Profile.
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, dummy_result),
-      base::MakeRefCounted<NotCalledCertVerifyProc>());
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  // Wait for CheckTrialEligibility task to finish.
-  content::RunAllTasksUntilIdle();
-
-  // Primary verifier should have ran, trial verifier should not have, control
-  // histogram also should not be recorded for incognito.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierErrorSecondaryOk) {
-  // Primary verifier returns an error status.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
-                                               primary_result);
-
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_DATE_INVALID));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-
-  chrome_browser_ssl::CertLoggerRequest report;
-  ASSERT_TRUE(report.ParseFromString(full_reports[0]));
-
-  ASSERT_EQ(1, report.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
-            report.cert_error()[0]);
-  EXPECT_EQ(0, report.cert_status_size());
-
-  ASSERT_TRUE(report.has_features_info());
-  ASSERT_TRUE(report.features_info().has_trial_verification_info());
-  const chrome_browser_ssl::TrialVerificationInfo& trial_info =
-      report.features_info().trial_verification_info();
-  ASSERT_EQ(0, trial_info.cert_error_size());
-  EXPECT_EQ(0, trial_info.cert_status_size());
-
-  EXPECT_THAT(report.unverified_cert_chain(), CertChainMatches(leaf_cert_1_));
-  EXPECT_THAT(report.cert_chain(), CertChainMatches(cert_chain_1_));
-  EXPECT_THAT(trial_info.cert_chain(), CertChainMatches(cert_chain_1_));
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierOkSecondaryError) {
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, primary_result);
-
-  // Trial verifier returns an error status.
-  net::CertVerifyResult secondary_result;
-  secondary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
-  secondary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
-                                               secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-
-  chrome_browser_ssl::CertLoggerRequest report;
-  ASSERT_TRUE(report.ParseFromString(full_reports[0]));
-
-  EXPECT_EQ(0, report.cert_error_size());
-  EXPECT_EQ(0, report.cert_status_size());
-
-  ASSERT_TRUE(report.has_features_info());
-  ASSERT_TRUE(report.features_info().has_trial_verification_info());
-  const chrome_browser_ssl::TrialVerificationInfo& trial_info =
-      report.features_info().trial_verification_info();
-  ASSERT_EQ(1, trial_info.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
-            trial_info.cert_error()[0]);
-  EXPECT_EQ(0, trial_info.cert_status_size());
-
-  EXPECT_THAT(report.unverified_cert_chain(), CertChainMatches(leaf_cert_1_));
-  EXPECT_THAT(report.cert_chain(), CertChainMatches(cert_chain_1_));
-  EXPECT_THAT(trial_info.cert_chain(), CertChainMatches(cert_chain_1_));
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, BothVerifiersDifferentErrors) {
-  // Primary verifier returns an error status.
-  net::CertVerifyResult primary_result;
-  primary_result.cert_status = net::CERT_STATUS_VALIDITY_TOO_LONG;
-  primary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_VALIDITY_TOO_LONG,
-                                               primary_result);
-
-  // Trial verifier returns a different error status.
-  net::CertVerifyResult secondary_result;
-  secondary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
-  secondary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
-                                               secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_VALIDITY_TOO_LONG));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-
-  chrome_browser_ssl::CertLoggerRequest report;
-  ASSERT_TRUE(report.ParseFromString(full_reports[0]));
-
-  ASSERT_EQ(1, report.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_VALIDITY_TOO_LONG,
-            report.cert_error()[0]);
-  EXPECT_EQ(0, report.cert_status_size());
-
-  ASSERT_TRUE(report.has_features_info());
-  ASSERT_TRUE(report.features_info().has_trial_verification_info());
-  const chrome_browser_ssl::TrialVerificationInfo& trial_info =
-      report.features_info().trial_verification_info();
-  ASSERT_EQ(1, trial_info.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
-            trial_info.cert_error()[0]);
-  EXPECT_EQ(0, trial_info.cert_status_size());
-
-  EXPECT_THAT(report.unverified_cert_chain(), CertChainMatches(leaf_cert_1_));
-  EXPECT_THAT(report.cert_chain(), CertChainMatches(cert_chain_1_));
-  EXPECT_THAT(trial_info.cert_chain(), CertChainMatches(cert_chain_1_));
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kBothErrorDifferentDetails, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest,
-       BothVerifiersOkDifferentVerifiedChains) {
-  // Primary verifier returns chain1 regardless of arguments.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, primary_result);
-
-  // Trial verifier returns a different verified cert chain.
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain_2_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-
-  chrome_browser_ssl::CertLoggerRequest report;
-  ASSERT_TRUE(report.ParseFromString(full_reports[0]));
-
-  EXPECT_EQ(0, report.cert_error_size());
-  EXPECT_EQ(0, report.cert_status_size());
-
-  ASSERT_TRUE(report.has_features_info());
-  ASSERT_TRUE(report.features_info().has_trial_verification_info());
-  const chrome_browser_ssl::TrialVerificationInfo& trial_info =
-      report.features_info().trial_verification_info();
-  EXPECT_EQ(0, trial_info.cert_error_size());
-  EXPECT_EQ(0, trial_info.cert_status_size());
-  EXPECT_EQ(0, trial_info.verify_flags_size());
-
-  EXPECT_THAT(report.unverified_cert_chain(), CertChainMatches(leaf_cert_1_));
-  EXPECT_THAT(report.cert_chain(), CertChainMatches(cert_chain_1_));
-  EXPECT_THAT(trial_info.cert_chain(), CertChainMatches(cert_chain_2_));
-
-  // Main CertVerifier_Job_Latency should have 2 counts since the
-  // primary_reverifier was used.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
-  // CertVerifier_Job_Latency_TrialPrimary only has 1 count since
-  // primary_reverifier doesn't use the same CertVerifier.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest,
-       BothVerifiersOkDifferentVerifiedChainsEqualAfterReverification) {
-  net::CertVerifyResult chain1_result;
-  chain1_result.verified_cert = cert_chain_1_;
-  net::CertVerifyResult chain2_result;
-  chain2_result.verified_cert = cert_chain_2_;
-
-  scoped_refptr<MockCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<MockCertVerifyProc>();
-  // Primary verifier returns ok status and chain1 if verifying the leaf alone.
-  EXPECT_CALL(*verify_proc1,
-              VerifyInternal(leaf_cert_1_.get(), _, _, _, _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<6>(chain1_result), Return(net::OK)));
-  // Primary verifier returns ok status and chain2 if verifying chain2.
-  EXPECT_CALL(*verify_proc1,
-              VerifyInternal(cert_chain_2_.get(), _, _, _, _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<6>(chain2_result), Return(net::OK)));
-
-  // Trial verifier returns ok status and chain2.
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, chain2_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  // Main CertVerifier_Job_Latency should have 2 counts since the
-  // primary_reverifier was used.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
-  // CertVerifier_Job_Latency_TrialPrimary only has 1 count since
-  // primary_reverifier doesn't use the same CertVerifier.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kIgnoredDifferentPathReVerifiesEquivalent,
-      1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest,
-       DifferentVerifiedChainsIgnorableDifferenceAfterReverification) {
-  base::FilePath certs_dir;
-  ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &certs_dir));
-  certs_dir = certs_dir.AppendASCII("net")
-                  .AppendASCII("trial_comparison_cert_verifier_unittest")
-                  .AppendASCII("target-multiple-policies");
-  scoped_refptr<net::X509Certificate> cert_chain =
-      CreateCertificateChainFromFile(certs_dir, "chain.pem",
-                                     net::X509Certificate::FORMAT_AUTO);
-  ASSERT_TRUE(cert_chain);
-  ASSERT_EQ(2U, cert_chain->intermediate_buffers().size());
-
-  scoped_refptr<net::X509Certificate> leaf =
-      net::X509Certificate::CreateFromBuffer(
-          bssl::UpRef(cert_chain->cert_buffer()), {});
-  ASSERT_TRUE(leaf);
-
-  // Chain with the same leaf and different root. This is not a valid chain, but
-  // doesn't matter for the unittest since this uses mock CertVerifyProcs.
-  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
-  intermediates.push_back(
-      bssl::UpRef(cert_chain_1_->intermediate_buffers().back().get()));
-  scoped_refptr<net::X509Certificate> different_chain =
-      net::X509Certificate::CreateFromBuffer(
-          bssl::UpRef(cert_chain->cert_buffer()), std::move(intermediates));
-  ASSERT_TRUE(different_chain);
-
-  net::CertVerifyResult different_chain_result;
-  different_chain_result.verified_cert = different_chain;
-
-  net::CertVerifyResult nonev_chain_result;
-  nonev_chain_result.verified_cert = cert_chain;
-
-  net::CertVerifyResult ev_chain_result;
-  ev_chain_result.verified_cert = cert_chain;
-  ev_chain_result.cert_status =
-      net::CERT_STATUS_IS_EV | net::CERT_STATUS_REV_CHECKING_ENABLED;
-
-  net::SHA256HashValue root_fingerprint;
-  crypto::SHA256HashString(net::x509_util::CryptoBufferAsStringPiece(
-                               cert_chain->intermediate_buffers().back().get()),
-                           root_fingerprint.data,
-                           sizeof(root_fingerprint.data));
-  // Both policies in the target are EV policies, but only 1.2.6.7 is valid for
-  // the root in cert_chain.
-  net::ScopedTestEVPolicy scoped_ev_policy_1(
-      net::EVRootCAMetadata::GetInstance(), root_fingerprint, "1.2.6.7");
-  net::ScopedTestEVPolicy scoped_ev_policy_2(
-      net::EVRootCAMetadata::GetInstance(), net::SHA256HashValue(), "1.2.3.4");
-
-  scoped_refptr<MockCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<MockCertVerifyProc>();
-  // Primary verifier returns ok status and different_chain if verifying leaf
-  // alone.
-  EXPECT_CALL(*verify_proc1, VerifyInternal(leaf.get(), _, _, _, _, _, _))
-      .WillRepeatedly(
-          DoAll(SetArgPointee<6>(different_chain_result), Return(net::OK)));
-  // Primary verifier returns ok status and nonev_chain_result if verifying
-  // cert_chain.
-  EXPECT_CALL(*verify_proc1, VerifyInternal(cert_chain.get(), _, _, _, _, _, _))
-      .WillRepeatedly(
-          DoAll(SetArgPointee<6>(nonev_chain_result), Return(net::OK)));
-
-  // Trial verifier returns ok status and ev_chain_result.
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, ev_chain_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf, "test.example", 0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  // Main CertVerifier_Job_Latency should have 2 counts since the
-  // primary_reverifier was used.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
-  // CertVerifier_Job_Latency_TrialPrimary only has 1 count since
-  // primary_reverifier doesn't use the same CertVerifier.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kIgnoredDifferentPathReVerifiesEquivalent,
-      1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, BothVerifiersOkDifferentCertStatus) {
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status =
-      net::CERT_STATUS_IS_EV | net::CERT_STATUS_REV_CHECKING_ENABLED;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, primary_result);
-
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain_1_;
-  secondary_result.cert_status = net::CERT_STATUS_CT_COMPLIANCE_FAILED;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-  net::CertVerifier::Config config;
-  config.enable_rev_checking = true;
-  config.enable_sha1_local_anchors = true;
-  verifier.SetConfig(config);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-
-  chrome_browser_ssl::CertLoggerRequest report;
-  ASSERT_TRUE(report.ParseFromString(full_reports[0]));
-
-  EXPECT_EQ(0, report.cert_error_size());
-  ASSERT_EQ(2, report.cert_status_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::STATUS_IS_EV,
-            report.cert_status()[0]);
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::STATUS_REV_CHECKING_ENABLED,
-            report.cert_status()[1]);
-
-  ASSERT_TRUE(report.has_features_info());
-  ASSERT_TRUE(report.features_info().has_trial_verification_info());
-  const chrome_browser_ssl::TrialVerificationInfo& trial_info =
-      report.features_info().trial_verification_info();
-  EXPECT_EQ(0, trial_info.cert_error_size());
-  ASSERT_EQ(1, trial_info.cert_status_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::STATUS_CT_COMPLIANCE_FAILED,
-            trial_info.cert_status()[0]);
-
-  EXPECT_THAT(
-      trial_info.verify_flags(),
-      testing::UnorderedElementsAre(chrome_browser_ssl::TrialVerificationInfo::
-                                        VERIFY_REV_CHECKING_ENABLED,
-                                    chrome_browser_ssl::TrialVerificationInfo::
-                                        VERIFY_ENABLE_SHA1_LOCAL_ANCHORS));
-
-  EXPECT_THAT(report.unverified_cert_chain(), CertChainMatches(leaf_cert_1_));
-  EXPECT_THAT(report.cert_chain(), CertChainMatches(cert_chain_1_));
-  EXPECT_THAT(trial_info.cert_chain(), CertChainMatches(cert_chain_1_));
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, Coalescing) {
-  // Primary verifier returns an error status.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
-                                               primary_result);
-
-  // Trial verifier has ok status.
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-
-  // Start first verification request.
-  net::CertVerifyResult result_1;
-  std::unique_ptr<net::CertVerifier::Request> request_1;
-  net::TestCompletionCallback callback_1;
-  int error = verifier.Verify(params, &result_1, callback_1.callback(),
-                              &request_1, net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request_1);
-
-  // Start second verification request with same params.
-  net::CertVerifyResult result_2;
-  std::unique_ptr<net::CertVerifier::Request> request_2;
-  net::TestCompletionCallback callback_2;
-  error = verifier.Verify(params, &result_2, callback_2.callback(), &request_2,
-                          net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request_2);
-
-  // Both callbacks should be called with same error code.
-  error = callback_1.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_DATE_INVALID));
-  error = callback_2.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_DATE_INVALID));
-
-  // Trial verifier should run.
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect a single report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-
-  chrome_browser_ssl::CertLoggerRequest report;
-  ASSERT_TRUE(report.ParseFromString(full_reports[0]));
-
-  ASSERT_EQ(1, report.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
-            report.cert_error()[0]);
-  EXPECT_EQ(0, report.cert_status_size());
-
-  ASSERT_TRUE(report.has_features_info());
-  ASSERT_TRUE(report.features_info().has_trial_verification_info());
-  const chrome_browser_ssl::TrialVerificationInfo& trial_info =
-      report.features_info().trial_verification_info();
-  ASSERT_EQ(0, trial_info.cert_error_size());
-  EXPECT_EQ(0, trial_info.cert_status_size());
-
-  EXPECT_THAT(report.unverified_cert_chain(), CertChainMatches(leaf_cert_1_));
-  EXPECT_THAT(report.cert_chain(), CertChainMatches(cert_chain_1_));
-  EXPECT_THAT(trial_info.cert_chain(), CertChainMatches(cert_chain_1_));
-
-  // Only one verification should be done by primary verifier.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  // Only one verification should be done by secondary verifier.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, CancelledDuringPrimaryVerification) {
-  // Primary verifier returns an error status.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
-                                               primary_result);
-
-  // Trial verifier has ok status.
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error =
-      verifier.Verify(params, &result, base::BindRepeating(&NotCalledCallback),
-                      &request, net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  // Delete the request, cancelling it.
-  request.reset();
-
-  // The callback to the main verifier does not run. However, the verification
-  // still completes in the background and triggers the trial verification.
-
-  // Trial verifier should still run.
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-
-  chrome_browser_ssl::CertLoggerRequest report;
-  ASSERT_TRUE(report.ParseFromString(full_reports[0]));
-
-  ASSERT_EQ(1, report.cert_error_size());
-  EXPECT_EQ(chrome_browser_ssl::CertLoggerRequest::ERR_CERT_DATE_INVALID,
-            report.cert_error()[0]);
-  EXPECT_EQ(0, report.cert_status_size());
-
-  ASSERT_TRUE(report.has_features_info());
-  ASSERT_TRUE(report.features_info().has_trial_verification_info());
-  const chrome_browser_ssl::TrialVerificationInfo& trial_info =
-      report.features_info().trial_verification_info();
-  ASSERT_EQ(0, trial_info.cert_error_size());
-  EXPECT_EQ(0, trial_info.cert_status_size());
-
-  EXPECT_THAT(report.unverified_cert_chain(), CertChainMatches(leaf_cert_1_));
-  EXPECT_THAT(report.cert_chain(), CertChainMatches(cert_chain_1_));
-  EXPECT_THAT(trial_info.cert_chain(), CertChainMatches(cert_chain_1_));
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, DeletedDuringPrimaryVerification) {
-  // Primary verifier returns an error status.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
-                                               primary_result);
-
-  auto verifier = std::make_unique<TrialComparisonCertVerifier>(
-      profile(), verify_proc1, base::MakeRefCounted<NotCalledCertVerifyProc>());
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error =
-      verifier->Verify(params, &result, base::BindRepeating(&NotCalledCallback),
-                       &request, net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  // Delete the TrialComparisonCertVerifier.
-  verifier.reset();
-
-  // The callback to the main verifier does not run. The verification task
-  // still completes in the background, but since the CertVerifier has been
-  // deleted, the result is ignored.
-
-  // Wait for any tasks to finish.
-  content::RunAllTasksUntilIdle();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  // Histograms should not be recorded.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, DeletedBeforeTrialVerificationStarted) {
-  // Primary verifier returns an error status.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
-                                               primary_result);
-
-  // Trial verifier has ok status.
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<NotCalledCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<NotCalledCertVerifyProc>();
-
-  auto verifier = std::make_unique<TrialComparisonCertVerifier>(
-      profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier->Verify(params, &result, callback.callback(), &request,
-                               net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  // Wait for primary verifier to finish.
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_DATE_INVALID));
-
-  // Delete the TrialComparisonCertVerifier.
-  verifier.reset();
-
-  // Trial verification has not yet started, as it was waiting on the profile
-  // to determine whether or not it would be permitted.
-
-  // Wait for any tasks to finish.
-  content::RunAllTasksUntilIdle();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  // The actual verification job should be completed, but neither the
-  // primary nor secondary job metrics should be recorded, as the verifier
-  // was deleted prior to determining whether a trial verification would be
-  // run.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, DeletedAfterTrialVerificationStarted) {
-  // Primary verifier returns an error status.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  primary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
-                                               primary_result);
-
-  // Trial verifier has ok status.
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, secondary_result);
-
-  auto verifier = std::make_unique<TrialComparisonCertVerifier>(
-      profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier->Verify(params, &result, callback.callback(), &request,
-                               net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  // Wait for primary verifier to finish.
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_DATE_INVALID));
-
-  // Allow the lookup on the UI thread for the profile to determine trial
-  // status.
-  std::unique_ptr<base::RunLoop> run_loop(std::make_unique<base::RunLoop>());
-  base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::UI})
-      ->PostTask(FROM_HERE, run_loop->QuitClosure());
-  run_loop->Run();
-
-  // Allow recording the metrics back on the IO thread, and starting the
-  // second verification, to run.
-  run_loop = std::make_unique<base::RunLoop>();
-  base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})
-      ->PostTask(FROM_HERE, run_loop->QuitClosure());
-  run_loop->Run();
-
-  // Delete the TrialComparisonCertVerifier. The trial verification is still
-  // running on the task scheduler (or, depending on timing, has posted back
-  // to the IO thread after the Quit event).
-  verifier.reset();
-
-  // The callback to the trial verifier does not run. The verification task
-  // still completes in the background, but since the CertVerifier has been
-  // deleted, the result is ignored.
-
-  // Wait for any tasks to finish.
-  content::RunAllTasksUntilIdle();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  // Histograms for trial verifier should not be recorded.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               0);
-  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
-}
-
-TEST_F(TrialComparisonCertVerifierTest,
-       PrimaryVerifierOkSecondaryErrorUmaOnly) {
-  // Enable feature with uma_only flag.
-  scoped_feature_.reset();
-  scoped_feature_ = std::make_unique<base::test::ScopedFeatureList>();
-  scoped_feature_->InitAndEnableFeatureWithParameters(
-      features::kCertDualVerificationTrialFeature, {{"uma_only", "true"}});
-
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, primary_result);
-
-  // Trial verifier returns an error status.
-  net::CertVerifyResult secondary_result;
-  secondary_result.cert_status = net::CERT_STATUS_DATE_INVALID;
-  secondary_result.verified_cert = cert_chain_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_DATE_INVALID,
-                                               secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Wait for any tasks to finish.
-  content::RunAllTasksUntilIdle();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  // Should still have UMA logs.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, MacUndesiredRevocationChecking) {
-  net::CertVerifyResult revoked_result;
-  revoked_result.verified_cert = cert_chain_1_;
-  revoked_result.cert_status = net::CERT_STATUS_REVOKED;
-
-  net::CertVerifyResult ok_result;
-  ok_result.verified_cert = cert_chain_1_;
-
-  // Primary verifier returns an error status.
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_REVOKED,
-                                               revoked_result);
-
-  scoped_refptr<MockCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<MockCertVerifyProc>();
-  // Secondary verifier returns ok status...
-  EXPECT_CALL(*verify_proc2, VerifyInternal(_, _, _, _, _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<6>(ok_result), Return(net::OK)));
-  // ...unless it was called with REV_CHECKING_ENABLED.
-  EXPECT_CALL(
-      *verify_proc2,
-      VerifyInternal(_, _, _, net::CertVerifyProc::VERIFY_REV_CHECKING_ENABLED,
-                     _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<6>(revoked_result),
-                            Return(net::ERR_CERT_REVOKED)));
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_REVOKED));
-
-  content::RunAllTasksUntilIdle();
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-#if defined(OS_MACOSX)
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  // Secondary should have been called twice
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               2);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kIgnoredMacUndesiredRevocationChecking, 1);
-#else
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-#endif
-}
-
-TEST_F(TrialComparisonCertVerifierTest, PrimaryRevokedSecondaryOk) {
-  net::CertVerifyResult revoked_result;
-  revoked_result.verified_cert = cert_chain_1_;
-  revoked_result.cert_status = net::CERT_STATUS_REVOKED;
-
-  net::CertVerifyResult ok_result;
-  ok_result.verified_cert = cert_chain_1_;
-
-  // Primary verifier returns an error status.
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_REVOKED,
-                                               revoked_result);
-
-  // Secondary verifier returns ok status regardless of whether
-  // REV_CHECKING_ENABLED was passed.
-  scoped_refptr<MockCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<MockCertVerifyProc>();
-  EXPECT_CALL(*verify_proc2, VerifyInternal(_, _, _, _, _, _, _))
-      .WillRepeatedly(DoAll(SetArgPointee<6>(ok_result), Return(net::OK)));
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::ERR_CERT_REVOKED));
-
-  content::RunAllTasksUntilIdle();
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-#if defined(OS_MACOSX)
-  // Secondary should have been called twice on mac due to attempting the
-  // kIgnoredMacUndesiredRevocationChecking workaround.
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               2);
-#else
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-
-#endif
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-}
-
-TEST_F(TrialComparisonCertVerifierTest, MultipleEVPolicies) {
-  base::FilePath certs_dir;
-  ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &certs_dir));
-  certs_dir = certs_dir.AppendASCII("net")
-                  .AppendASCII("trial_comparison_cert_verifier_unittest")
-                  .AppendASCII("target-multiple-policies");
-  scoped_refptr<net::X509Certificate> cert_chain =
-      CreateCertificateChainFromFile(certs_dir, "chain.pem",
-                                     net::X509Certificate::FORMAT_AUTO);
-  ASSERT_TRUE(cert_chain);
-  ASSERT_EQ(2U, cert_chain->intermediate_buffers().size());
-
-  net::SHA256HashValue root_fingerprint;
-  crypto::SHA256HashString(net::x509_util::CryptoBufferAsStringPiece(
-                               cert_chain->intermediate_buffers().back().get()),
-                           root_fingerprint.data,
-                           sizeof(root_fingerprint.data));
-
-  // Both policies in the target are EV policies, but only 1.2.6.7 is valid for
-  // the root in this chain.
-  net::ScopedTestEVPolicy scoped_ev_policy_1(
-      net::EVRootCAMetadata::GetInstance(), root_fingerprint, "1.2.6.7");
-  net::ScopedTestEVPolicy scoped_ev_policy_2(
-      net::EVRootCAMetadata::GetInstance(), net::SHA256HashValue(), "1.2.3.4");
-
-  // Both verifiers return OK, but secondary returns EV status.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, primary_result);
-
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain;
-  secondary_result.cert_status =
-      net::CERT_STATUS_IS_EV | net::CERT_STATUS_REV_CHECKING_ENABLED;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kIgnoredMultipleEVPoliciesAndOneMatchesRoot,
-      1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, MultipleEVPoliciesNoneValidForRoot) {
-  base::FilePath certs_dir;
-  ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &certs_dir));
-  certs_dir = certs_dir.AppendASCII("net")
-                  .AppendASCII("trial_comparison_cert_verifier_unittest")
-                  .AppendASCII("target-multiple-policies");
-  scoped_refptr<net::X509Certificate> cert_chain =
-      CreateCertificateChainFromFile(certs_dir, "chain.pem",
-                                     net::X509Certificate::FORMAT_AUTO);
-  ASSERT_TRUE(cert_chain);
-
-  // Both policies in the target are EV policies, but neither is valid for the
-  // root in this chain.
-  net::ScopedTestEVPolicy scoped_ev_policy_1(
-      net::EVRootCAMetadata::GetInstance(), {1}, "1.2.6.7");
-  net::ScopedTestEVPolicy scoped_ev_policy_2(
-      net::EVRootCAMetadata::GetInstance(), {2}, "1.2.3.4");
-
-  // Both verifiers return OK, but secondary returns EV status.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, primary_result);
-
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain;
-  secondary_result.cert_status =
-      net::CERT_STATUS_IS_EV | net::CERT_STATUS_REV_CHECKING_ENABLED;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, MultiplePoliciesOnlyOneIsEV) {
-  base::FilePath certs_dir;
-  ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &certs_dir));
-  certs_dir = certs_dir.AppendASCII("net")
-                  .AppendASCII("trial_comparison_cert_verifier_unittest")
-                  .AppendASCII("target-multiple-policies");
-  scoped_refptr<net::X509Certificate> cert_chain =
-      CreateCertificateChainFromFile(certs_dir, "chain.pem",
-                                     net::X509Certificate::FORMAT_AUTO);
-  ASSERT_TRUE(cert_chain);
-  ASSERT_EQ(2U, cert_chain->intermediate_buffers().size());
-
-  net::SHA256HashValue root_fingerprint;
-  crypto::SHA256HashString(net::x509_util::CryptoBufferAsStringPiece(
-                               cert_chain->intermediate_buffers().back().get()),
-                           root_fingerprint.data,
-                           sizeof(root_fingerprint.data));
-
-  // One policy in the target is an EV policy and is valid for the root.
-  net::ScopedTestEVPolicy scoped_ev_policy_1(
-      net::EVRootCAMetadata::GetInstance(), root_fingerprint, "1.2.6.7");
-
-  // Both verifiers return OK, but secondary returns EV status.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = cert_chain;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, primary_result);
-
-  net::CertVerifyResult secondary_result;
-  secondary_result.verified_cert = cert_chain;
-  secondary_result.cert_status =
-      net::CERT_STATUS_IS_EV | net::CERT_STATUS_REV_CHECKING_ENABLED;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect a report.
-  std::vector<std::string> full_reports;
-  reporting_service_test_helper()->WaitForRequestsDestroyed(
-      ReportExpectation::Successful({{"127.0.0.1", RetryStatus::NOT_RETRIED}}),
-      &full_reports);
-
-  ASSERT_EQ(1U, full_reports.size());
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
-}
-
-TEST_F(TrialComparisonCertVerifierTest, LocallyTrustedLeaf) {
-  // Platform verifier verifies the leaf directly.
-  net::CertVerifyResult primary_result;
-  primary_result.verified_cert = leaf_cert_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::OK, primary_result);
-
-  // Trial verifier does not support directly-trusted leaf certs.
-  net::CertVerifyResult secondary_result;
-  secondary_result.cert_status = net::CERT_STATUS_AUTHORITY_INVALID;
-  secondary_result.verified_cert = leaf_cert_1_;
-  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
-      base::MakeRefCounted<FakeCertVerifyProc>(net::ERR_CERT_AUTHORITY_INVALID,
-                                               secondary_result);
-
-  TrialComparisonCertVerifier verifier(profile(), verify_proc1, verify_proc2);
-
-  net::CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1",
-                                          0 /* flags */,
-                                          std::string() /* ocsp_response */);
-  net::CertVerifyResult result;
-  net::TestCompletionCallback callback;
-  std::unique_ptr<net::CertVerifier::Request> request;
-  int error = verifier.Verify(params, &result, callback.callback(), &request,
-                              net::NetLogWithSource());
-  ASSERT_THAT(error, IsError(net::ERR_IO_PENDING));
-  EXPECT_TRUE(request);
-
-  error = callback.WaitForResult();
-  EXPECT_THAT(error, IsError(net::OK));
-
-  verify_proc2->WaitForVerifyCall();
-
-  // Expect no report.
-  reporting_service_test_helper()->ExpectNoRequests(service());
-
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
-  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
-                               1);
-  histograms_.ExpectUniqueSample(
-      "Net.CertVerifier_TrialComparisonResult",
-      TrialComparisonCertVerifier::kIgnoredLocallyTrustedLeaf, 1);
-}
diff --git a/chrome/browser/net/variations_http_headers_browsertest.cc b/chrome/browser/net/variations_http_headers_browsertest.cc
index ced14acf..f105be8 100644
--- a/chrome/browser/net/variations_http_headers_browsertest.cc
+++ b/chrome/browser/net/variations_http_headers_browsertest.cc
@@ -334,35 +334,6 @@
 // Verify in an integration that that the variations header (X-Client-Data) is
 // correctly attached and stripped from network requests that are triggered via
 // a URLFetcher.
-//
-// TODO(juncai): Remove this test when there are no more clients left that use
-// URLFetcher.
-// https://crbug.com/773295
-IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
-                       TestStrippingHeadersFromInternalRequest) {
-  if (base::FeatureList::IsEnabled(network::features::kNetworkService))
-    return;  // URLFetcher doesn't work with network service.
-
-  BlockingURLFetcherDelegate delegate;
-
-  GURL url = GetGoogleRedirectUrl1();
-  std::unique_ptr<net::URLFetcher> fetcher =
-      net::URLFetcher::Create(url, net::URLFetcher::GET, &delegate);
-  net::HttpRequestHeaders headers;
-  variations::AppendVariationHeadersUnknownSignedIn(
-      url, variations::InIncognito::kNo, &headers);
-  fetcher->SetRequestContext(browser()->profile()->GetRequestContext());
-  fetcher->SetExtraRequestHeaders(headers.ToString());
-  fetcher->Start();
-
-  delegate.AwaitResponse();
-
-  EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl1(), "X-Client-Data"));
-  EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl2(), "X-Client-Data"));
-  EXPECT_TRUE(HasReceivedHeader(GetExampleUrl(), "Host"));
-  EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data"));
-}
-
 IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
                        TestStrippingHeadersFromSubresourceRequest) {
   GURL url = server()->GetURL("/simple_page.html");
@@ -383,7 +354,7 @@
   resource_request->url = url;
 
   std::unique_ptr<network::SimpleURLLoader> loader =
-      variations::CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn(
+      variations::CreateSimpleURLLoaderWithVariationsHeaderUnknownSignedIn(
           std::move(resource_request), variations::InIncognito::kNo,
           TRAFFIC_ANNOTATION_FOR_TESTS);
 
@@ -414,7 +385,7 @@
   resource_request->url = url;
 
   std::unique_ptr<network::SimpleURLLoader> loader =
-      variations::CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn(
+      variations::CreateSimpleURLLoaderWithVariationsHeaderUnknownSignedIn(
           std::move(resource_request), variations::InIncognito::kNo,
           TRAFFIC_ANNOTATION_FOR_TESTS);
 
diff --git a/chrome/browser/notifications/notification_channels_provider_android_unittest.cc b/chrome/browser/notifications/notification_channels_provider_android_unittest.cc
index ddb5f10..96346df 100644
--- a/chrome/browser/notifications/notification_channels_provider_android_unittest.cc
+++ b/chrome/browser/notifications/notification_channels_provider_android_unittest.cc
@@ -70,7 +70,7 @@
                                     const base::Time& timestamp,
                                     bool enabled) override {
     std::string channel_id =
-        origin + base::Int64ToString(timestamp.ToInternalValue());
+        origin + base::NumberToString(timestamp.ToInternalValue());
     // Note if a channel with this channel ID was already created, this is a
     // no-op. This is intentional, to match the Android Channels API.
     NotificationChannel channel =
diff --git a/chrome/browser/notifications/notification_platform_bridge_win.cc b/chrome/browser/notifications/notification_platform_bridge_win.cc
index dabed06..9fa1e8c2 100644
--- a/chrome/browser/notifications/notification_platform_bridge_win.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_win.cc
@@ -679,7 +679,7 @@
                         bool incognito) {
     std::string payload = base::StringPrintf(
         "%s|%s|%d", notification_id.c_str(), profile_id.c_str(), incognito);
-    return base::UintToString16(base::Hash(payload));
+    return base::NumberToString16(base::Hash(payload));
   }
 
   HRESULT OnDismissed(
diff --git a/chrome/browser/notifications/notification_platform_bridge_win_unittest.cc b/chrome/browser/notifications/notification_platform_bridge_win_unittest.cc
index 4c1acf3..2c58b6a 100644
--- a/chrome/browser/notifications/notification_platform_bridge_win_unittest.cc
+++ b/chrome/browser/notifications/notification_platform_bridge_win_unittest.cc
@@ -128,7 +128,7 @@
   ASSERT_HRESULT_SUCCEEDED(toast2->get_Tag(&hstring_tag));
   base::win::ScopedHString tag(hstring_tag);
   std::string tag_data = std::string(kNotificationId) + "|" + kProfileId + "|0";
-  ASSERT_STREQ(base::UintToString16(base::Hash(tag_data)).c_str(),
+  ASSERT_STREQ(base::NumberToString16(base::Hash(tag_data)).c_str(),
                tag.Get().as_string().c_str());
 }
 
@@ -240,7 +240,7 @@
 
   // Register a single notification with a specific tag.
   std::string tag_data = std::string(kNotificationId) + "|" + kProfileId + "|0";
-  base::string16 tag = base::UintToString16(base::Hash(tag_data));
+  base::string16 tag = base::NumberToString16(base::Hash(tag_data));
   // Microsoft::WRL::Make() requires FakeIToastNotification to derive from
   // RuntimeClass.
   notifications.push_back(Microsoft::WRL::Make<FakeIToastNotification>(
diff --git a/chrome/browser/notifications/notification_ui_manager_browsertest.cc b/chrome/browser/notifications/notification_ui_manager_browsertest.cc
index 609a9bf..417d68da 100644
--- a/chrome/browser/notifications/notification_ui_manager_browsertest.cc
+++ b/chrome/browser/notifications/notification_ui_manager_browsertest.cc
@@ -59,7 +59,7 @@
                const base::Optional<base::string16>& reply) override {
       if (button_index) {
         log_ += "ButtonClick_";
-        log_ += base::IntToString(*button_index) + "_";
+        log_ += base::NumberToString(*button_index) + "_";
       } else {
         log_ += "Click_";
       }
diff --git a/chrome/browser/notifications/win/fake_notification_image_retainer.cc b/chrome/browser/notifications/win/fake_notification_image_retainer.cc
index dc21de3..a5913c9c 100644
--- a/chrome/browser/notifications/win/fake_notification_image_retainer.cc
+++ b/chrome/browser/notifications/win/fake_notification_image_retainer.cc
@@ -14,7 +14,7 @@
 base::FilePath FakeNotificationImageRetainer::RegisterTemporaryImage(
     const gfx::Image& image) {
   base::string16 file = base::string16(L"c:\\temp\\img") +
-                        base::IntToString16(counter_++) +
+                        base::NumberToString16(counter_++) +
                         base::string16(L".tmp");
   return base::FilePath(file);
 }
diff --git a/chrome/browser/ntp_snippets/download_suggestions_provider.cc b/chrome/browser/ntp_snippets/download_suggestions_provider.cc
index b764b796..70be668c 100644
--- a/chrome/browser/ntp_snippets/download_suggestions_provider.cc
+++ b/chrome/browser/ntp_snippets/download_suggestions_provider.cc
@@ -89,13 +89,13 @@
 std::string GetOfflinePagePerCategoryID(int64_t raw_offline_page_id) {
   // Raw ID is prefixed in order to avoid conflicts with asset downloads.
   return std::string(1, kOfflinePageDownloadsPrefix) +
-         base::IntToString(raw_offline_page_id);
+         base::NumberToString(raw_offline_page_id);
 }
 
 std::string GetAssetDownloadPerCategoryID(uint32_t raw_download_id) {
   // Raw ID is prefixed in order to avoid conflicts with offline page downloads.
   return std::string(1, kAssetDownloadsPrefix) +
-         base::UintToString(raw_download_id);
+         base::NumberToString(raw_download_id);
 }
 
 // Determines whether |suggestion_id| corresponds to offline page suggestion or
diff --git a/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc b/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc
index 7010dc1..ecb70be4 100644
--- a/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc
+++ b/chrome/browser/ntp_snippets/download_suggestions_provider_unittest.cc
@@ -160,7 +160,7 @@
 std::unique_ptr<FakeDownloadItem> CreateDummyAssetDownload(int id) {
   std::unique_ptr<FakeDownloadItem> item = std::make_unique<FakeDownloadItem>();
   item->SetId(id);
-  std::string id_string = base::IntToString(id);
+  std::string id_string = base::NumberToString(id);
   item->SetGuid("XYZ-100032-EFZBDF-13323-PXZ" + id_string);
   item->SetTargetFilePath(
       base::FilePath::FromUTF8Unsafe("folder/file" + id_string + ".mhtml"));
@@ -344,7 +344,7 @@
   ContentSuggestion::ID GetDummySuggestionId(int id, bool is_offline_page) {
     return ContentSuggestion::ID(
         downloads_category(),
-        (is_offline_page ? "O" : "D") + base::IntToString(id));
+        (is_offline_page ? "O" : "D") + base::NumberToString(id));
   }
 
   std::vector<ContentSuggestion> GetDismissedSuggestions() {
diff --git a/chrome/browser/offline_pages/android/offline_page_bridge.cc b/chrome/browser/offline_pages/android/offline_page_bridge.cc
index 59ae997b..196ae8e 100644
--- a/chrome/browser/offline_pages/android/offline_page_bridge.cc
+++ b/chrome/browser/offline_pages/android/offline_page_bridge.cc
@@ -207,7 +207,7 @@
       break;
   }
   offline_header.need_to_persist = true;
-  offline_header.id = base::Int64ToString(offline_id);
+  offline_header.id = base::NumberToString(offline_id);
 
   RunLoadUrlParamsCallbackAndroid(j_callback_obj, launch_url, offline_header);
 }
@@ -1043,7 +1043,7 @@
             ? offline_pages::OfflinePageHeader::Reason::FILE_URL_INTENT
             : offline_pages::OfflinePageHeader::Reason::CONTENT_URL_INTENT;
     offline_header.need_to_persist = true;
-    offline_header.id = base::Int64ToString(offline_page->offline_id);
+    offline_header.id = base::NumberToString(offline_page->offline_id);
     offline_header.intent_url = intent_url;
   } else {
     // If the offline page can't be found, launch the intent URL.
diff --git a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
index f279366..7a723c5 100644
--- a/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
+++ b/chrome/browser/offline_pages/offline_page_request_handler_unittest.cc
@@ -672,7 +672,7 @@
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
   }
   std::string file_name("test");
-  file_name += base::IntToString(file_name_sequence_num_++);
+  file_name += base::NumberToString(file_name_sequence_num_++);
   file_name += ".mht";
   temp_file_path_ = temp_dir_.GetPath().AppendASCII(file_name);
   ASSERT_NE(base::WriteFile(temp_file_path_, content.c_str(), content.length()),
@@ -877,7 +877,7 @@
   DCHECK_NE(OfflinePageHeader::Reason::NONE, reason);
   offline_page_header_.reason = reason;
   if (offline_id)
-    offline_page_header_.id = base::Int64ToString(offline_id);
+    offline_page_header_.id = base::NumberToString(offline_id);
   return offline_page_header_.GetCompleteHeaderString();
 }
 
@@ -888,7 +888,7 @@
   DCHECK_NE(OfflinePageHeader::Reason::NONE, reason);
   DCHECK(offline_id);
   offline_page_header_.reason = reason;
-  offline_page_header_.id = base::Int64ToString(offline_id);
+  offline_page_header_.id = base::NumberToString(offline_id);
   offline_page_header_.intent_url = intent_url;
   return offline_page_header_.GetCompleteHeaderString();
 }
@@ -943,7 +943,7 @@
   OfflinePageModel::SavePageParams save_page_params;
   save_page_params.url = url;
   save_page_params.client_id =
-      ClientId(kDownloadNamespace, base::IntToString(item_counter));
+      ClientId(kDownloadNamespace, base::NumberToString(item_counter));
   save_page_params.original_url = original_url;
   OfflinePageModelFactory::GetForBrowserContext(profile())->SavePage(
       save_page_params, std::move(archiver), nullptr,
diff --git a/chrome/browser/offline_pages/offline_page_utils.cc b/chrome/browser/offline_pages/offline_page_utils.cc
index c94db5e..db4141a 100644
--- a/chrome/browser/offline_pages/offline_page_utils.cc
+++ b/chrome/browser/offline_pages/offline_page_utils.cc
@@ -64,7 +64,7 @@
     base::OnceCallback<void(const std::vector<OfflinePageItem>&)> callback,
     const MultipleOfflinePageItemResult& pages) {
   std::vector<OfflinePageItem> selected_pages;
-  std::string tab_id_str = base::IntToString(tab_id);
+  std::string tab_id_str = base::NumberToString(tab_id);
 
   // Exclude pages whose tab id does not match.
   // Note: For this restriction to work offline pages saved to tab-bound
diff --git a/chrome/browser/offline_pages/recent_tab_helper.cc b/chrome/browser/offline_pages/recent_tab_helper.cc
index 38e60dff..19a8127 100644
--- a/chrome/browser/offline_pages/recent_tab_helper.cc
+++ b/chrome/browser/offline_pages/recent_tab_helper.cc
@@ -175,7 +175,7 @@
   tab_id_.clear();
 
   if (delegate_->GetTabId(web_contents(), &tab_id_number))
-    tab_id_ = base::IntToString(tab_id_number);
+    tab_id_ = base::NumberToString(tab_id_number);
 
   // TODO(dimich): When we have BackgroundOffliner, avoid capturing prerenderer
   // WebContents with its origin as well.
diff --git a/chrome/browser/offline_pages/recent_tab_helper_unittest.cc b/chrome/browser/offline_pages/recent_tab_helper_unittest.cc
index 42d10c0..00cb8fbd 100644
--- a/chrome/browser/offline_pages/recent_tab_helper_unittest.cc
+++ b/chrome/browser/offline_pages/recent_tab_helper_unittest.cc
@@ -288,7 +288,7 @@
 ClientId RecentTabHelperTest::NewDownloadClientId() {
   static int counter = 0;
   return ClientId(kDownloadNamespace,
-                  std::string("id") + base::IntToString(++counter));
+                  std::string("id") + base::NumberToString(++counter));
 }
 
 // Checks the test setup.
diff --git a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
index ed7b671..9b51a90 100644
--- a/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/metrics_web_contents_observer_unittest.cc
@@ -1190,8 +1190,7 @@
 
 // Main frame delivers an input notification. Subsequently, a subframe delivers
 // an input notification, where the input occurred first. Verify that
-// FirstInputDelay and FirstInputTimestamp come from the main frame, as FID is
-// currently main-frame-only.
+// FirstInputDelay and FirstInputTimestamp come from the subframe.
 TEST_F(MetricsWebContentsObserverTest,
        FirstInputDelayAndTimingSubframeFirstDeliveredSecond) {
   mojom::PageLoadTiming timing;
@@ -1232,11 +1231,12 @@
   const mojom::InteractiveTiming& interactive_timing =
       *complete_timings().back()->interactive_timing;
 
-  EXPECT_EQ(base::TimeDelta::FromMilliseconds(10),
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(15),
             interactive_timing.first_input_delay);
-  // Ensure the timestamp is from the main frame.
-  EXPECT_EQ(interactive_timing.first_input_timestamp,
-            base::TimeDelta::FromMinutes(100));
+  // Ensure the timestamp is from the subframe. The main frame timestamp was 100
+  // minutes.
+  EXPECT_LT(interactive_timing.first_input_timestamp,
+            base::TimeDelta::FromMinutes(10));
 
   CheckNoErrorEvents();
 }
@@ -1280,6 +1280,7 @@
   timing.interactive_timing->first_input_timestamp =
       base::TimeDelta::FromMilliseconds(90);
 
+  web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
   SimulateTimingUpdate(timing);
 
   // Navigate again to confirm the timing updated for the mainframe message.
@@ -1329,6 +1330,7 @@
       base::TimeDelta::FromMilliseconds(100);
   main_frame_timing.interactive_timing->longest_input_timestamp =
       base::TimeDelta::FromMilliseconds(2000);
+  web_contents_tester->NavigateAndCommit(GURL(kDefaultTestUrl));
   SimulateTimingUpdate(main_frame_timing);
 
   // Second subframe.
@@ -1364,9 +1366,6 @@
 //     LID (15ms)                 LID (100ms)                LID (200ms)
 //
 // Delivery order: Main Frame -> Subframe1 -> Subframe2.
-//
-// Since LID is only recorded in the main frame, we expect the subframe LID to
-// be ignored.
 TEST_F(MetricsWebContentsObserverTest, LongestInputInSubframe) {
   content::WebContentsTester* web_contents_tester =
       content::WebContentsTester::For(web_contents());
@@ -1414,11 +1413,15 @@
   const mojom::InteractiveTiming& interactive_timing =
       *complete_timings().back()->interactive_timing;
 
-  EXPECT_EQ(base::TimeDelta::FromMilliseconds(100),
+  EXPECT_EQ(base::TimeDelta::FromMilliseconds(200),
             interactive_timing.longest_input_delay);
 
-  EXPECT_EQ(interactive_timing.longest_input_timestamp.value(),
-            base::TimeDelta::FromMilliseconds(2000));
+  // Actual LID timestamp includes the delta between navigation start in
+  // subframe2 and navigation time in the main frame. That delta varies with
+  // different runs, so we only check here that the timestamp is greater than
+  // 3s.
+  EXPECT_GT(interactive_timing.longest_input_timestamp.value(),
+            base::TimeDelta::FromMilliseconds(3000));
 
   CheckNoErrorEvents();
 }
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
new file mode 100644
index 0000000..1634d379
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.cc
@@ -0,0 +1,698 @@
+// 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/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h"
+
+#include <algorithm>
+#include <string>
+#include <utility>
+
+#include "base/feature_list.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
+#include "components/subresource_filter/core/common/common_features.h"
+#include "components/ukm/content/source_url_recorder.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "net/base/mime_util.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
+#include "third_party/blink/public/common/download/download_stats.h"
+#include "third_party/blink/public/common/frame/sandbox_flags.h"
+#include "third_party/blink/public/common/mime_util/mime_util.h"
+#include "ui/gfx/geometry/size.h"
+#include "url/gurl.h"
+
+namespace {
+
+#define ADS_HISTOGRAM(suffix, hist_macro, value) \
+  hist_macro("PageLoad.Clients.Ads." suffix, value);
+
+#define RESOURCE_BYTES_HISTOGRAM(suffix, was_cached, value)                \
+  if (was_cached) {                                                        \
+    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Cache." suffix, value);   \
+  } else {                                                                 \
+    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Network." suffix, value); \
+  }
+
+// Finds the RenderFrameHost for the handle, possibly using the FrameTreeNode
+// ID directly if the the handle has not been committed.
+// NOTE: Unsafe with respect to security privileges.
+content::RenderFrameHost* FindFrameMaybeUnsafe(
+    content::NavigationHandle* handle) {
+  return handle->HasCommitted()
+             ? handle->GetRenderFrameHost()
+             : handle->GetWebContents()->UnsafeFindFrameByFrameTreeNodeId(
+                   handle->GetFrameTreeNodeId());
+}
+
+void RecordSingleFeatureUsage(content::RenderFrameHost* rfh,
+                              blink::mojom::WebFeature web_feature) {
+  page_load_metrics::mojom::PageLoadFeatures page_load_features(
+      {web_feature}, {} /* css_properties */, {} /* animated_css_properties */);
+  page_load_metrics::MetricsWebContentsObserver::RecordFeatureUsage(
+      rfh, page_load_features);
+}
+
+using ResourceMimeType = AdsPageLoadMetricsObserver::ResourceMimeType;
+
+}  // namespace
+
+// static
+std::unique_ptr<AdsPageLoadMetricsObserver>
+AdsPageLoadMetricsObserver::CreateIfNeeded() {
+  if (!base::FeatureList::IsEnabled(subresource_filter::kAdTagging))
+    return nullptr;
+  return std::make_unique<AdsPageLoadMetricsObserver>();
+}
+
+// static
+bool AdsPageLoadMetricsObserver::IsSubframeSameOriginToMainFrame(
+    content::RenderFrameHost* sub_host,
+    bool use_parent_origin) {
+  DCHECK(sub_host);
+  content::RenderFrameHost* main_host =
+      content::WebContents::FromRenderFrameHost(sub_host)->GetMainFrame();
+  if (use_parent_origin)
+    sub_host = sub_host->GetParent();
+  url::Origin subframe_origin = sub_host->GetLastCommittedOrigin();
+  url::Origin mainframe_origin = main_host->GetLastCommittedOrigin();
+  return subframe_origin.IsSameOriginWith(mainframe_origin);
+}
+
+AdsPageLoadMetricsObserver::AdsPageLoadMetricsObserver()
+    : subresource_observer_(this) {}
+
+AdsPageLoadMetricsObserver::~AdsPageLoadMetricsObserver() = default;
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+AdsPageLoadMetricsObserver::OnStart(
+    content::NavigationHandle* navigation_handle,
+    const GURL& currently_committed_url,
+    bool started_in_foreground) {
+  auto* observer_manager =
+      subresource_filter::SubresourceFilterObserverManager::FromWebContents(
+          navigation_handle->GetWebContents());
+  // |observer_manager| isn't constructed if the feature for subresource
+  // filtering isn't enabled.
+  if (observer_manager)
+    subresource_observer_.Add(observer_manager);
+  return CONTINUE_OBSERVING;
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+AdsPageLoadMetricsObserver::OnCommit(
+    content::NavigationHandle* navigation_handle,
+    ukm::SourceId source_id) {
+  DCHECK(ad_frames_data_.empty());
+
+  committed_ = true;
+
+  // The main frame is never considered an ad.
+  ad_frames_data_[navigation_handle->GetFrameTreeNodeId()] = nullptr;
+  ProcessOngoingNavigationResource(navigation_handle->GetFrameTreeNodeId());
+  return CONTINUE_OBSERVING;
+}
+
+// Given an ad being triggered for a frame or navigation, get its FrameData
+// and record it into the appropriate data structures.
+void AdsPageLoadMetricsObserver::RecordAdFrameData(
+    FrameTreeNodeId ad_id,
+    bool is_adframe,
+    content::RenderFrameHost* ad_host,
+    bool frame_navigated) {
+  // If an existing subframe is navigating and it was an ad previously that
+  // hasn't navigated yet, then we need to update it.
+  const auto& id_and_data = ad_frames_data_.find(ad_id);
+  FrameData* previous_data = nullptr;
+  if (id_and_data != ad_frames_data_.end() && id_and_data->second) {
+    DCHECK(frame_navigated);
+    if (id_and_data->second->frame_navigated()) {
+      ProcessOngoingNavigationResource(ad_id);
+      return;
+    }
+    previous_data = id_and_data->second;
+  }
+
+  // Determine who the parent frame's ad ancestor is.  If we don't know who it
+  // is, return, such as with a frame from a previous navigation.
+  content::RenderFrameHost* parent_frame_host =
+      ad_host ? ad_host->GetParent() : nullptr;
+  const auto& parent_id_and_data =
+      parent_frame_host
+          ? ad_frames_data_.find(parent_frame_host->GetFrameTreeNodeId())
+          : ad_frames_data_.end();
+  bool parent_exists = parent_id_and_data != ad_frames_data_.end();
+  if (!parent_exists)
+    return;
+
+  // This frame is not nested within an ad frame but is itself an ad.
+  FrameData* ad_data = parent_id_and_data->second;
+
+  // If data existed already, update it and exit, otherwise, add it.
+  if (!ad_data && is_adframe) {
+    if (previous_data) {
+      previous_data->UpdateForNavigation(ad_host, frame_navigated);
+      return;
+    }
+    // If there is not existing data for this frame then create it.
+    ad_frames_data_storage_.emplace_back(ad_id);
+    ad_data = &ad_frames_data_storage_.back();
+    ad_data->UpdateForNavigation(ad_host, frame_navigated);
+  }
+
+  // If there was previous data, then we don't want to overwrite this frame.
+  if (!previous_data)
+    ad_frames_data_[ad_id] = ad_data;
+}
+
+void AdsPageLoadMetricsObserver::ReadyToCommitNextNavigation(
+    content::NavigationHandle* navigation_handle) {
+  // When the renderer receives a CommitNavigation message for the main frame,
+  // all subframes detach and become display : none. Since this is not user
+  // visible, and not reflective of the frames state during the page lifetime,
+  // ignore any such messages when a navigation is about to commit.
+  if (!navigation_handle->IsInMainFrame())
+    return;
+  process_display_state_updates_ = false;
+}
+
+// Determine if the frame is part of an existing ad, the root of a new ad, or a
+// non-ad frame. Once a frame is labeled as an ad, it is always considered an
+// ad, even if it navigates to a non-ad page. This function labels all of a
+// page's frames, even those that fail to commit.
+void AdsPageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
+    content::NavigationHandle* navigation_handle) {
+  FrameTreeNodeId frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
+  bool is_adframe = DetectAds(navigation_handle);
+
+  // NOTE: Frame look-up only used for determining cross-origin status, not
+  // granting security permissions.
+  content::RenderFrameHost* ad_host = FindFrameMaybeUnsafe(navigation_handle);
+
+  if (navigation_handle->IsDownload()) {
+    bool has_sandbox = ad_host->IsSandboxed(blink::WebSandboxFlags::kDownloads);
+    bool has_gesture = navigation_handle->HasUserGesture();
+
+    std::vector<blink::mojom::WebFeature> web_features;
+    if (is_adframe) {
+      // Note: Here it covers download due to navigations to non-web-renderable
+      // content. These two features can also be logged from blink for download
+      // originated from clicking on <a download> link that results in direct
+      // download.
+      blink::mojom::WebFeature web_feature =
+          has_gesture
+              ? blink::mojom::WebFeature::kDownloadInAdFrameWithUserGesture
+              : blink::mojom::WebFeature::kDownloadInAdFrameWithoutUserGesture;
+      RecordSingleFeatureUsage(ad_host, web_feature);
+    }
+    if (has_sandbox) {
+      blink::mojom::WebFeature web_feature =
+          has_gesture ? blink::mojom::WebFeature::
+                            kNavigationDownloadInSandboxWithUserGesture
+                      : blink::mojom::WebFeature::
+                            kNavigationDownloadInSandboxWithoutUserGesture;
+      RecordSingleFeatureUsage(ad_host, web_feature);
+    }
+
+    blink::DownloadStats::SubframeDownloadFlags flags;
+    flags.has_sandbox = has_sandbox;
+    flags.is_cross_origin =
+        !IsSubframeSameOriginToMainFrame(ad_host, /*use_parent_origin=*/false);
+    flags.is_ad_frame = is_adframe;
+    flags.has_gesture = has_gesture;
+    blink::DownloadStats::RecordSubframeDownloadFlags(
+        flags,
+        ukm::GetSourceIdForWebContentsDocument(
+            navigation_handle->GetWebContents()),
+        ukm::UkmRecorder::Get());
+  }
+
+  RecordAdFrameData(frame_tree_node_id, is_adframe, ad_host,
+                    /*frame_navigated=*/true);
+  ProcessOngoingNavigationResource(frame_tree_node_id);
+}
+
+void AdsPageLoadMetricsObserver::FrameReceivedFirstUserActivation(
+    content::RenderFrameHost* render_frame_host) {
+  const auto& id_and_data =
+      ad_frames_data_.find(render_frame_host->GetFrameTreeNodeId());
+  if (id_and_data == ad_frames_data_.end())
+    return;
+  FrameData* ancestor_data = id_and_data->second;
+  if (ancestor_data)
+    ancestor_data->set_received_user_activation();
+}
+
+void AdsPageLoadMetricsObserver::OnDidInternalNavigationAbort(
+    content::NavigationHandle* navigation_handle) {
+  // Main frame navigation
+  if (navigation_handle->IsDownload()) {
+    content::RenderFrameHost* rfh = FindFrameMaybeUnsafe(navigation_handle);
+    bool has_sandbox = rfh->IsSandboxed(blink::WebSandboxFlags::kDownloads);
+    bool has_gesture = navigation_handle->HasUserGesture();
+    if (has_sandbox) {
+      blink::mojom::WebFeature web_feature =
+          has_gesture ? blink::mojom::WebFeature::
+                            kNavigationDownloadInSandboxWithUserGesture
+                      : blink::mojom::WebFeature::
+                            kNavigationDownloadInSandboxWithoutUserGesture;
+      RecordSingleFeatureUsage(rfh, web_feature);
+    }
+
+    blink::DownloadStats::MainFrameDownloadFlags flags;
+    flags.has_sandbox = has_sandbox;
+    flags.has_gesture = has_gesture;
+    blink::DownloadStats::RecordMainFrameDownloadFlags(
+        flags,
+        ukm::GetSourceIdForWebContentsDocument(
+            navigation_handle->GetWebContents()),
+        ukm::UkmRecorder::Get());
+  }
+}
+
+page_load_metrics::PageLoadMetricsObserver::ObservePolicy
+AdsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& extra_info) {
+  // The browser may come back, but there is no guarantee. To be safe, record
+  // what we have now and ignore future changes to this navigation.
+  if (extra_info.did_commit) {
+    if (timing.response_start)
+      time_commit_ = timing.navigation_start + *timing.response_start;
+    RecordHistograms(extra_info.source_id);
+  }
+
+  return STOP_OBSERVING;
+}
+
+void AdsPageLoadMetricsObserver::OnComplete(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (info.did_commit && timing.response_start)
+    time_commit_ = timing.navigation_start + *timing.response_start;
+  RecordHistograms(info.source_id);
+}
+
+void AdsPageLoadMetricsObserver::OnResourceDataUseObserved(
+    FrameTreeNodeId frame_tree_node_id,
+    const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
+        resources) {
+  for (auto const& resource : resources)
+    UpdateResource(frame_tree_node_id, resource);
+}
+
+void AdsPageLoadMetricsObserver::OnSubframeNavigationEvaluated(
+    content::NavigationHandle* navigation_handle,
+    subresource_filter::LoadPolicy load_policy,
+    bool is_ad_subframe) {
+  // We don't track DISALLOW frames because their resources won't be loaded
+  // and therefore would provide bad histogram data. Note that WOULD_DISALLOW
+  // is only seen in dry runs.
+  if (is_ad_subframe &&
+      load_policy != subresource_filter::LoadPolicy::DISALLOW) {
+    unfinished_subresource_ad_frames_.insert(
+        navigation_handle->GetFrameTreeNodeId());
+  }
+}
+
+void AdsPageLoadMetricsObserver::OnPageInteractive(
+    const page_load_metrics::mojom::PageLoadTiming& timing,
+    const page_load_metrics::PageLoadExtraInfo& info) {
+  if (timing.interactive_timing->interactive) {
+    time_interactive_ =
+        timing.navigation_start + *timing.interactive_timing->interactive;
+  }
+}
+
+void AdsPageLoadMetricsObserver::FrameDisplayStateChanged(
+    content::RenderFrameHost* render_frame_host,
+    bool is_display_none) {
+  if (!process_display_state_updates_)
+    return;
+  const auto& id_and_data =
+      ad_frames_data_.find(render_frame_host->GetFrameTreeNodeId());
+  if (id_and_data == ad_frames_data_.end())
+    return;
+  FrameData* ancestor_data = id_and_data->second;
+  // If the frame whose display state has changed is the root of the ad ancestry
+  // chain, then update it. The display property is propagated to all child
+  // frames.
+  if (ancestor_data && render_frame_host->GetFrameTreeNodeId() ==
+                           ancestor_data->frame_tree_node_id()) {
+    ancestor_data->SetDisplayState(is_display_none);
+  }
+}
+
+void AdsPageLoadMetricsObserver::FrameSizeChanged(
+    content::RenderFrameHost* render_frame_host,
+    const gfx::Size& frame_size) {
+  const auto& id_and_data =
+      ad_frames_data_.find(render_frame_host->GetFrameTreeNodeId());
+  if (id_and_data == ad_frames_data_.end())
+    return;
+  FrameData* ancestor_data = id_and_data->second;
+  // If the frame whose size has changed is the root of the ad ancestry chain,
+  // then update it
+  if (ancestor_data && render_frame_host->GetFrameTreeNodeId() ==
+                           ancestor_data->frame_tree_node_id()) {
+    ancestor_data->set_frame_size(frame_size);
+  }
+}
+
+void AdsPageLoadMetricsObserver::OnAdSubframeDetected(
+    content::RenderFrameHost* render_frame_host) {
+  FrameTreeNodeId frame_tree_node_id = render_frame_host->GetFrameTreeNodeId();
+  RecordAdFrameData(frame_tree_node_id, true /* is_adframe */,
+                    render_frame_host,
+                    /*frame_navigated=*/false);
+}
+
+void AdsPageLoadMetricsObserver::OnSubresourceFilterGoingAway() {
+  subresource_observer_.RemoveAll();
+}
+
+bool AdsPageLoadMetricsObserver::DetectSubresourceFilterAd(
+    FrameTreeNodeId frame_tree_node_id) {
+  return unfinished_subresource_ad_frames_.erase(frame_tree_node_id);
+}
+
+bool AdsPageLoadMetricsObserver::DetectAds(
+    content::NavigationHandle* navigation_handle) {
+  return DetectSubresourceFilterAd(navigation_handle->GetFrameTreeNodeId());
+}
+
+void AdsPageLoadMetricsObserver::ProcessResourceForFrame(
+    FrameTreeNodeId frame_tree_node_id,
+    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  const auto& id_and_data = ad_frames_data_.find(frame_tree_node_id);
+  if (id_and_data == ad_frames_data_.end()) {
+    if (resource->is_primary_frame_resource) {
+      // Only hold onto primary resources if their load has finished.
+      if (!resource->is_complete)
+        return;
+
+      // This resource request is the primary resource load for a frame that
+      // hasn't yet finished navigating. Hang onto the request info and replay
+      // it once the frame finishes navigating.
+      ongoing_navigation_resources_.emplace(
+          std::piecewise_construct, std::forward_as_tuple(frame_tree_node_id),
+          std::forward_as_tuple(resource.Clone()));
+    } else {
+      // This is unexpected, it could be:
+      // 1. a resource from a previous navigation that started its resource
+      //    load after this page started navigation.
+      // 2. possibly a resource from a document.written frame whose frame
+      //    failure message has yet to arrive. (uncertain of this)
+    }
+    return;
+  }
+
+  // |delta_bytes| only includes bytes used by the network.
+  page_bytes_ += resource->delta_bytes;
+  page_network_bytes_ += resource->delta_bytes;
+  if (resource->is_complete && resource->was_fetched_via_cache)
+    page_bytes_ += resource->encoded_body_length;
+
+  // Determine if the frame (or its ancestor) is an ad, if so attribute the
+  // bytes to the highest ad ancestor.
+  FrameData* ancestor_data = id_and_data->second;
+  if (ancestor_data)
+    ancestor_data->ProcessResourceLoadInFrame(resource);
+}
+
+AdsPageLoadMetricsObserver::ResourceMimeType
+AdsPageLoadMetricsObserver::GetResourceMimeType(
+    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  if (blink::IsSupportedImageMimeType(resource->mime_type))
+    return ResourceMimeType::kImage;
+  if (blink::IsSupportedJavascriptMimeType(resource->mime_type))
+    return ResourceMimeType::kJavascript;
+
+  std::string top_level_type;
+  std::string subtype;
+  // Categorize invalid mime types as "Other".
+  if (!net::ParseMimeTypeWithoutParameter(resource->mime_type, &top_level_type,
+                                          &subtype)) {
+    return ResourceMimeType::kOther;
+  }
+  if (top_level_type.compare("video") == 0)
+    return ResourceMimeType::kVideo;
+  else if (top_level_type.compare("text") == 0 && subtype.compare("css") == 0)
+    return ResourceMimeType::kCss;
+  else if (top_level_type.compare("text") == 0 && subtype.compare("html") == 0)
+    return ResourceMimeType::kHtml;
+  else
+    return ResourceMimeType::kOther;
+}
+
+void AdsPageLoadMetricsObserver::UpdateResource(
+    FrameTreeNodeId frame_tree_node_id,
+    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  ProcessResourceForFrame(frame_tree_node_id, resource);
+  auto it = page_resources_.find(resource->request_id);
+  // A new resource has been observed.
+  if (it == page_resources_.end())
+    total_number_page_resources_++;
+
+  if (resource->reported_as_ad_resource) {
+    // If the resource had already started loading, and is now labeled as an ad,
+    // but was not before, we need to account for all the previously received
+    // bytes.
+    bool is_new_ad =
+        (it != page_resources_.end()) && !it->second->reported_as_ad_resource;
+    int unaccounted_ad_bytes =
+        is_new_ad ? resource->received_data_length - resource->delta_bytes : 0;
+    page_ad_resource_bytes_ += resource->delta_bytes + unaccounted_ad_bytes;
+    if (resource->is_main_frame_resource) {
+      page_main_frame_ad_resource_bytes_ +=
+          resource->delta_bytes + unaccounted_ad_bytes;
+    }
+    if (!time_interactive_.is_null()) {
+      page_ad_resource_bytes_since_interactive_ +=
+          resource->delta_bytes + unaccounted_ad_bytes;
+    }
+    ResourceMimeType mime_type = GetResourceMimeType(resource);
+    if (mime_type == ResourceMimeType::kVideo)
+      page_ad_video_bytes_ += resource->delta_bytes + unaccounted_ad_bytes;
+    if (mime_type == ResourceMimeType::kJavascript)
+      page_ad_javascript_bytes_ += resource->delta_bytes + unaccounted_ad_bytes;
+  }
+
+  // Update resource map.
+  if (resource->is_complete) {
+    RecordResourceHistograms(resource);
+    if (it != page_resources_.end())
+      page_resources_.erase(it);
+  } else {
+    // Must clone resource so it will be accessible when the observer is
+    // destroyed.
+    if (it != page_resources_.end()) {
+      it->second = resource->Clone();
+    } else {
+      page_resources_.emplace(std::piecewise_construct,
+                              std::forward_as_tuple(resource->request_id),
+                              std::forward_as_tuple(resource->Clone()));
+    }
+  }
+}
+
+void AdsPageLoadMetricsObserver::RecordResourceMimeHistograms(
+    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  int64_t data_length = resource->was_fetched_via_cache
+                            ? resource->encoded_body_length
+                            : resource->received_data_length;
+  ResourceMimeType mime_type = GetResourceMimeType(resource);
+  if (mime_type == ResourceMimeType::kImage) {
+    RESOURCE_BYTES_HISTOGRAM("Mime.Image", resource->was_fetched_via_cache,
+                             data_length);
+  } else if (mime_type == ResourceMimeType::kJavascript) {
+    RESOURCE_BYTES_HISTOGRAM("Mime.JS", resource->was_fetched_via_cache,
+                             data_length);
+  } else if (mime_type == ResourceMimeType::kVideo) {
+    RESOURCE_BYTES_HISTOGRAM("Mime.Video", resource->was_fetched_via_cache,
+                             data_length);
+  } else if (mime_type == ResourceMimeType::kCss) {
+    RESOURCE_BYTES_HISTOGRAM("Mime.CSS", resource->was_fetched_via_cache,
+                             data_length);
+  } else if (mime_type == ResourceMimeType::kHtml) {
+    RESOURCE_BYTES_HISTOGRAM("Mime.HTML", resource->was_fetched_via_cache,
+                             data_length);
+  } else if (mime_type == ResourceMimeType::kOther) {
+    RESOURCE_BYTES_HISTOGRAM("Mime.Other", resource->was_fetched_via_cache,
+                             data_length);
+  }
+}
+
+void AdsPageLoadMetricsObserver::RecordResourceHistograms(
+    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  int64_t data_length = resource->was_fetched_via_cache
+                            ? resource->encoded_body_length
+                            : resource->received_data_length;
+  if (resource->is_main_frame_resource && resource->reported_as_ad_resource) {
+    RESOURCE_BYTES_HISTOGRAM("Mainframe.AdResource",
+                             resource->was_fetched_via_cache, data_length);
+  } else if (resource->is_main_frame_resource) {
+    RESOURCE_BYTES_HISTOGRAM("Mainframe.VanillaResource",
+                             resource->was_fetched_via_cache, data_length);
+  } else if (resource->reported_as_ad_resource) {
+    RESOURCE_BYTES_HISTOGRAM("Subframe.AdResource",
+                             resource->was_fetched_via_cache, data_length);
+  } else {
+    RESOURCE_BYTES_HISTOGRAM("Subframe.VanillaResource",
+                             resource->was_fetched_via_cache, data_length);
+  }
+
+  // Only report sizes by mime type for ad resources.
+  if (resource->reported_as_ad_resource)
+    RecordResourceMimeHistograms(resource);
+}
+
+void AdsPageLoadMetricsObserver::RecordPageResourceTotalHistograms(
+    ukm::SourceId source_id) {
+  // Only records histograms on pages that have some ad bytes.
+  if (page_ad_resource_bytes_ == 0)
+    return;
+  PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Resources.Bytes.Ads",
+                       page_ad_resource_bytes_);
+  PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Resources.Bytes.TopLevelAds",
+                       page_main_frame_ad_resource_bytes_);
+  size_t unfinished_bytes = 0;
+  for (auto const& kv : page_resources_)
+    unfinished_bytes += kv.second->received_data_length;
+  PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Resources.Bytes.Unfinished",
+                       unfinished_bytes);
+
+  auto* ukm_recorder = ukm::UkmRecorder::Get();
+  ukm::builders::AdPageLoad builder(source_id);
+  builder.SetTotalBytes(page_bytes_ >> 10)
+      .SetAdBytes(page_ad_resource_bytes_ >> 10)
+      .SetAdJavascriptBytes(page_ad_javascript_bytes_ >> 10)
+      .SetAdVideoBytes(page_ad_video_bytes_ >> 10);
+  base::Time current_time = base::Time::Now();
+  if (!time_commit_.is_null()) {
+    int time_since_commit = (current_time - time_commit_).InMicroseconds();
+    if (time_since_commit > 0) {
+      int ad_kbps_from_commit =
+          (page_ad_resource_bytes_ >> 10) * 1000 * 1000 / time_since_commit;
+      builder.SetAdBytesPerSecond(ad_kbps_from_commit);
+    }
+  }
+  if (!time_interactive_.is_null()) {
+    int time_since_interactive =
+        (current_time - time_interactive_).InMicroseconds();
+    if (time_since_interactive > 0) {
+      int ad_kbps_since_interactive =
+          (page_ad_resource_bytes_since_interactive_ >> 10) * 1000 * 1000 /
+          time_since_interactive;
+      builder.SetAdBytesPerSecondAfterInteractive(ad_kbps_since_interactive);
+    }
+  }
+  builder.Record(ukm_recorder->Get());
+}
+
+void AdsPageLoadMetricsObserver::RecordHistograms(ukm::SourceId source_id) {
+  RecordHistogramsForAdTagging();
+  RecordPageResourceTotalHistograms(source_id);
+  for (auto const& kv : page_resources_)
+    RecordResourceHistograms(kv.second);
+}
+
+void AdsPageLoadMetricsObserver::RecordHistogramsForAdTagging() {
+  if (page_bytes_ == 0)
+    return;
+
+  int non_zero_ad_frames = 0;
+  size_t total_ad_frame_bytes = 0;
+  size_t ad_frame_network_bytes = 0;
+
+  for (const FrameData& ad_frame_data : ad_frames_data_storage_) {
+    if (ad_frame_data.frame_bytes() == 0)
+      continue;
+
+    ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.Visibility",
+                  UMA_HISTOGRAM_ENUMERATION, ad_frame_data.visibility());
+
+    // Record pixel metrics only for adframes that are displayed.
+    if (ad_frame_data.visibility() !=
+        FrameData::FrameVisibility::kDisplayNone) {
+      ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.SqrtNumberOfPixels",
+                    UMA_HISTOGRAM_COUNTS_10000,
+                    std::sqrt(ad_frame_data.frame_size().GetArea()));
+      ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.SmallestDimension",
+                    UMA_HISTOGRAM_COUNTS_10000,
+                    std::min(ad_frame_data.frame_size().width(),
+                             ad_frame_data.frame_size().height()));
+    }
+
+    non_zero_ad_frames += 1;
+    total_ad_frame_bytes += ad_frame_data.frame_bytes();
+    ad_frame_network_bytes += ad_frame_data.frame_network_bytes();
+
+    ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.Total", PAGE_BYTES_HISTOGRAM,
+                  ad_frame_data.frame_bytes());
+    ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.Network", PAGE_BYTES_HISTOGRAM,
+                  ad_frame_data.frame_network_bytes());
+    ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.PercentNetwork",
+                  UMA_HISTOGRAM_PERCENTAGE,
+                  ad_frame_data.frame_network_bytes() * 100 /
+                      ad_frame_data.frame_bytes());
+    ADS_HISTOGRAM(
+        "SubresourceFilter.FrameCounts.AdFrames.PerFrame.OriginStatus",
+        UMA_HISTOGRAM_ENUMERATION, ad_frame_data.origin_status());
+    ADS_HISTOGRAM(
+        "SubresourceFilter.FrameCounts.AdFrames.PerFrame.UserActivation",
+        UMA_HISTOGRAM_ENUMERATION, ad_frame_data.user_activation_status());
+  }
+
+  // TODO(ericrobinson): Consider renaming this to match
+  //   'FrameCounts.AdFrames.PerFrame.OriginStatus'.
+  ADS_HISTOGRAM("SubresourceFilter.FrameCounts.AnyParentFrame.AdFrames",
+                UMA_HISTOGRAM_COUNTS_1000, non_zero_ad_frames);
+
+  // Don't post UMA for pages that don't have ads.
+  if (non_zero_ad_frames == 0)
+    return;
+
+  ADS_HISTOGRAM("Bytes.NonAdFrames.Aggregate.Total", PAGE_BYTES_HISTOGRAM,
+                page_bytes_ - total_ad_frame_bytes);
+
+  ADS_HISTOGRAM("Bytes.FullPage.Total", PAGE_BYTES_HISTOGRAM, page_bytes_);
+  ADS_HISTOGRAM("Bytes.FullPage.Network", PAGE_BYTES_HISTOGRAM,
+                page_network_bytes_);
+
+  if (page_bytes_) {
+    ADS_HISTOGRAM("Bytes.FullPage.Total.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
+                  total_ad_frame_bytes * 100 / page_bytes_);
+  }
+  if (page_network_bytes_) {
+    ADS_HISTOGRAM("Bytes.FullPage.Network.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
+                  ad_frame_network_bytes * 100 / page_network_bytes_);
+  }
+
+  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Total", PAGE_BYTES_HISTOGRAM,
+                total_ad_frame_bytes);
+  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Network", PAGE_BYTES_HISTOGRAM,
+                ad_frame_network_bytes);
+
+  if (total_ad_frame_bytes) {
+    ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.PercentNetwork",
+                  UMA_HISTOGRAM_PERCENTAGE,
+                  ad_frame_network_bytes * 100 / total_ad_frame_bytes);
+  }
+}
+
+void AdsPageLoadMetricsObserver::ProcessOngoingNavigationResource(
+    FrameTreeNodeId frame_tree_node_id) {
+  const auto& frame_id_and_request =
+      ongoing_navigation_resources_.find(frame_tree_node_id);
+  if (frame_id_and_request == ongoing_navigation_resources_.end())
+    return;
+
+  ProcessResourceForFrame(frame_tree_node_id, frame_id_and_request->second);
+  ongoing_navigation_resources_.erase(frame_id_and_request);
+}
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
new file mode 100644
index 0000000..fa6f857
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h
@@ -0,0 +1,202 @@
+// 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_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_ADS_PAGE_LOAD_METRICS_OBSERVER_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_ADS_PAGE_LOAD_METRICS_OBSERVER_H_
+
+#include <bitset>
+#include <list>
+#include <map>
+#include <memory>
+
+#include "base/macros.h"
+#include "base/scoped_observer.h"
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "chrome/common/page_load_metrics/page_load_metrics.mojom.h"
+#include "components/subresource_filter/content/browser/subresource_filter_observer.h"
+#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
+#include "components/subresource_filter/core/common/load_policy.h"
+#include "net/http/http_response_info.h"
+#include "services/metrics/public/cpp/ukm_source.h"
+
+// This observer labels each sub-frame as an ad or not, and keeps track of
+// relevant per-frame and whole-page byte statistics.
+class AdsPageLoadMetricsObserver
+    : public page_load_metrics::PageLoadMetricsObserver,
+      public subresource_filter::SubresourceFilterObserver {
+ public:
+  // High level categories of mime types for resources loaded by the page.
+  enum class ResourceMimeType {
+    kJavascript = 0,
+    kVideo = 1,
+    kImage = 2,
+    kCss = 3,
+    kHtml = 4,
+    kOther = 5,
+    kMaxValue = kOther,
+  };
+
+  // Returns a new AdsPageLoadMetricObserver. If the feature is disabled it
+  // returns nullptr.
+  static std::unique_ptr<AdsPageLoadMetricsObserver> CreateIfNeeded();
+
+  // For a given subframe, returns whether or not the subframe's url would be
+  // considering same origin to the main frame's url. |use_parent_origin|
+  // indicates that the subframe's parent frames's origin should be used when
+  // performing the comparison.
+  static bool IsSubframeSameOriginToMainFrame(
+      content::RenderFrameHost* sub_host,
+      bool use_parent_origin);
+
+  AdsPageLoadMetricsObserver();
+  ~AdsPageLoadMetricsObserver() override;
+
+  // 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 RecordAdFrameData(FrameTreeNodeId ad_id,
+                         bool is_adframe,
+                         content::RenderFrameHost* ad_host,
+                         bool frame_navigated);
+  void ReadyToCommitNextNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  void OnDidFinishSubFrameNavigation(
+      content::NavigationHandle* navigation_handle) override;
+  void OnDidInternalNavigationAbort(
+      content::NavigationHandle* navigation_handle) override;
+  ObservePolicy FlushMetricsOnAppEnterBackground(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
+                  const page_load_metrics::PageLoadExtraInfo& info) override;
+  void OnResourceDataUseObserved(
+      FrameTreeNodeId frame_tree_node_id,
+      const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
+          resources) override;
+  void OnPageInteractive(
+      const page_load_metrics::mojom::PageLoadTiming& timing,
+      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
+  void FrameReceivedFirstUserActivation(content::RenderFrameHost* rfh) override;
+  void FrameDisplayStateChanged(content::RenderFrameHost* render_frame_host,
+                                bool is_display_none) override;
+  void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
+                        const gfx::Size& frame_size) override;
+
+ private:
+  // subresource_filter::SubresourceFilterObserver:
+  void OnSubframeNavigationEvaluated(
+      content::NavigationHandle* navigation_handle,
+      subresource_filter::LoadPolicy load_policy,
+      bool is_ad_subframe) override;
+  void OnAdSubframeDetected(
+      content::RenderFrameHost* render_frame_host) override;
+  void OnSubresourceFilterGoingAway() override;
+
+  // Determines if the URL of a frame matches the SubresourceFilter block
+  // list. Should only be called once per frame navigation.
+  bool DetectSubresourceFilterAd(FrameTreeNodeId frame_tree_node_id);
+
+  // This should only be called once per frame navigation, as the
+  // SubresourceFilter detector clears its state about detected frames after
+  // each call in order to free up memory.
+  bool DetectAds(content::NavigationHandle* navigation_handle);
+
+  void ProcessResourceForFrame(
+      FrameTreeNodeId frame_tree_node_id,
+      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
+
+  // Get the mime type of a resource. This only returns a subset of mime types,
+  // grouped at a higher level. For example, all video mime types return the
+  // same value.
+  ResourceMimeType GetResourceMimeType(
+      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
+
+  // Update all of the per-resource page counters given a new resource data
+  // update. Updates |page_resources_| to reflect the new state of the resource.
+  // Called once per ResourceDataUpdate.
+  void UpdateResource(
+      FrameTreeNodeId frame_tree_node_id,
+      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
+
+  // Records size of resources by mime type.
+  void RecordResourceMimeHistograms(
+      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
+
+  // Records per-resource histograms.
+  void RecordResourceHistograms(
+      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
+  void RecordPageResourceTotalHistograms(ukm::SourceId source_id);
+  void RecordHistograms(ukm::SourceId source_id);
+  void RecordHistogramsForAdTagging();
+
+  // Checks to see if a resource is waiting for a navigation with the given
+  // |frame_tree_node_id| to commit before it can be processed. If so, call
+  // OnResourceDataUpdate for the delayed resource.
+  void ProcessOngoingNavigationResource(FrameTreeNodeId frame_tree_node_id);
+
+  // Stores the size data of each ad frame. Pointed to by ad_frames_ so use a
+  // data structure that won't move the data around.
+  std::list<FrameData> ad_frames_data_storage_;
+
+  // Maps a frame (by id) to the FrameData responsible for the frame.
+  // Multiple frame ids can point to the same FrameData. The responsible
+  // frame is the top-most frame labeled as an ad in the frame's ancestry,
+  // which may be itself. If no responsible frame is found, the data is
+  // nullptr.
+  std::map<FrameTreeNodeId, FrameData*> ad_frames_data_;
+
+  // The set of frames that have yet to finish but that the SubresourceFilter
+  // has reported are ads. Once DetectSubresourceFilterAd is called the id is
+  // removed from the set.
+  std::set<FrameTreeNodeId> unfinished_subresource_ad_frames_;
+
+  // When the observer receives report of a document resource loading for a
+  // sub-frame before the sub-frame commit occurs, hold onto the resource
+  // request info (delay it) until the sub-frame commits.
+  std::map<FrameTreeNodeId, page_load_metrics::mojom::ResourceDataUpdatePtr>
+      ongoing_navigation_resources_;
+
+  // Maps a request_id for a blink resource to the metadata for the resource
+  // load. Only contains resources that have not completed loading.
+  std::map<int, page_load_metrics::mojom::ResourceDataUpdatePtr>
+      page_resources_;
+
+  // Tallies for bytes and counts observed in resource data updates for the
+  // entire page.
+  size_t page_ad_javascript_bytes_ = 0u;
+  size_t page_ad_video_bytes_ = 0u;
+  size_t page_ad_resource_bytes_ = 0u;
+  size_t page_main_frame_ad_resource_bytes_ = 0u;
+  uint32_t total_number_page_resources_ = 0;
+
+  // Flag denoting that this observer should no longer monitor changes in
+  // display state for frames. This prevents us from receiving the updates when
+  // the frame elements are being destroyed in the renderer.
+  bool process_display_state_updates_ = true;
+
+  // Time the page was committed.
+  base::Time time_commit_;
+
+  // Time the page was observed to be interactive.
+  base::Time time_interactive_;
+
+  // Total ad bytes loaded by the page since it was observed to be interactive.
+  size_t page_ad_resource_bytes_since_interactive_ = 0u;
+
+  size_t page_bytes_ = 0u;
+  size_t page_network_bytes_ = 0u;
+  bool committed_ = false;
+
+  ScopedObserver<subresource_filter::SubresourceFilterObserverManager,
+                 subresource_filter::SubresourceFilterObserver>
+      subresource_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserver);
+};
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_ADS_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
new file mode 100644
index 0000000..b6737a7
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc
@@ -0,0 +1,1278 @@
+// 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 <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/metrics/subprocess_metrics_provider.h"
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h"
+#include "chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/subresource_filter/content/browser/ruleset_service.h"
+#include "components/subresource_filter/core/browser/subresource_filter_features.h"
+#include "components/subresource_filter/core/common/activation_scope.h"
+#include "components/subresource_filter/core/common/common_features.h"
+#include "components/subresource_filter/core/common/test_ruleset_utils.h"
+#include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/download_test_observer.h"
+#include "content/public/test/test_navigation_observer.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/controllable_http_response.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/download/download_stats.h"
+#include "url/gurl.h"
+#include "url/url_constants.h"
+
+namespace {
+
+const char kCrossOriginHistogramId[] =
+    "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AdFrames.PerFrame."
+    "OriginStatus";
+
+const char kAdUserActivationHistogramId[] =
+    "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AdFrames.PerFrame."
+    "UserActivation";
+
+const char kVisibilityHistogramId[] =
+    "PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame."
+    "Visibility";
+
+const char kSqrtNumberOfPixelsHistogramId[] =
+    "PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame."
+    "SqrtNumberOfPixels";
+
+const char kSmallestDimensionHistogramId[] =
+    "PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame."
+    "SmallestDimension";
+
+enum class Origin {
+  kNavigation,
+  kAnchorAttribute,
+};
+
+std::ostream& operator<<(std::ostream& os, Origin origin) {
+  switch (origin) {
+    case Origin::kNavigation:
+      return os << "Navigation";
+    case Origin::kAnchorAttribute:
+      return os << "AnchorAttribute";
+  }
+}
+
+enum class SandboxOption {
+  kNoSandbox,
+  kDisallowDownloadsWithoutUserActivation,
+  kAllowDownloadsWithoutUserActivation,
+};
+
+std::ostream& operator<<(std::ostream& os, SandboxOption sandbox_option) {
+  switch (sandbox_option) {
+    case SandboxOption::kNoSandbox:
+      return os << "NoSandbox";
+    case SandboxOption::kDisallowDownloadsWithoutUserActivation:
+      return os << "DisallowDownloadsWithoutUserActivation";
+    case SandboxOption::kAllowDownloadsWithoutUserActivation:
+      return os << "AllowDownloadsWithoutUserActivation";
+  }
+}
+
+// Allow PageLoadMetricsTestWaiter to be initialized for a new web content
+// before the first commit.
+class PopupPageLoadMetricsWaiterInitializer : public TabStripModelObserver {
+ public:
+  PopupPageLoadMetricsWaiterInitializer(
+      TabStripModel* tab_strip_model,
+      std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>* waiter)
+      : waiter_(waiter), scoped_observer_(this) {
+    scoped_observer_.Add(tab_strip_model);
+  }
+
+  void OnTabStripModelChanged(
+      TabStripModel* tab_strip_model,
+      const TabStripModelChange& change,
+      const TabStripSelectionChange& selection) override {
+    if (change.type() == TabStripModelChange::kInserted &&
+        selection.active_tab_changed()) {
+      DCHECK(waiter_ && !(*waiter_));
+      *waiter_ = std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
+          tab_strip_model->GetActiveWebContents());
+    }
+  }
+
+ private:
+  std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>* waiter_ =
+      nullptr;
+  ScopedObserver<TabStripModel, PopupPageLoadMetricsWaiterInitializer>
+      scoped_observer_;
+
+  DISALLOW_COPY_AND_ASSIGN(PopupPageLoadMetricsWaiterInitializer);
+};
+
+}  // namespace
+
+class AdsPageLoadMetricsObserverBrowserTest
+    : public subresource_filter::SubresourceFilterBrowserTest {
+ public:
+  AdsPageLoadMetricsObserverBrowserTest()
+      : subresource_filter::SubresourceFilterBrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(subresource_filter::kAdTagging);
+  }
+  ~AdsPageLoadMetricsObserverBrowserTest() override {}
+
+  std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>
+  CreatePageLoadMetricsTestWaiter() {
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    return std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
+        web_contents);
+  }
+
+  void SetUpOnMainThread() override {
+    SubresourceFilterBrowserTest::SetUpOnMainThread();
+    SetRulesetWithRules(
+        {subresource_filter::testing::CreateSuffixRule("ad_iframe_writer.js"),
+         subresource_filter::testing::CreateSuffixRule("ad_script.js")});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserverBrowserTest);
+};
+
+// Test that an embedded ad is same origin.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       OriginStatusMetricEmbedded) {
+  base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("/ads_observer/srcdoc_embedded_ad.html"));
+  waiter->AddMinimumCompleteResourcesExpectation(4);
+  waiter->Wait();
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  histogram_tester.ExpectUniqueSample(kCrossOriginHistogramId,
+                                      FrameData::OriginStatus::kSame, 1);
+}
+
+// Test that an empty embedded ad isn't reported at all.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       OriginStatusMetricEmbeddedEmpty) {
+  base::HistogramTester histogram_tester;
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(
+                     "/ads_observer/srcdoc_embedded_ad_empty.html"));
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  histogram_tester.ExpectTotalCount(kCrossOriginHistogramId, 0);
+}
+
+// Test that an ad with the same origin as the main page is same origin.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       OriginStatusMetricSame) {
+  base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("/ads_observer/same_origin_ad.html"));
+  waiter->AddMinimumCompleteResourcesExpectation(4);
+  waiter->Wait();
+
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  histogram_tester.ExpectUniqueSample(kCrossOriginHistogramId,
+                                      FrameData::OriginStatus::kSame, 1);
+}
+
+// Test that an ad with a different origin as the main page is cross origin.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       OriginStatusMetricCross) {
+  // Note: Cannot navigate cross-origin without dynamically generating the URL.
+  base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("/iframe_blank.html"));
+  // Note that the initial iframe is not an ad, so the metric doesn't observe
+  // it initially as same origin.  However, on re-navigating to a cross
+  // origin site that has an ad at its origin, the ad on that page is cross
+  // origin from the original page.
+  NavigateIframeToURL(web_contents(), "test",
+                      embedded_test_server()->GetURL(
+                          "a.com", "/ads_observer/same_origin_ad.html"));
+
+  // Wait until all resource data updates are sent.
+  waiter->AddPageExpectation(
+      page_load_metrics::PageLoadMetricsTestWaiter::TimingField::kLoadEvent);
+  waiter->Wait();
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  histogram_tester.ExpectUniqueSample(kCrossOriginHistogramId,
+                                      FrameData::OriginStatus::kCross, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       UserActivationSetOnFrame) {
+  base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(
+                     "foo.com", "/ad_tagging/frame_factory.html"));
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Create a second frame that will not receive activation.
+  EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
+      web_contents, "createAdFrame('/ad_tagging/frame_factory.html', '');"));
+  EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
+      web_contents, "createAdFrame('/ad_tagging/frame_factory.html', '');"));
+
+  // Wait for the frames resources to be loaded as we only log histograms for
+  // frames that have non-zero bytes. Four resources per frame and one favicon.
+  waiter->AddMinimumCompleteResourcesExpectation(13);
+  waiter->Wait();
+
+  // Activate one frame by executing a dummy script.
+  content::RenderFrameHost* ad_frame =
+      ChildFrameAt(web_contents->GetMainFrame(), 0);
+  const std::string no_op_script = "// No-op script";
+  EXPECT_TRUE(ExecuteScript(ad_frame, no_op_script));
+
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  histogram_tester.ExpectBucketCount(
+      kAdUserActivationHistogramId,
+      FrameData::UserActivationStatus::kReceivedActivation, 1);
+  histogram_tester.ExpectBucketCount(
+      kAdUserActivationHistogramId,
+      FrameData::UserActivationStatus::kNoActivation, 1);
+}
+
+// Test that a subframe that aborts (due to doc.write) doesn't cause a crash
+// if it continues to load resources.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       DocOverwritesNavigation) {
+  content::DOMMessageQueue msg_queue;
+
+  base::HistogramTester histogram_tester;
+
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(
+                     "/ads_observer/docwrite_provisional_frame.html"));
+  std::string status;
+  EXPECT_TRUE(msg_queue.WaitForMessage(&status));
+  EXPECT_EQ("\"loaded\"", status);
+
+  // Navigate away to force the histogram recording.
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+  // TODO(johnidel): Check that the subresources of the new frame are reported
+  // correctly. Resources from a failed provisional load are not reported to
+  // resource data updates, causing this adframe to not be recorded. This is an
+  // uncommon case but should be reported. See crbug.com/914893.
+}
+
+// Test that a blank ad subframe that is docwritten correctly reports metrics.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       DocWriteAboutBlankAdframe) {
+  base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(browser(),
+                               embedded_test_server()->GetURL(
+                                   "/ads_observer/docwrite_blank_frame.html"));
+  waiter->AddMinimumCompleteResourcesExpectation(5);
+  waiter->Wait();
+  // Navigate away to force the histogram recording.
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  histogram_tester.ExpectUniqueSample(
+      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AnyParentFrame."
+      "AdFrames",
+      1, 1);
+  histogram_tester.ExpectUniqueSample(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 0 /* < 1 KB */, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       SubresourceFilter) {
+  ResetConfiguration(subresource_filter::Configuration(
+      subresource_filter::mojom::ActivationLevel::kDryRun,
+      subresource_filter::ActivationScope::ALL_SITES));
+  base::HistogramTester histogram_tester;
+
+  // cross_site_iframe_factory loads URLs like:
+  // http://b.com:40919/cross_site_iframe_factory.html?b()
+  SetRulesetToDisallowURLsWithPathSuffix("b()");
+  const GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b,b,c,d)"));
+
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(browser(), main_url);
+
+  // One favicon resource and 2 resources for each frame.
+  waiter->AddMinimumCompleteResourcesExpectation(11);
+  waiter->Wait();
+
+  // Navigate away to force the histogram recording.
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+
+  histogram_tester.ExpectUniqueSample(
+      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AnyParentFrame."
+      "AdFrames",
+      2, 1);
+}
+
+// Test that a frame without display:none is reported as visible.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       VisibleAdframeRecorded) {
+  base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(browser(),
+                               embedded_test_server()->GetURL(
+                                   "/ads_observer/display_block_adframe.html"));
+  waiter->AddMinimumCompleteResourcesExpectation(4);
+  waiter->Wait();
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  histogram_tester.ExpectUniqueSample(kVisibilityHistogramId,
+                                      FrameData::FrameVisibility::kVisible, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
+                       DisplayNoneAdframeRecorded) {
+  base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(browser(),
+                               embedded_test_server()->GetURL(
+                                   "/ads_observer/display_none_adframe.html"));
+  waiter->AddMinimumCompleteResourcesExpectation(4);
+  waiter->Wait();
+  // Navigate away to force the histogram recording.
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  histogram_tester.ExpectUniqueSample(
+      kVisibilityHistogramId, FrameData::FrameVisibility::kDisplayNone, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest, FramePixelSize) {
+  base::HistogramTester histogram_tester;
+  auto waiter = CreatePageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL(
+                     "/ads_observer/blank_with_adiframe_writer.html"));
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  // Create a 100x100 iframe.
+  ASSERT_TRUE(ExecJs(
+      web_contents,
+      "let frame = createAdIframe(); frame.width=100; frame.height = 100; "
+      "frame.src = '/ads_observer/pixel.png';"));
+
+  // Create a 300x300 iframe with display none that should not be recorded.
+  ASSERT_TRUE(ExecJs(web_contents,
+                     "frame = createAdIframe(); frame.width=300; frame.src "
+                     "= '/ads_observer/pixel.png';"
+                     "frame.height = 300; frame.style.display= 'none';"));
+
+  // Create a 0x0 iframe.
+  ASSERT_TRUE(ExecJs(
+      web_contents,
+      "frame = createAdIframe(); frame.width=0; frame.height = 0; frame.src = "
+      "'/ads_observer/pixel.png';"));
+
+  // Create a 10 x 1000 iframe.
+  ASSERT_TRUE(
+      ExecJs(web_contents,
+             "frame = createAdIframe(); frame.width=10; frame.height = 1000; "
+             "frame.src = '/ads_observer/pixel.png';"));
+
+  // Wait for each frames resource to load so that they will have non-zero
+  // bytes.
+  waiter->AddMinimumCompleteResourcesExpectation(7);
+  waiter->Wait();
+
+  // Navigate away to force the histogram recording.
+  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
+  histogram_tester.ExpectBucketCount(kSqrtNumberOfPixelsHistogramId, 100, 2);
+  histogram_tester.ExpectBucketCount(kSqrtNumberOfPixelsHistogramId, 0, 1);
+  histogram_tester.ExpectBucketCount(kSmallestDimensionHistogramId, 0, 1);
+  histogram_tester.ExpectBucketCount(kSmallestDimensionHistogramId, 10, 1);
+  histogram_tester.ExpectBucketCount(kSmallestDimensionHistogramId, 100, 1);
+
+  // Verify the display: none frame is not recorded.
+  histogram_tester.ExpectBucketCount(kSqrtNumberOfPixelsHistogramId, 300, 0);
+  histogram_tester.ExpectBucketCount(kSmallestDimensionHistogramId, 300, 0);
+}
+
+class AdsPageLoadMetricsTestWaiter
+    : public page_load_metrics::PageLoadMetricsTestWaiter {
+ public:
+  explicit AdsPageLoadMetricsTestWaiter(content::WebContents* web_contents)
+      : page_load_metrics::PageLoadMetricsTestWaiter(web_contents) {}
+  void AddMinimumAdResourceExpectation(int num_ad_resources) {
+    expected_minimum_num_ad_resources_ = num_ad_resources;
+  }
+
+ protected:
+  bool ExpectationsSatisfied() const override {
+    int num_ad_resources = 0;
+    for (auto& kv : page_resources_) {
+      if (kv.second->reported_as_ad_resource)
+        num_ad_resources++;
+    }
+    return num_ad_resources >= expected_minimum_num_ad_resources_ &&
+           PageLoadMetricsTestWaiter::ExpectationsSatisfied();
+  };
+
+ private:
+  int expected_minimum_num_ad_resources_ = 0;
+};
+
+class AdsPageLoadMetricsObserverResourceBrowserTest
+    : public subresource_filter::SubresourceFilterBrowserTest {
+ public:
+  AdsPageLoadMetricsObserverResourceBrowserTest() {
+    scoped_feature_list_.InitAndEnableFeature(subresource_filter::kAdTagging);
+  }
+
+  ~AdsPageLoadMetricsObserverResourceBrowserTest() override {}
+  void SetUpOnMainThread() override {
+    host_resolver()->AddRule("*", "127.0.0.1");
+    SetRulesetWithRules(
+        {subresource_filter::testing::CreateSuffixRule("ad_script.js"),
+         subresource_filter::testing::CreateSuffixRule("ad_script_2.js"),
+         subresource_filter::testing::CreateSuffixRule("disallow.zip")});
+  }
+
+  void OpenLinkInFrame(const content::ToRenderFrameHost& adapter,
+                       const std::string& link_id,
+                       bool has_gesture) {
+    std::string open_link_script = base::StringPrintf(
+        R"(
+            var evt = document.createEvent("MouseEvent");
+            evt.initMouseEvent('click', true, true);
+            document.getElementById('%s').dispatchEvent(evt);
+        )",
+        link_id.c_str());
+    if (has_gesture) {
+      EXPECT_TRUE(ExecuteScript(adapter, open_link_script));
+    } else {
+      EXPECT_TRUE(ExecuteScriptWithoutUserGesture(adapter, open_link_script));
+    }
+  }
+
+ protected:
+  std::unique_ptr<AdsPageLoadMetricsTestWaiter>
+  CreateAdsPageLoadMetricsTestWaiter() {
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    return std::make_unique<AdsPageLoadMetricsTestWaiter>(web_contents);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       ReceivedAdResources) {
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
+  // Two subresources should have been reported as ads.
+  waiter->AddMinimumAdResourceExpectation(2);
+  waiter->Wait();
+}
+
+// Main resources for adframes are counted as ad resources.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       ReceivedMainResourceAds) {
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
+  contents->GetMainFrame()->ExecuteJavaScriptForTests(
+      base::ASCIIToUTF16("createAdFrame('frame_factory.html', '');"));
+  // Two pages subresources should have been reported as ad. The iframe resource
+  // and its three subresources should also be reported as ads.
+  waiter->AddMinimumAdResourceExpectation(6);
+  waiter->Wait();
+}
+
+// Subframe navigations report ad resources correctly.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       ReceivedSubframeNavigationAds) {
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
+  contents->GetMainFrame()->ExecuteJavaScriptForTests(
+      base::ASCIIToUTF16("createAdFrame('frame_factory.html', 'test');"));
+  waiter->AddMinimumAdResourceExpectation(6);
+  waiter->Wait();
+  NavigateIframeToURL(
+      web_contents(), "test",
+      embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
+  // The new subframe and its three subresources should be reported
+  // as ads.
+  waiter->AddMinimumAdResourceExpectation(10);
+  waiter->Wait();
+}
+
+// Verify that per-resource metrics are recorded correctly.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       ReceivedAdResourceMetrics) {
+  base::HistogramTester histogram_tester;
+
+  const char kHttpResponseHeader[] =
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type: text/html; charset=utf-8\r\n"
+      "\r\n";
+  auto main_html_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/mock_page.html",
+          true /*relative_url_is_prefix*/);
+  auto ad_script_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/ad_script.js",
+          true /*relative_url_is_prefix*/);
+  auto iframe_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/iframe.html",
+          true /*relative_url_is_prefix*/);
+  auto vanilla_script_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/vanilla_script.js",
+          true /*relative_url_is_prefix*/);
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
+
+  browser()->OpenURL(content::OpenURLParams(
+      embedded_test_server()->GetURL("/mock_page.html"), content::Referrer(),
+      WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
+
+  main_html_response->WaitForRequest();
+  main_html_response->Send(kHttpResponseHeader);
+  main_html_response->Send(
+      "<html><body></body><script src=\"ad_script.js\"></script></html>");
+  main_html_response->Done();
+
+  ad_script_response->WaitForRequest();
+  ad_script_response->Send(kHttpResponseHeader);
+  ad_script_response->Send(
+      "var iframe = document.createElement(\"iframe\");"
+      "iframe.src =\"iframe.html\";"
+      "document.body.appendChild(iframe);");
+  ad_script_response->Send(std::string(1000, ' '));
+  ad_script_response->Done();
+
+  iframe_response->WaitForRequest();
+  iframe_response->Send(kHttpResponseHeader);
+  iframe_response->Send("<html><script src=\"vanilla_script.js\"></script>");
+  iframe_response->Send(std::string(2000, ' '));
+  iframe_response->Send("</html>");
+  iframe_response->Done();
+
+  vanilla_script_response->WaitForRequest();
+  vanilla_script_response->Send(kHttpResponseHeader);
+  vanilla_script_response->Send(std::string(1024, ' '));
+  waiter->AddMinimumNetworkBytesExpectation(4000);
+  waiter->Wait();
+
+  // Verify correct numbers of resources are recorded.
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Network.Mainframe.VanillaResource", 1);
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Network.Mainframe.AdResource", 1);
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Network.Subframe.AdResource", 1);
+  // Verify unfinished resource not yet recorded.
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Network.Subframe.VanillaResource", 0);
+
+  // Close all tabs instead of navigating as the embedded_test_server will
+  // hang waiting for loads to finish when we have an unfinished
+  // ControlledHttpReseonse.
+  browser()->tab_strip_model()->CloseAllTabs();
+
+  // Verify unfinished resource recorded when page is destroyed.
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Network.Subframe.AdResource", 2);
+
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.FullPage.Network", 4, 1);
+  // We have received 4 KB of ads and 1 KB of toplevel ads.
+  histogram_tester.ExpectBucketCount("PageLoad.Clients.Ads.Resources.Bytes.Ads",
+                                     4, 1);
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Resources.Bytes.TopLevelAds", 1, 1);
+
+  // 4 resources loaded, one unfinished.
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Resources.Bytes.Unfinished", 1, 1);
+}
+
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       IncompleteResourcesRecordedToFrameMetrics) {
+  base::HistogramTester histogram_tester;
+  SetRulesetWithRules(
+      {subresource_filter::testing::CreateSuffixRule("ad_iframe_writer.js")});
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ads_observer");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+
+  const char kHttpResponseHeader[] =
+      "HTTP/1.1 200 OK\r\n"
+      "Content-Type: text/html; charset=utf-8\r\n"
+      "\r\n";
+  auto incomplete_resource_response =
+      std::make_unique<net::test_server::ControllableHttpResponse>(
+          embedded_test_server(), "/incomplete_resource.js",
+          true /*relative_url_is_prefix*/);
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
+
+  browser()->OpenURL(content::OpenURLParams(
+      embedded_test_server()->GetURL("/ad_with_incomplete_resource.html"),
+      content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
+      ui::PAGE_TRANSITION_TYPED, false));
+
+  waiter->AddMinimumCompleteResourcesExpectation(3);
+  waiter->Wait();
+  int64_t initial_page_bytes = waiter->current_network_bytes();
+
+  // Ad resource will not finish loading but should be reported to metrics.
+  incomplete_resource_response->WaitForRequest();
+  incomplete_resource_response->Send(kHttpResponseHeader);
+  incomplete_resource_response->Send(std::string(2048, ' '));
+
+  // Wait for the resource update to be received for the incomplete response.
+  waiter->AddMinimumNetworkBytesExpectation(2048);
+  waiter->Wait();
+
+  // Close all tabs instead of navigating as the embedded_test_server will
+  // hang waiting for loads to finish when we have an unfinished
+  // ControlledHttpResponse.
+  browser()->tab_strip_model()->CloseAllTabs();
+
+  int expected_page_kilobytes = (initial_page_bytes + 2048) / 1024;
+
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.FullPage.Network", expected_page_kilobytes,
+      1);
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Network", 2, 1);
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 2, 1);
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Network", 2, 1);
+  histogram_tester.ExpectBucketCount(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Total", 2, 1);
+}
+
+// Verify that per-resource metrics are reported for cached resources and
+// resources loaded by the network.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       RecordedCacheResourceMetrics) {
+  base::HistogramTester histogram_tester;
+  SetRulesetWithRules(
+      {subresource_filter::testing::CreateSuffixRule("create_frame.js")});
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("foo.com", "/cachetime"));
+
+  // Wait for the favicon to be fetched.
+  waiter->AddMinimumCompleteResourcesExpectation(2);
+  waiter->Wait();
+
+  // All resources should have been loaded by network.
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Network.Mainframe.VanillaResource", 2);
+
+  // Open a new tab and navigate so that resources are fetched via the disk
+  // cache. Navigating to the same URL in the same tab triggers a refresh which
+  // will not check the disk cache.
+  ui_test_utils::NavigateToURLWithDisposition(
+      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
+      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
+          ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
+  waiter = CreateAdsPageLoadMetricsTestWaiter();
+  ui_test_utils::NavigateToURL(
+      browser(), embedded_test_server()->GetURL("foo.com", "/cachetime"));
+
+  // Wait for the resource to be fetched.
+  waiter->AddMinimumCompleteResourcesExpectation(1);
+  waiter->Wait();
+
+  // Resource should be recorded as loaded from the cache. Favicon not
+  // fetched this time.
+  histogram_tester.ExpectTotalCount(
+      "Ads.ResourceUsage.Size.Cache.Mainframe.VanillaResource", 1);
+}
+
+// Verify that Mime type metrics are recorded correctly.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       RecordedMimeMetrics) {
+  base::HistogramTester histogram_tester;
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+  GURL url = embedded_test_server()->GetURL("foo.com", "/frame_factory.html");
+  ui_test_utils::NavigateToURL(browser(), url);
+  contents->GetMainFrame()->ExecuteJavaScriptForTests(
+      base::ASCIIToUTF16("createAdFrame('multiple_mimes.html', 'test');"));
+  waiter->AddMinimumAdResourceExpectation(8);
+  waiter->Wait();
+
+  // Close all tabs to log metrics, as the video resource request is incomplete.
+  browser()->tab_strip_model()->CloseAllTabs();
+
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.HTML",
+                                    1);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.CSS",
+                                    1);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.JS",
+                                    3);
+
+  // Note: png and video/webm mime types are not set explicitly by the
+  // embedded_test_server.
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Image",
+                                    1);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Video",
+                                    1);
+  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Other",
+                                    1);
+
+  // Verify UKM Metrics recorded.
+  auto entries =
+      ukm_recorder.GetEntriesByName(ukm::builders::AdPageLoad::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+  ukm_recorder.ExpectEntrySourceHasUrl(entries.front(), url);
+  EXPECT_GT(*ukm_recorder.GetEntryMetric(
+                entries.front(), ukm::builders::AdPageLoad::kAdBytesName),
+            0);
+  EXPECT_GT(
+      *ukm_recorder.GetEntryMetric(
+          entries.front(), ukm::builders::AdPageLoad::kAdBytesPerSecondName),
+      0);
+
+  // TTI is not reached by this page and thus should not have this recorded.
+  EXPECT_FALSE(ukm_recorder.EntryHasMetric(
+      entries.front(),
+      ukm::builders::AdPageLoad::kAdBytesPerSecondAfterInteractiveName));
+  EXPECT_GT(
+      *ukm_recorder.GetEntryMetric(
+          entries.front(), ukm::builders::AdPageLoad::kAdJavascriptBytesName),
+      0);
+  EXPECT_GT(*ukm_recorder.GetEntryMetric(
+                entries.front(), ukm::builders::AdPageLoad::kAdVideoBytesName),
+            0);
+}
+
+// Download gets blocked when LoadPolicy is DISALLOW for the navigation
+// to download.
+IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
+                       SubframeNavigationDownloadBlockedByLoadPolicy) {
+  ResetConfiguration(subresource_filter::Configuration(
+      subresource_filter::mojom::ActivationLevel::kEnabled,
+      subresource_filter::ActivationScope::ALL_SITES));
+
+  base::HistogramTester histogram_tester;
+
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  std::string host_name = "foo.com";
+  ui_test_utils::NavigateToURL(
+      browser(),
+      embedded_test_server()->GetURL(host_name, "/frame_factory.html"));
+  content::TestNavigationObserver navigation_observer(web_contents());
+  contents->GetMainFrame()->ExecuteJavaScriptForTests(
+      base::ASCIIToUTF16("createFrame('download.html', 'test');"));
+  navigation_observer.Wait();
+
+  content::RenderFrameHost* rfh = content::FrameMatchingPredicate(
+      web_contents(), base::BindRepeating(&content::FrameMatchesName, "test"));
+  OpenLinkInFrame(rfh, "blocked_nav_download_id", false /* gesture*/);
+
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+  histogram_tester.ExpectTotalCount("Download.Subframe.SandboxOriginAdGesture",
+                                    0);
+}
+
+class RemoteFrameNavigationBrowserTest
+    : public AdsPageLoadMetricsObserverResourceBrowserTest {
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    command_line->AppendSwitchASCII(
+        "enable-blink-features",
+        "BlockingDownloadsInSandboxWithoutUserActivation");
+  }
+};
+
+IN_PROC_BROWSER_TEST_F(RemoteFrameNavigationBrowserTest,
+                       DownloadsBlockedInSandbox) {
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  base::HistogramTester histogram_tester;
+  std::string origin1 = "foo.com";
+  std::string origin2 = "bar.com";
+  GURL tab1_url =
+      embedded_test_server()->GetURL(origin1, "/frame_factory.html");
+
+  auto subframe_navigation_waiter =
+      std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
+          web_contents());
+  subframe_navigation_waiter->AddSubframeNavigationExpectation(2);
+
+  ui_test_utils::NavigateToURL(browser(), tab1_url);
+
+  std::string subframe_url =
+      embedded_test_server()->GetURL(origin2, "/frame_factory.html").spec();
+  content::TestNavigationObserver new_subframe_waiter(web_contents());
+  std::string script =
+      base::StringPrintf("createFrame('%s','test','');", subframe_url.c_str());
+  web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
+      base::ASCIIToUTF16(script));
+  new_subframe_waiter.Wait();
+
+  GURL dld_url = embedded_test_server()->GetURL(origin1, "/allow.zip");
+  EXPECT_TRUE(ExecuteScriptWithoutUserGesture(
+      web_contents(),
+      "document.getElementById('test').src = \"" + dld_url.spec() + "\";"));
+
+  subframe_navigation_waiter->Wait();
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+  histogram_tester.ExpectTotalCount("Download.Subframe.SandboxOriginAdGesture",
+                                    0 /* expected_count */);
+}
+
+class MainFrameDownloadFlagsBrowserTest
+    : public AdsPageLoadMetricsObserverResourceBrowserTest,
+      public ::testing::WithParamInterface<std::tuple<
+          Origin,
+          bool /* enable_blocking_downloads_in_sandbox_without_user_activation
+                */
+          ,
+          SandboxOption,
+          bool /* has_gesture */>> {
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    bool enable_blocking_downloads_in_sandbox_without_user_activation;
+    std::tie(std::ignore,
+             enable_blocking_downloads_in_sandbox_without_user_activation,
+             std::ignore, std::ignore) = GetParam();
+    std::string cmd =
+        enable_blocking_downloads_in_sandbox_without_user_activation
+            ? "enable-blink-features"
+            : "disable-blink-features";
+    command_line->AppendSwitchASCII(
+        cmd, "BlockingDownloadsInSandboxWithoutUserActivation");
+  }
+};
+
+// Main frame download events are reported correctly.
+IN_PROC_BROWSER_TEST_P(MainFrameDownloadFlagsBrowserTest, Download) {
+  Origin origin;
+  bool enable_blocking_downloads_in_sandbox_without_user_activation;
+  SandboxOption sandbox_option;
+  bool has_gesture;
+  std::tie(origin, enable_blocking_downloads_in_sandbox_without_user_activation,
+           sandbox_option, has_gesture) = GetParam();
+  SCOPED_TRACE(
+      ::testing::Message()
+      << "origin = " << origin << ", "
+      << "enable_blocking_downloads_in_sandbox_without_user_activation = "
+      << enable_blocking_downloads_in_sandbox_without_user_activation << ", "
+      << "sandbox_option = " << sandbox_option << ", "
+      << "has_gesture = " << has_gesture);
+
+  bool expected_download =
+      !enable_blocking_downloads_in_sandbox_without_user_activation ||
+      has_gesture ||
+      sandbox_option != SandboxOption::kDisallowDownloadsWithoutUserActivation;
+  bool expected_sandbox_bit =
+      enable_blocking_downloads_in_sandbox_without_user_activation
+          ? has_gesture &&
+                sandbox_option ==
+                    SandboxOption::kDisallowDownloadsWithoutUserActivation
+          : sandbox_option != SandboxOption::kNoSandbox;
+
+  base::HistogramTester histogram_tester;
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  std::string host_name = "foo.com";
+  GURL main_url = embedded_test_server()->GetURL(host_name, "/download.html");
+
+  std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>
+      web_feature_waiter;
+
+  if (sandbox_option == SandboxOption::kNoSandbox) {
+    ui_test_utils::NavigateToURL(browser(), main_url);
+  } else {
+    GURL first_tab_url =
+        embedded_test_server()->GetURL(host_name, "/frame_factory.html");
+    ui_test_utils::NavigateToURL(browser(), first_tab_url);
+    const char* method = "createFrame";
+    std::string subframe_url =
+        embedded_test_server()->GetURL(host_name, "/frame_factory.html").spec();
+    const char* id = "test";
+    const char* sandbox_param =
+        sandbox_option == SandboxOption::kDisallowDownloadsWithoutUserActivation
+            ? "'allow-scripts allow-same-origin allow-popups'"
+            : "'allow-scripts allow-same-origin allow-popups "
+              "allow-downloads-without-user-activation'";
+    content::TestNavigationObserver navigation_observer(web_contents());
+    std::string script = base::StringPrintf(
+        "%s('%s','%s',%s);", method, subframe_url.c_str(), id, sandbox_param);
+    web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
+        base::ASCIIToUTF16(script));
+    navigation_observer.Wait();
+
+    content::RenderFrameHost* child = content::FrameMatchingPredicate(
+        web_contents(), base::BindRepeating(&content::FrameMatchesName, id));
+
+    std::unique_ptr<PopupPageLoadMetricsWaiterInitializer> waiter_initializer;
+    if (expected_sandbox_bit) {
+      waiter_initializer =
+          std::make_unique<PopupPageLoadMetricsWaiterInitializer>(
+              browser()->tab_strip_model(), &web_feature_waiter);
+    }
+    content::TestNavigationObserver popup_observer(main_url);
+    popup_observer.StartWatchingNewWebContents();
+    EXPECT_TRUE(
+        ExecuteScript(child, "window.open(\"" + main_url.spec() + "\");"));
+    popup_observer.Wait();
+    ASSERT_EQ(2, browser()->tab_strip_model()->count());
+  }
+
+  DCHECK(!expected_sandbox_bit || web_feature_waiter);
+  if (expected_sandbox_bit) {
+    blink::mojom::WebFeature feature =
+        origin == Origin::kNavigation
+            ? has_gesture ? blink::mojom::WebFeature::
+                                kNavigationDownloadInSandboxWithUserGesture
+                          : blink::mojom::WebFeature::
+                                kNavigationDownloadInSandboxWithoutUserGesture
+            : has_gesture
+                  ? blink::mojom::WebFeature::
+                        kHTMLAnchorElementDownloadInSandboxWithUserGesture
+                  : blink::mojom::WebFeature::
+                        kHTMLAnchorElementDownloadInSandboxWithoutUserGesture;
+    web_feature_waiter->AddWebFeatureExpectation(feature);
+  }
+
+  std::string link_id =
+      origin == Origin::kNavigation ? "nav_download_id" : "anchor_download_id";
+
+  std::unique_ptr<content::DownloadTestObserver> download_observer(
+      new content::DownloadTestObserverTerminal(
+          content::BrowserContext::GetDownloadManager(browser()->profile()),
+          expected_download /* wait_count */,
+          content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL));
+  OpenLinkInFrame(web_contents(), link_id, has_gesture);
+  download_observer->WaitForFinished();
+  if (web_feature_waiter)
+    web_feature_waiter->Wait();
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+
+  if (!expected_download) {
+    histogram_tester.ExpectTotalCount("Download.MainFrame.SandboxGesture",
+                                      0 /* expected_count */);
+    return;
+  }
+
+  blink::DownloadStats::MainFrameDownloadFlags expected_flags;
+  expected_flags.has_sandbox = expected_sandbox_bit;
+  expected_flags.has_gesture = has_gesture;
+  histogram_tester.ExpectUniqueSample("Download.MainFrame.SandboxGesture",
+                                      expected_flags.ToUmaValue(),
+                                      1 /* expected_count */);
+
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::MainFrameDownload::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+  ukm_recorder.ExpectEntrySourceHasUrl(entries.back(), main_url);
+  ukm_recorder.ExpectEntryMetric(
+      entries.back(), ukm::builders::MainFrameDownload::kHasSandboxName,
+      expected_sandbox_bit);
+  ukm_recorder.ExpectEntryMetric(
+      entries.back(), ukm::builders::MainFrameDownload::kHasGestureName,
+      has_gesture);
+}
+
+INSTANTIATE_TEST_CASE_P(
+    /* no prefix */,
+    MainFrameDownloadFlagsBrowserTest,
+    ::testing::Combine(
+        ::testing::Values(Origin::kNavigation, Origin::kAnchorAttribute),
+        ::testing::Bool(),
+        ::testing::Values(
+            SandboxOption::kNoSandbox,
+            SandboxOption::kDisallowDownloadsWithoutUserActivation,
+            SandboxOption::kAllowDownloadsWithoutUserActivation),
+        ::testing::Bool()));
+
+class SubframeDownloadFlagsBrowserTest
+    : public AdsPageLoadMetricsObserverResourceBrowserTest,
+      public ::testing::WithParamInterface<std::tuple<
+          Origin,
+          bool /* enable_blocking_downloads_in_sandbox_without_user_activation
+                */
+          ,
+          SandboxOption,
+          bool /* is_cross_origin */,
+          bool /* is_ad_frame */,
+          bool /* has_gesture */>> {
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    bool enable_blocking_downloads_in_sandbox_without_user_activation;
+    std::tie(std::ignore,
+             enable_blocking_downloads_in_sandbox_without_user_activation,
+             std::ignore, std::ignore, std::ignore, std::ignore) = GetParam();
+    std::string cmd =
+        enable_blocking_downloads_in_sandbox_without_user_activation
+            ? "enable-blink-features"
+            : "disable-blink-features";
+    command_line->AppendSwitchASCII(
+        cmd, "BlockingDownloadsInSandboxWithoutUserActivation");
+  }
+};
+
+// Subframe download events are reported correctly.
+IN_PROC_BROWSER_TEST_P(SubframeDownloadFlagsBrowserTest, Download) {
+  Origin origin;
+  bool enable_blocking_downloads_in_sandbox_without_user_activation;
+  SandboxOption sandbox_option;
+  bool is_cross_origin;
+  bool is_ad_frame;
+  bool has_gesture;
+  std::tie(origin, enable_blocking_downloads_in_sandbox_without_user_activation,
+           sandbox_option, is_cross_origin, is_ad_frame, has_gesture) =
+      GetParam();
+  SCOPED_TRACE(
+      ::testing::Message()
+      << "origin = " << origin << ", "
+      << "enable_blocking_downloads_in_sandbox_without_user_activation = "
+      << enable_blocking_downloads_in_sandbox_without_user_activation << ", "
+      << "sandbox_option = " << sandbox_option << ", "
+      << "is_cross_origin = " << is_cross_origin << ", "
+      << "is_ad_frame = " << is_ad_frame << ", "
+      << "has_gesture = " << has_gesture);
+
+  base::HistogramTester histogram_tester;
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  bool expected_download =
+      !enable_blocking_downloads_in_sandbox_without_user_activation ||
+      has_gesture ||
+      sandbox_option != SandboxOption::kDisallowDownloadsWithoutUserActivation;
+  bool expected_sandbox_bit =
+      enable_blocking_downloads_in_sandbox_without_user_activation
+          ? has_gesture &&
+                sandbox_option ==
+                    SandboxOption::kDisallowDownloadsWithoutUserActivation
+          : sandbox_option != SandboxOption::kNoSandbox;
+
+  std::unique_ptr<content::DownloadTestObserver> download_observer(
+      new content::DownloadTestObserverTerminal(
+          content::BrowserContext::GetDownloadManager(browser()->profile()),
+          expected_download /* wait_count */,
+          content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL));
+
+  embedded_test_server()->ServeFilesFromSourceDirectory(
+      "chrome/test/data/ad_tagging");
+  content::SetupCrossSiteRedirector(embedded_test_server());
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  content::WebContents* contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  std::unique_ptr<AdsPageLoadMetricsTestWaiter> waiter;
+  if (origin == Origin::kNavigation) {
+    waiter = std::make_unique<AdsPageLoadMetricsTestWaiter>(contents);
+    waiter->AddSubframeNavigationExpectation(2);
+  }
+  if (expected_download && is_ad_frame) {
+    if (!waiter)
+      waiter = std::make_unique<AdsPageLoadMetricsTestWaiter>(contents);
+    blink::mojom::WebFeature feature =
+        has_gesture
+            ? blink::mojom::WebFeature::kDownloadInAdFrameWithUserGesture
+            : blink::mojom::WebFeature::kDownloadInAdFrameWithoutUserGesture;
+    waiter->AddWebFeatureExpectation(feature);
+  }
+  if (expected_sandbox_bit) {
+    if (!waiter)
+      waiter = std::make_unique<AdsPageLoadMetricsTestWaiter>(contents);
+    blink::mojom::WebFeature feature =
+        origin == Origin::kNavigation
+            ? has_gesture ? blink::mojom::WebFeature::
+                                kNavigationDownloadInSandboxWithUserGesture
+                          : blink::mojom::WebFeature::
+                                kNavigationDownloadInSandboxWithoutUserGesture
+            : has_gesture
+                  ? blink::mojom::WebFeature::
+                        kHTMLAnchorElementDownloadInSandboxWithUserGesture
+                  : blink::mojom::WebFeature::
+                        kHTMLAnchorElementDownloadInSandboxWithoutUserGesture;
+    waiter->AddWebFeatureExpectation(feature);
+  }
+
+  std::string host_name = "foo.com";
+  GURL main_url =
+      embedded_test_server()->GetURL(host_name, "/frame_factory.html");
+  ui_test_utils::NavigateToURL(browser(), main_url);
+
+  std::string link_id =
+      origin == Origin::kNavigation ? "nav_download_id" : "anchor_download_id";
+
+  const char* method = is_ad_frame ? "createAdFrame" : "createFrame";
+  std::string url =
+      embedded_test_server()
+          ->GetURL(is_cross_origin ? "bar.com" : host_name, "/download.html")
+          .spec();
+  const char* id = "test";
+  const char* sandbox_param =
+      sandbox_option == SandboxOption::kNoSandbox
+          ? "undefined"
+          : sandbox_option ==
+                    SandboxOption::kDisallowDownloadsWithoutUserActivation
+                ? "'allow-scripts allow-same-origin'"
+                : "'allow-scripts allow-same-origin "
+                  "allow-downloads-without-user-activation'";
+
+  content::TestNavigationObserver navigation_observer(web_contents());
+  std::string script = base::StringPrintf("%s('%s','%s',%s);", method,
+                                          url.c_str(), id, sandbox_param);
+
+  contents->GetMainFrame()->ExecuteJavaScriptForTests(
+      base::ASCIIToUTF16(script));
+  navigation_observer.Wait();
+
+  content::RenderFrameHost* rfh = content::FrameMatchingPredicate(
+      web_contents(), base::BindRepeating(&content::FrameMatchesName, id));
+  OpenLinkInFrame(rfh, link_id, has_gesture);
+
+  download_observer->WaitForFinished();
+  if (waiter)
+    waiter->Wait();
+  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
+
+  if (!expected_download) {
+    histogram_tester.ExpectTotalCount(
+        "Download.Subframe.SandboxOriginAdGesture", 0 /* expected_count */);
+    return;
+  }
+
+  blink::DownloadStats::SubframeDownloadFlags expected_flags;
+  expected_flags.has_sandbox = expected_sandbox_bit;
+  expected_flags.is_cross_origin = is_cross_origin;
+  expected_flags.is_ad_frame = is_ad_frame;
+  expected_flags.has_gesture = has_gesture;
+  histogram_tester.ExpectUniqueSample(
+      "Download.Subframe.SandboxOriginAdGesture", expected_flags.ToUmaValue(),
+      1 /* expected_count */);
+
+  auto entries = ukm_recorder.GetEntriesByName(
+      ukm::builders::SubframeDownload::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+
+  switch (origin) {
+    case Origin::kAnchorAttribute: {
+      const ukm::mojom::UkmEntry* dc_entry =
+          ukm_recorder.GetDocumentCreatedEntryForSourceId(
+              entries.back()->source_id);
+      const ukm::UkmSource* navigation_source =
+          ukm_recorder.GetSourceForSourceId(*ukm_recorder.GetEntryMetric(
+              dc_entry,
+              ukm::builders::DocumentCreated::kNavigationSourceIdName));
+      EXPECT_EQ(main_url, navigation_source->url());
+    } break;
+    case Origin::kNavigation: {
+      ukm_recorder.ExpectEntrySourceHasUrl(entries.back(), main_url);
+    } break;
+  }
+
+  ukm_recorder.ExpectEntryMetric(
+      entries.back(), ukm::builders::SubframeDownload::kHasSandboxName,
+      expected_flags.has_sandbox);
+  ukm_recorder.ExpectEntryMetric(
+      entries.back(), ukm::builders::SubframeDownload::kIsCrossOriginName,
+      is_cross_origin);
+  ukm_recorder.ExpectEntryMetric(
+      entries.back(), ukm::builders::SubframeDownload::kIsAdFrameName,
+      is_ad_frame);
+  ukm_recorder.ExpectEntryMetric(
+      entries.back(), ukm::builders::SubframeDownload::kHasGestureName,
+      has_gesture);
+}
+
+INSTANTIATE_TEST_CASE_P(
+    /* no prefix */,
+    SubframeDownloadFlagsBrowserTest,
+    ::testing::Combine(
+        ::testing::Values(Origin::kNavigation, Origin::kAnchorAttribute),
+        ::testing::Bool(),
+        ::testing::Values(
+            SandboxOption::kNoSandbox,
+            SandboxOption::kDisallowDownloadsWithoutUserActivation,
+            SandboxOption::kAllowDownloadsWithoutUserActivation),
+        ::testing::Bool(),
+        ::testing::Bool(),
+        ::testing::Bool()));
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
new file mode 100644
index 0000000..ff9ef100
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -0,0 +1,705 @@
+// 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/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/sequenced_task_runner.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "base/time/time.h"
+#include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h"
+#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/page_load_tracker.h"
+#include "chrome/browser/subresource_filter/subresource_filter_test_harness.h"
+#include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
+#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
+#include "components/subresource_filter/core/common/load_policy.h"
+#include "components/ukm/test_ukm_recorder.h"
+#include "content/public/browser/global_request_id.h"
+#include "content/public/browser/navigation_handle.h"
+#include "content/public/browser/navigation_throttle.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_observer.h"
+#include "content/public/common/resource_type.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_navigation_throttle.h"
+#include "content/public/test/test_navigation_throttle_inserter.h"
+#include "content/public/test/test_renderer_host.h"
+#include "net/base/host_port_pair.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "url/gurl.h"
+
+using content::NavigationSimulator;
+using content::RenderFrameHost;
+using content::RenderFrameHostTester;
+using content::TestNavigationThrottle;
+
+namespace {
+
+struct ExpectedFrameBytes {
+  ExpectedFrameBytes(size_t cached_kb, size_t uncached_kb)
+      : cached_kb(cached_kb), uncached_kb(uncached_kb) {}
+  size_t cached_kb;
+  size_t uncached_kb;
+};
+
+enum class ResourceCached { NOT_CACHED = false, CACHED = true };
+enum class FrameType { AD = 0, NON_AD };
+
+const char kAdUrl[] = "https://ads.com/ad/disallowed.html";
+const char kNonAdUrl[] = "https://foo.com/";
+const char kNonAdUrlSameOrigin[] = "https://ads.com/foo";
+
+// Asynchronously cancels the navigation at WillProcessResponse. Before
+// cancelling, simulates loading a main frame resource.
+class ResourceLoadingCancellingThrottle
+    : public content::TestNavigationThrottle {
+ public:
+  static std::unique_ptr<content::NavigationThrottle> Create(
+      content::NavigationHandle* handle) {
+    return std::make_unique<ResourceLoadingCancellingThrottle>(handle);
+  }
+
+  explicit ResourceLoadingCancellingThrottle(
+      content::NavigationHandle* navigation_handle)
+      : content::TestNavigationThrottle(navigation_handle) {
+    SetResponse(TestNavigationThrottle::WILL_PROCESS_RESPONSE,
+                TestNavigationThrottle::ASYNCHRONOUS, CANCEL);
+  }
+
+ private:
+  // content::TestNavigationThrottle:
+  void OnWillRespond(NavigationThrottle::ThrottleCheckResult result) {
+    if (result.action() != CANCEL) {
+      return;
+    }
+
+    auto* observer =
+        page_load_metrics::MetricsWebContentsObserver::FromWebContents(
+            navigation_handle()->GetWebContents());
+    DCHECK(observer);
+
+    // Load a resource for the main frame before it commits.
+    std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources;
+    page_load_metrics::mojom::ResourceDataUpdatePtr resource =
+        page_load_metrics::mojom::ResourceDataUpdate::New();
+    resource->received_data_length = 10 * 1024;
+    resource->delta_bytes = 10 * 1024;
+    resource->encoded_body_length = 10 * 1024;
+    resource->was_fetched_via_cache = false;
+    resource->is_complete = true;
+    resource->is_primary_frame_resource = true;
+    resources.push_back(std::move(resource));
+    auto timing = page_load_metrics::mojom::PageLoadTimingPtr(base::in_place);
+    page_load_metrics::InitPageLoadTimingForTest(timing.get());
+    observer->OnTimingUpdated(
+        navigation_handle()->GetRenderFrameHost(), std::move(timing),
+        page_load_metrics::mojom::PageLoadMetadataPtr(base::in_place),
+        page_load_metrics::mojom::PageLoadFeaturesPtr(base::in_place),
+        resources, page_load_metrics::mojom::PageRenderDataPtr(base::in_place));
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(ResourceLoadingCancellingThrottle);
+};
+
+std::string SuffixedHistogram(const std::string& suffix) {
+  return base::StringPrintf("PageLoad.Clients.Ads.%s", suffix.c_str());
+}
+
+// Verifies that the histograms match what is expected.
+void TestHistograms(const base::HistogramTester& histograms,
+                    const std::vector<ExpectedFrameBytes>& ad_frames,
+                    size_t non_ad_cached_kb,
+                    size_t non_ad_uncached_kb) {
+  size_t total_ad_cached_kb = 0;
+  size_t total_ad_uncached_kb = 0;
+  size_t total_ad_kb = 0;
+  size_t ad_frame_count = 0;
+
+  std::map<size_t, int> frames_with_total_byte_count;
+  std::map<size_t, int> frames_with_network_byte_count;
+  std::map<size_t, int> frames_with_percent_network_count;
+
+  // Perform some initial calculations on the number of bytes, of each type,
+  // in each ad frame.
+  for (const ExpectedFrameBytes& bytes : ad_frames) {
+    total_ad_cached_kb += bytes.cached_kb;
+    total_ad_uncached_kb += bytes.uncached_kb;
+    total_ad_kb += bytes.cached_kb + bytes.uncached_kb;
+
+    if (total_ad_kb == 0)
+      continue;
+
+    ad_frame_count += 1;
+
+    size_t total_frame_kb = bytes.cached_kb + bytes.uncached_kb;
+
+    frames_with_total_byte_count[total_frame_kb] += 1;
+    frames_with_network_byte_count[bytes.uncached_kb] += 1;
+    frames_with_percent_network_count[(bytes.uncached_kb * 100) /
+                                      total_frame_kb] += 1;
+  }
+
+  // Test the histograms.
+  histograms.ExpectUniqueSample(
+      SuffixedHistogram(
+          "SubresourceFilter.FrameCounts.AnyParentFrame.AdFrames"),
+      ad_frame_count, 1);
+
+  if (ad_frame_count == 0)
+    return;
+
+  for (const auto& total_bytes_and_count : frames_with_total_byte_count) {
+    histograms.ExpectBucketCount(
+        SuffixedHistogram("Bytes.AdFrames.PerFrame.Total"),
+        total_bytes_and_count.first, total_bytes_and_count.second);
+  }
+  for (const auto& network_bytes_and_count : frames_with_network_byte_count) {
+    histograms.ExpectBucketCount(
+        SuffixedHistogram("Bytes.AdFrames.PerFrame.Network"),
+        network_bytes_and_count.first, network_bytes_and_count.second);
+  }
+  for (const auto& percent_network_and_count :
+       frames_with_percent_network_count) {
+    histograms.ExpectBucketCount(
+        SuffixedHistogram("Bytes.AdFrames.PerFrame.PercentNetwork"),
+        percent_network_and_count.first, percent_network_and_count.second);
+  }
+
+  histograms.ExpectUniqueSample(
+      SuffixedHistogram("Bytes.AdFrames.Aggregate.Total"), total_ad_kb, 1);
+  histograms.ExpectUniqueSample(
+      SuffixedHistogram("Bytes.AdFrames.Aggregate.Network"),
+      total_ad_uncached_kb, 1);
+  histograms.ExpectUniqueSample(
+      SuffixedHistogram("Bytes.FullPage.Total"),
+      non_ad_cached_kb + non_ad_uncached_kb + total_ad_kb, 1);
+  histograms.ExpectUniqueSample(SuffixedHistogram("Bytes.FullPage.Network"),
+                                non_ad_uncached_kb + total_ad_uncached_kb, 1);
+  histograms.ExpectUniqueSample(
+      SuffixedHistogram("Bytes.NonAdFrames.Aggregate.Total"),
+      non_ad_cached_kb + non_ad_uncached_kb, 1);
+  histograms.ExpectUniqueSample(
+      SuffixedHistogram("Bytes.FullPage.Total.PercentAds"),
+      (total_ad_kb * 100) /
+          (total_ad_kb + non_ad_cached_kb + non_ad_uncached_kb),
+      1);
+  histograms.ExpectUniqueSample(
+      SuffixedHistogram("Bytes.AdFrames.Aggregate.PercentNetwork"),
+      ((total_ad_uncached_kb * 100) / total_ad_kb), 1);
+  histograms.ExpectUniqueSample(
+      SuffixedHistogram("Bytes.FullPage.Network.PercentAds"),
+      (total_ad_uncached_kb * 100) /
+          (total_ad_uncached_kb + non_ad_uncached_kb),
+      1);
+}
+
+}  // namespace
+
+class AdsPageLoadMetricsObserverTest : public SubresourceFilterTestHarness {
+ public:
+  AdsPageLoadMetricsObserverTest() {}
+
+  void SetUp() override {
+    SubresourceFilterTestHarness::SetUp();
+    tester_ =
+        std::make_unique<page_load_metrics::PageLoadMetricsObserverTester>(
+            web_contents(),
+            base::BindRepeating(
+                &AdsPageLoadMetricsObserverTest::RegisterObservers,
+                base::Unretained(this)));
+    ConfigureAsSubresourceFilterOnlyURL(GURL(kAdUrl));
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* NavigateFrame(const std::string& url,
+                                 content::RenderFrameHost* frame) {
+    auto navigation_simulator =
+        NavigationSimulator::CreateRendererInitiated(GURL(url), frame);
+    navigation_simulator->Commit();
+    return navigation_simulator->GetFinalRenderFrameHost();
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* NavigateMainFrame(const std::string& url) {
+    return NavigateFrame(url, web_contents()->GetMainFrame());
+  }
+
+  // Frame creation doesn't trigger a mojo call since unit tests have no render
+  // process. Just mock them for now.
+  void OnAdSubframeDetected(RenderFrameHost* render_frame_host) {
+    subresource_filter::SubresourceFilterObserverManager::FromWebContents(
+        web_contents())
+        ->NotifyAdSubframeDetected(render_frame_host);
+  }
+
+  // Returns the final RenderFrameHost after navigation commits.
+  RenderFrameHost* CreateAndNavigateSubFrame(const std::string& url,
+                                             content::RenderFrameHost* parent) {
+    RenderFrameHost* subframe =
+        RenderFrameHostTester::For(parent)->AppendChild("frame_name");
+    auto navigation_simulator =
+        NavigationSimulator::CreateRendererInitiated(GURL(url), subframe);
+    navigation_simulator->Commit();
+    return navigation_simulator->GetFinalRenderFrameHost();
+  }
+
+  void ResourceDataUpdate(RenderFrameHost* render_frame_host,
+                          ResourceCached resource_cached,
+                          int resource_size_in_kbyte,
+                          std::string mime_type = "",
+                          bool is_ad_resource = false) {
+    std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources;
+    page_load_metrics::mojom::ResourceDataUpdatePtr resource =
+        page_load_metrics::mojom::ResourceDataUpdate::New();
+    resource->received_data_length =
+        static_cast<bool>(resource_cached) ? 0 : resource_size_in_kbyte << 10;
+    resource->delta_bytes = resource->received_data_length;
+    resource->encoded_body_length = resource_size_in_kbyte << 10;
+    resource->reported_as_ad_resource = is_ad_resource;
+    resource->is_complete = true;
+    resource->was_fetched_via_cache = static_cast<bool>(resource_cached);
+    resource->mime_type = mime_type;
+    resource->is_primary_frame_resource = true;
+    resources.push_back(std::move(resource));
+    tester_->SimulateResourceDataUseUpdate(resources, render_frame_host);
+  }
+
+  void TimingUpdate(const page_load_metrics::mojom::PageLoadTiming& timing) {
+    tester_->SimulateTimingUpdate(timing);
+  }
+
+  page_load_metrics::PageLoadMetricsObserverTester* tester() {
+    return tester_.get();
+  }
+
+  base::HistogramTester& histogram_tester() { return histogram_tester_; }
+
+  AdsPageLoadMetricsObserver* ads_observer_ = nullptr;
+
+ private:
+  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) {
+    auto observer = std::make_unique<AdsPageLoadMetricsObserver>();
+    ads_observer_ = observer.get();
+    tracker->AddObserver(std::move(observer));
+  }
+
+  base::HistogramTester histogram_tester_;
+  std::unique_ptr<page_load_metrics::PageLoadMetricsObserverTester> tester_;
+
+  DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserverTest);
+};
+
+TEST_F(AdsPageLoadMetricsObserverTest, PageWithNoAds) {
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  RenderFrameHost* frame1 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
+  RenderFrameHost* frame2 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(frame1, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(frame2, ResourceCached::NOT_CACHED, 10);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  TestHistograms(histogram_tester(), std::vector<ExpectedFrameBytes>(),
+                 0 /* non_ad_cached_kb */, 30 /* non_ad_uncached_kb */);
+
+  // Verify that other UMA wasn't written.
+  histogram_tester().ExpectTotalCount(
+      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 0);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest, PageWithAds) {
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  RenderFrameHost* frame1 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
+  RenderFrameHost* frame2 = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(frame1, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(frame2, ResourceCached::NOT_CACHED, 10);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  TestHistograms(histogram_tester(), {{0, 10}}, 0 /* non_ad_cached_kb */,
+                 20 /* non_ad_uncached_kb */);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest, ResourceBeforeAdFrameCommits) {
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+
+  // Create subframe and load resource before commit.
+  RenderFrameHost* subframe =
+      RenderFrameHostTester::For(main_frame)->AppendChild("foo");
+  auto navigation_simulator =
+      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe);
+  ResourceDataUpdate(subframe, ResourceCached::NOT_CACHED, 10);
+  navigation_simulator->Commit();
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  TestHistograms(histogram_tester(), {{0, 10}}, 0 /* non_ad_cached_kb */,
+                 10 /*non_ad_uncached_kb*/);
+}
+
+// Test that the cross-origin ad subframe navigation metric works as it's
+// supposed to, triggering a false addition with each ad that's in the same
+// origin as the main page, and a true when when the ad has a separate origin.
+TEST_F(AdsPageLoadMetricsObserverTest, AdsOriginStatusMetrics) {
+  const char kCrossOriginHistogramId[] =
+      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AdFrames.PerFrame."
+      "OriginStatus";
+
+  // Test that when the main frame origin is different from a direct ad
+  // subframe it is correctly identified as cross-origin, but do not count
+  // indirect ad subframes.
+  {
+    base::HistogramTester histograms;
+    RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+    RenderFrameHost* ad_sub_frame =
+        CreateAndNavigateSubFrame(kAdUrl, main_frame);
+    ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(ad_sub_frame, ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, ad_sub_frame),
+                       ResourceCached::NOT_CACHED, 10);
+    // Trigger histograms by navigating away, then test them.
+    NavigateFrame(kAdUrl, main_frame);
+    histograms.ExpectUniqueSample(kCrossOriginHistogramId,
+                                  FrameData::OriginStatus::kCross, 1);
+  }
+
+  // Add a non-ad subframe and an ad subframe and make sure the total count
+  // only adjusts by one.
+  {
+    base::HistogramTester histograms;
+    RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+    ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, main_frame),
+                       ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(CreateAndNavigateSubFrame(kNonAdUrl, main_frame),
+                       ResourceCached::NOT_CACHED, 10);
+    // Trigger histograms by navigating away, then test them.
+    NavigateFrame(kAdUrl, main_frame);
+    histograms.ExpectUniqueSample(kCrossOriginHistogramId,
+                                  FrameData::OriginStatus::kCross, 1);
+  }
+
+  // Add an ad subframe in the same origin as the parent frame and make sure it
+  // gets identified as non-cross-origin. Note: top-level navigations are never
+  // considered to be ads.
+  {
+    base::HistogramTester histograms;
+    RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrlSameOrigin);
+    ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+    ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, main_frame),
+                       ResourceCached::NOT_CACHED, 10);
+    // Trigger histograms by navigating away, then test them.
+    NavigateFrame(kAdUrl, main_frame);
+    histograms.ExpectUniqueSample(kCrossOriginHistogramId,
+                                  FrameData::OriginStatus::kSame, 1);
+  }
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest, PageWithAdFrameThatRenavigates) {
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
+
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED, 10);
+
+  // Navigate the ad frame again.
+  ad_frame = NavigateFrame(kNonAdUrl, ad_frame);
+
+  // In total, 30KB for entire page and 20 in one ad frame.
+  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED, 10);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
+                 10 /* non_ad_uncached_kb */);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest, PageWithNonAdFrameThatRenavigatesToAd) {
+  // Main frame.
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+
+  // Sub frame that is not an ad.
+  RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
+
+  // Child of the sub-frame that is an ad.
+  RenderFrameHost* sub_frame_child_ad =
+      CreateAndNavigateSubFrame(kAdUrl, sub_frame);
+
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(sub_frame_child_ad, ResourceCached::NOT_CACHED, 10);
+
+  // Navigate the subframe again, this time it's an ad.
+  sub_frame = NavigateFrame(kAdUrl, sub_frame);
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
+
+  // In total, 40KB was loaded for the entire page and 20KB from ad
+  // frames (the original child ad frame and the renavigated frame which
+  // turned into an ad).
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  TestHistograms(histogram_tester(), {{0, 10}, {0, 10}},
+                 0 /* non_ad_cached_kb */, 20 /* non_ad_uncached_kb */);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedNavigation) {
+  // If the first navigation in a frame is aborted, keep track of its bytes.
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+
+  // Create an ad subframe that aborts before committing.
+  RenderFrameHost* subframe_ad =
+      RenderFrameHostTester::For(main_frame)->AppendChild("foo");
+  auto navigation_simulator =
+      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe_ad);
+  // The sub-frame renavigates before it commits.
+  navigation_simulator->Start();
+  OnAdSubframeDetected(subframe_ad);
+  navigation_simulator->Fail(net::ERR_ABORTED);
+
+  // Load resources for the aborted frame (e.g., simulate the navigation
+  // aborting due to a doc.write during provisional navigation). They should
+  // be counted.
+  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
+                 10 /* non_ad_uncached_kb */);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedSecondNavigationForFrame) {
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+
+  // Sub frame that is not an ad.
+  RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
+
+  // Now navigate (and abort) the subframe to an ad.
+  auto navigation_simulator =
+      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), sub_frame);
+  // The sub-frame renavigates before it commits.
+  navigation_simulator->Start();
+  OnAdSubframeDetected(sub_frame);
+  navigation_simulator->Fail(net::ERR_ABORTED);
+
+  // Load resources for the aborted frame (e.g., simulate the navigation
+  // aborting due to a doc.write during provisional navigation). Since the
+  // frame attempted to load an ad, the frame is tagged forever as an ad.
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
+  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
+                 20 /* non_ad_uncached_kb */);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest, TwoResourceLoadsBeforeCommit) {
+  // Main frame.
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
+
+  // Now open a subframe and have its resource load before notification of
+  // navigation finishing.
+  RenderFrameHost* subframe_ad =
+      RenderFrameHostTester::For(main_frame)->AppendChild("foo");
+  auto navigation_simulator =
+      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe_ad);
+  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
+
+  // The sub-frame renavigates before it commits.
+  navigation_simulator->Start();
+  OnAdSubframeDetected(subframe_ad);
+  navigation_simulator->Fail(net::ERR_ABORTED);
+
+  // Renavigate the subframe to a successful commit. But again, the resource
+  // loads before the observer sees the finished navigation.
+  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
+  NavigateFrame(kNonAdUrl, subframe_ad);
+
+  // Navigate again to trigger histograms.
+  NavigateFrame(kNonAdUrl, main_frame);
+
+  TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
+                 10 /* non_ad_uncached_kb */);
+}
+
+// This tests an issue that is believed to be the cause of
+// https://crbug.com/721369. The issue is that a frame from a previous
+// navigation might commit during a new navigation, and the ads metrics won't
+// know about the frame's parent (because it doesn't exist in the page).
+TEST_F(AdsPageLoadMetricsObserverTest, FrameWithNoParent) {
+  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
+  RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
+
+  // Renavigate the child, but, while navigating, the main frame renavigates.
+  RenderFrameHost* child_of_subframe =
+      RenderFrameHostTester::For(sub_frame)->AppendChild("foo");
+  auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
+      GURL(kAdUrl), child_of_subframe);
+  navigation_simulator->Start();
+
+  // Main frame renavigates.
+  NavigateMainFrame(kNonAdUrl);
+
+  // Child frame commits.
+  navigation_simulator->Commit();
+  child_of_subframe = navigation_simulator->GetFinalRenderFrameHost();
+
+  // Test that a resource loaded into an unknown frame doesn't cause any
+  // issues.
+  ResourceDataUpdate(child_of_subframe, ResourceCached::NOT_CACHED, 10);
+}
+
+TEST_F(AdsPageLoadMetricsObserverTest, MainFrameResource) {
+  // Start main-frame navigation
+  auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
+      GURL(kNonAdUrl), web_contents()->GetMainFrame());
+  navigation_simulator->Start();
+  navigation_simulator->Commit();
+
+  ResourceDataUpdate(navigation_simulator->GetFinalRenderFrameHost(),
+                     ResourceCached::NOT_CACHED, 10);
+
+  NavigateMainFrame(kNonAdUrl);
+
+  // We only log histograms if we observed bytes for the page. Verify that the
+  // main frame resource was properly tracked and attributed.
+  histogram_tester().ExpectUniqueSample(
+      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AnyParentFrame."
+      "AdFrames",
+      0, 1);
+  // There shouldn't be any other histograms for a page with no ad
+  // resources.
+  EXPECT_EQ(1u, histogram_tester()
+                    .GetTotalCountsForPrefix("PageLoad.Clients.Ads.")
+                    .size());
+}
+
+// Make sure that ads histograms aren't recorded if the tracker never commits
+// (see https://crbug.com/723219).
+TEST_F(AdsPageLoadMetricsObserverTest, NoHistogramWithoutCommit) {
+  {
+    // Once the metrics observer has the GlobalRequestID, throttle.
+    content::TestNavigationThrottleInserter throttle_inserter(
+        web_contents(),
+        base::BindRepeating(&ResourceLoadingCancellingThrottle::Create));
+
+    // Start main-frame navigation. The commit will defer after calling
+    // WillProcessNavigationResponse, it will load a resource, and then the
+    // throttle will cancel the commit.
+    SimulateNavigateAndCommit(GURL(kNonAdUrl), main_rfh());
+  }
+
+  // Force navigation to a new page to make sure OnComplete() runs for the
+  // previous failed navigation.
+  NavigateMainFrame(kNonAdUrl);
+
+  // There shouldn't be any histograms for an aborted main frame.
+  EXPECT_EQ(0u, histogram_tester()
+                    .GetTotalCountsForPrefix("PageLoad.Clients.Ads.")
+                    .size());
+}
+
+// Frames that are disallowed (and filtered) by the subresource filter should
+// not be counted.
+TEST_F(AdsPageLoadMetricsObserverTest, FilterAds_DoNotLogMetrics) {
+  ConfigureAsSubresourceFilterOnlyURL(GURL(kNonAdUrl));
+  NavigateMainFrame(kNonAdUrl);
+
+  ResourceDataUpdate(main_rfh(), ResourceCached::NOT_CACHED, 10);
+
+  RenderFrameHost* subframe =
+      RenderFrameHostTester::For(main_rfh())->AppendChild("foo");
+  std::unique_ptr<NavigationSimulator> simulator =
+      NavigationSimulator::CreateRendererInitiated(GURL(kDefaultDisallowedUrl),
+                                                   subframe);
+  ResourceDataUpdate(subframe, ResourceCached::CACHED, 10);
+  simulator->Commit();
+
+  EXPECT_NE(content::NavigationThrottle::PROCEED,
+            simulator->GetLastThrottleCheckResult());
+
+  NavigateMainFrame(kNonAdUrl);
+  TestHistograms(histogram_tester(), std::vector<ExpectedFrameBytes>(),
+                 0u /* non_ad_cached_kb */, 0u /* non_ad_uncached_kb */);
+}
+
+// UKM metrics for ad page load are recorded correctly.
+TEST_F(AdsPageLoadMetricsObserverTest, AdPageLoadUKM) {
+  ukm::TestAutoSetUkmRecorder ukm_recorder;
+  NavigateMainFrame(kNonAdUrl);
+
+  page_load_metrics::mojom::PageLoadTiming timing;
+  page_load_metrics::InitPageLoadTimingForTest(&timing);
+  timing.navigation_start = base::Time::Now();
+  timing.response_start = base::TimeDelta::FromSeconds(0);
+  timing.interactive_timing->interactive = base::TimeDelta::FromSeconds(0);
+  PopulateRequiredTimingFields(&timing);
+  TimingUpdate(timing);
+  ResourceDataUpdate(
+      main_rfh(), ResourceCached::NOT_CACHED, 10 /* resource_size_in_kbyte */,
+      "application/javascript" /* mime_type */, false /* is_ad_resource */);
+  ResourceDataUpdate(
+      main_rfh(), ResourceCached::NOT_CACHED, 10 /* resource_size_in_kbyte */,
+      "application/javascript" /* mime_type */, true /* is_ad_resource */);
+  ResourceDataUpdate(main_rfh(), ResourceCached::NOT_CACHED,
+                     10 /* resource_size_in_kbyte */,
+                     "video/webm" /* mime_type */, true /* is_ad_resource */);
+  NavigateMainFrame(kNonAdUrl);
+
+  auto entries =
+      ukm_recorder.GetEntriesByName(ukm::builders::AdPageLoad::kEntryName);
+  EXPECT_EQ(1u, entries.size());
+
+  EXPECT_EQ(*ukm_recorder.GetEntryMetric(
+                entries.front(), ukm::builders::AdPageLoad::kTotalBytesName),
+            30);
+  EXPECT_EQ(*ukm_recorder.GetEntryMetric(
+                entries.front(), ukm::builders::AdPageLoad::kAdBytesName),
+            20);
+  EXPECT_EQ(
+      *ukm_recorder.GetEntryMetric(
+          entries.front(), ukm::builders::AdPageLoad::kAdJavascriptBytesName),
+      10);
+  EXPECT_EQ(*ukm_recorder.GetEntryMetric(
+                entries.front(), ukm::builders::AdPageLoad::kAdVideoBytesName),
+            10);
+  EXPECT_GT(
+      *ukm_recorder.GetEntryMetric(
+          entries.front(), ukm::builders::AdPageLoad::kAdBytesPerSecondName),
+      0);
+  EXPECT_GT(
+      *ukm_recorder.GetEntryMetric(
+          entries.front(),
+          ukm::builders::AdPageLoad::kAdBytesPerSecondAfterInteractiveName),
+      0);
+}
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
new file mode 100644
index 0000000..757a0fd
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.cc
@@ -0,0 +1,57 @@
+// 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/page_load_metrics/observers/ad_metrics/frame_data.h"
+#include <algorithm>
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "url/gurl.h"
+
+FrameData::FrameData(FrameTreeNodeId frame_tree_node_id)
+    : frame_bytes_(0u),
+      frame_network_bytes_(0u),
+      frame_tree_node_id_(frame_tree_node_id),
+      origin_status_(OriginStatus::kUnknown),
+      frame_navigated_(false),
+      user_activation_status_(UserActivationStatus::kNoActivation),
+      visibility_(FrameVisibility::kVisible),
+      frame_size_(gfx::Size()) {}
+
+FrameData::~FrameData() = default;
+
+void FrameData::UpdateForNavigation(content::RenderFrameHost* render_frame_host,
+                                    bool frame_navigated) {
+  frame_navigated_ = frame_navigated;
+  if (!render_frame_host)
+    return;
+
+  SetDisplayState(render_frame_host->IsFrameDisplayNone());
+  if (render_frame_host->GetFrameSize())
+    set_frame_size(*(render_frame_host->GetFrameSize()));
+
+  // For frames triggered on render, their origin is their parent's origin.
+  origin_status_ =
+      AdsPageLoadMetricsObserver::IsSubframeSameOriginToMainFrame(
+          render_frame_host, !frame_navigated /* use_parent_origin */)
+          ? OriginStatus::kSame
+          : OriginStatus::kCross;
+}
+
+void FrameData::ProcessResourceLoadInFrame(
+    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
+  frame_bytes_ += resource->delta_bytes;
+  frame_network_bytes_ += resource->delta_bytes;
+
+  // Report cached resource body bytes to overall frame bytes.
+  if (resource->is_complete && resource->was_fetched_via_cache)
+    frame_bytes_ += resource->encoded_body_length;
+}
+
+void FrameData::SetDisplayState(bool is_display_none) {
+  if (is_display_none)
+    visibility_ = FrameVisibility::kDisplayNone;
+  else
+    visibility_ = FrameVisibility::kVisible;
+}
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
new file mode 100644
index 0000000..a0d86aa
--- /dev/null
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h
@@ -0,0 +1,102 @@
+// 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_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_FRAME_DATA_H_
+#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_FRAME_DATA_H_
+
+#include "base/macros.h"
+#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
+#include "chrome/common/page_load_metrics/page_load_metrics.mojom.h"
+#include "ui/gfx/geometry/size.h"
+
+// Store information received for a frame on the page. FrameData is meant
+// to represent a frame along with it's entire subtree.
+class FrameData {
+ public:
+  // The origin of the ad relative to the main frame's origin.
+  // Note: Logged to UMA, keep in sync with CrossOriginAdStatus in enums.xml.
+  //   Add new entries to the end, and do not renumber.
+  enum class OriginStatus {
+    kUnknown = 0,
+    kSame = 1,
+    kCross = 2,
+    kMaxValue = kCross,
+  };
+
+  // Whether or not the frame has a display: none styling. These values are
+  // persisted to logs. Entries should not be renumbered and numeric values
+  // should never be reused.
+  enum class FrameVisibility {
+    kDisplayNone = 0,
+    kVisible = 1,
+    kMaxValue = kVisible,
+  };
+
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused. For any additions, also update the
+  // corresponding PageEndReason enum in enums.xml.
+  enum class UserActivationStatus {
+    kNoActivation = 0,
+    kReceivedActivation = 1,
+    kMaxValue = kReceivedActivation,
+  };
+
+  using FrameTreeNodeId =
+      page_load_metrics::PageLoadMetricsObserver::FrameTreeNodeId;
+
+  explicit FrameData(FrameTreeNodeId frame_tree_node_id);
+  ~FrameData();
+
+  // Update the metadata of this frame if it is being navigated.
+  void UpdateForNavigation(content::RenderFrameHost* render_frame_host,
+                           bool frame_navigated);
+
+  // Updates the number of bytes loaded in the frame given a resource load.
+  void ProcessResourceLoadInFrame(
+      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
+
+  // Sets the display state of the frame and updates its visibility state.
+  void SetDisplayState(bool is_display_none);
+
+  // Records that the sticky user activation bit has been set on the frame. This
+  // cannot be unset.
+  void set_received_user_activation() {
+    user_activation_status_ = UserActivationStatus::kReceivedActivation;
+  };
+
+  FrameTreeNodeId frame_tree_node_id() const { return frame_tree_node_id_; }
+
+  OriginStatus origin_status() const { return origin_status_; }
+
+  size_t frame_bytes() const { return frame_bytes_; }
+
+  size_t frame_network_bytes() const { return frame_network_bytes_; }
+
+  UserActivationStatus user_activation_status() const {
+    return user_activation_status_;
+  }
+
+  bool frame_navigated() const { return frame_navigated_; }
+
+  FrameVisibility visibility() const { return visibility_; }
+
+  void set_frame_size(gfx::Size frame_size) { frame_size_ = frame_size; }
+
+  gfx::Size frame_size() const { return frame_size_; }
+
+ private:
+  // Total bytes used to load resources on the page, including headers.
+  size_t frame_bytes_;
+  size_t frame_network_bytes_;
+  const FrameTreeNodeId frame_tree_node_id_;
+  OriginStatus origin_status_;
+  bool frame_navigated_;
+  UserActivationStatus user_activation_status_;
+  FrameVisibility visibility_;
+  gfx::Size frame_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(FrameData);
+};
+
+#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_AD_METRICS_FRAME_DATA_H_
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
deleted file mode 100644
index dbf34e1..0000000
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.cc
+++ /dev/null
@@ -1,724 +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/page_load_metrics/observers/ads_page_load_metrics_observer.h"
-
-#include <algorithm>
-#include <string>
-#include <utility>
-
-#include "base/feature_list.h"
-#include "base/logging.h"
-#include "base/strings/string_util.h"
-#include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
-#include "chrome/browser/page_load_metrics/page_load_metrics_util.h"
-#include "components/subresource_filter/core/common/common_features.h"
-#include "components/ukm/content/source_url_recorder.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "net/base/mime_util.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "services/metrics/public/cpp/ukm_recorder.h"
-#include "third_party/blink/public/common/download/download_stats.h"
-#include "third_party/blink/public/common/frame/sandbox_flags.h"
-#include "third_party/blink/public/common/mime_util/mime_util.h"
-#include "url/gurl.h"
-
-namespace {
-
-#define ADS_HISTOGRAM(suffix, hist_macro, value) \
-  hist_macro("PageLoad.Clients.Ads." suffix, value);
-
-#define RESOURCE_BYTES_HISTOGRAM(suffix, was_cached, value)                \
-  if (was_cached) {                                                        \
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Cache." suffix, value);   \
-  } else {                                                                 \
-    PAGE_BYTES_HISTOGRAM("Ads.ResourceUsage.Size.Network." suffix, value); \
-  }
-
-// Finds the RenderFrameHost for the handle, possibly using the FrameTreeNode
-// ID directly if the the handle has not been committed.
-// NOTE: Unsafe with respect to security privileges.
-content::RenderFrameHost* FindFrameMaybeUnsafe(
-    content::NavigationHandle* handle) {
-  return handle->HasCommitted()
-             ? handle->GetRenderFrameHost()
-             : handle->GetWebContents()->UnsafeFindFrameByFrameTreeNodeId(
-                   handle->GetFrameTreeNodeId());
-}
-
-bool IsSubframeSameOriginToMainFrame(content::RenderFrameHost* sub_host,
-                                     bool use_parent_origin) {
-  DCHECK(sub_host);
-  content::RenderFrameHost* main_host =
-      content::WebContents::FromRenderFrameHost(sub_host)->GetMainFrame();
-  if (use_parent_origin)
-    sub_host = sub_host->GetParent();
-  url::Origin subframe_origin = sub_host->GetLastCommittedOrigin();
-  url::Origin mainframe_origin = main_host->GetLastCommittedOrigin();
-  return subframe_origin.IsSameOriginWith(mainframe_origin);
-}
-
-void RecordSingleFeatureUsage(content::RenderFrameHost* rfh,
-                              blink::mojom::WebFeature web_feature) {
-  page_load_metrics::mojom::PageLoadFeatures page_load_features(
-      {web_feature}, {} /* css_properties */, {} /* animated_css_properties */);
-  page_load_metrics::MetricsWebContentsObserver::RecordFeatureUsage(
-      rfh, page_load_features);
-}
-
-using ResourceMimeType = AdsPageLoadMetricsObserver::ResourceMimeType;
-
-}  // namespace
-
-AdsPageLoadMetricsObserver::AdFrameData::AdFrameData(
-    FrameTreeNodeId frame_tree_node_id,
-    AdOriginStatus origin_status,
-    bool frame_navigated)
-    : frame_bytes(0u),
-      frame_network_bytes(0u),
-      frame_tree_node_id(frame_tree_node_id),
-      origin_status(origin_status),
-      frame_navigated(frame_navigated),
-      user_activation_status(UserActivationStatus::kNoActivation),
-      is_display_none(false),
-      frame_size(gfx::Size()) {}
-
-// static
-std::unique_ptr<AdsPageLoadMetricsObserver>
-AdsPageLoadMetricsObserver::CreateIfNeeded() {
-  if (!base::FeatureList::IsEnabled(subresource_filter::kAdTagging))
-    return nullptr;
-  return std::make_unique<AdsPageLoadMetricsObserver>();
-}
-
-AdsPageLoadMetricsObserver::AdsPageLoadMetricsObserver()
-    : subresource_observer_(this) {}
-
-AdsPageLoadMetricsObserver::~AdsPageLoadMetricsObserver() = default;
-
-page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-AdsPageLoadMetricsObserver::OnStart(
-    content::NavigationHandle* navigation_handle,
-    const GURL& currently_committed_url,
-    bool started_in_foreground) {
-  auto* observer_manager =
-      subresource_filter::SubresourceFilterObserverManager::FromWebContents(
-          navigation_handle->GetWebContents());
-  // |observer_manager| isn't constructed if the feature for subresource
-  // filtering isn't enabled.
-  if (observer_manager)
-    subresource_observer_.Add(observer_manager);
-  return CONTINUE_OBSERVING;
-}
-
-page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-AdsPageLoadMetricsObserver::OnCommit(
-    content::NavigationHandle* navigation_handle,
-    ukm::SourceId source_id) {
-  DCHECK(ad_frames_data_.empty());
-
-  committed_ = true;
-
-  // The main frame is never considered an ad.
-  ad_frames_data_[navigation_handle->GetFrameTreeNodeId()] = nullptr;
-  ProcessOngoingNavigationResource(navigation_handle->GetFrameTreeNodeId());
-  return CONTINUE_OBSERVING;
-}
-
-// Given an ad being triggered for a frame or navigation, get its AdFrameData
-// and record it into the appropriate data structures.
-void AdsPageLoadMetricsObserver::RecordAdFrameData(
-    FrameTreeNodeId ad_id,
-    bool is_adframe,
-    content::RenderFrameHost* ad_host,
-    bool frame_navigated) {
-  // If an existing subframe is navigating and it was an ad previously that
-  // hasn't navigated yet, then we need to update it.
-  const auto& id_and_data = ad_frames_data_.find(ad_id);
-  AdFrameData* previous_data = nullptr;
-  if (id_and_data != ad_frames_data_.end() && id_and_data->second) {
-    DCHECK(frame_navigated);
-    if (id_and_data->second->frame_navigated) {
-      ProcessOngoingNavigationResource(ad_id);
-      return;
-    }
-    previous_data = id_and_data->second;
-  }
-
-  // Determine who the parent frame's ad ancestor is.  If we don't know who it
-  // is, return, such as with a frame from a previous navigation.
-  content::RenderFrameHost* parent_frame_host =
-      ad_host ? ad_host->GetParent() : nullptr;
-  const auto& parent_id_and_data =
-      parent_frame_host
-          ? ad_frames_data_.find(parent_frame_host->GetFrameTreeNodeId())
-          : ad_frames_data_.end();
-  bool parent_exists = parent_id_and_data != ad_frames_data_.end();
-  if (!parent_exists)
-    return;
-
-  // This frame is not nested within an ad frame but is itself an ad.
-  AdFrameData* ad_data = parent_id_and_data->second;
-  if (!ad_data && is_adframe) {
-    AdOriginStatus origin_status = AdOriginStatus::kUnknown;
-    if (ad_host) {
-      // For ads triggered on render, their origin is their parent's origin.
-      origin_status = IsSubframeSameOriginToMainFrame(ad_host, !frame_navigated)
-                          ? AdOriginStatus::kSame
-                          : AdOriginStatus::kCross;
-    }
-
-    // If data existed already, update it and exit, otherwise, add it.
-    if (previous_data) {
-      previous_data->origin_status = origin_status;
-      previous_data->frame_navigated = frame_navigated;
-      return;
-    }
-    ad_frames_data_storage_.emplace_back(ad_id, origin_status, frame_navigated);
-    ad_data = &ad_frames_data_storage_.back();
-
-    if (ad_host) {
-      ad_data->is_display_none = ad_host->IsFrameDisplayNone();
-      if (ad_host->GetFrameSize())
-        ad_data->frame_size = *(ad_host->GetFrameSize());
-    }
-  }
-  // If there was previous data, then we don't want to overwrite this frame.
-  if (!previous_data)
-    ad_frames_data_[ad_id] = ad_data;
-}
-
-void AdsPageLoadMetricsObserver::ReadyToCommitNextNavigation(
-    content::NavigationHandle* navigation_handle) {
-  // When the renderer receives a CommitNavigation message for the main frame,
-  // all subframes detach and become display : none. Since this is not user
-  // visible, and not reflective of the frames state during the page lifetime,
-  // ignore any such messages when a navigation is about to commit.
-  if (!navigation_handle->IsInMainFrame())
-    return;
-  process_display_state_updates_ = false;
-}
-
-// Determine if the frame is part of an existing ad, the root of a new ad, or a
-// non-ad frame. Once a frame is labeled as an ad, it is always considered an
-// ad, even if it navigates to a non-ad page. This function labels all of a
-// page's frames, even those that fail to commit.
-void AdsPageLoadMetricsObserver::OnDidFinishSubFrameNavigation(
-    content::NavigationHandle* navigation_handle) {
-  FrameTreeNodeId frame_tree_node_id = navigation_handle->GetFrameTreeNodeId();
-  bool is_adframe = DetectAds(navigation_handle);
-
-  // NOTE: Frame look-up only used for determining cross-origin status, not
-  // granting security permissions.
-  content::RenderFrameHost* ad_host = FindFrameMaybeUnsafe(navigation_handle);
-
-  if (navigation_handle->IsDownload()) {
-    bool has_sandbox = ad_host->IsSandboxed(blink::WebSandboxFlags::kDownloads);
-    bool has_gesture = navigation_handle->HasUserGesture();
-
-    std::vector<blink::mojom::WebFeature> web_features;
-    if (is_adframe) {
-      // Note: Here it covers download due to navigations to non-web-renderable
-      // content. These two features can also be logged from blink for download
-      // originated from clicking on <a download> link that results in direct
-      // download.
-      blink::mojom::WebFeature web_feature =
-          has_gesture
-              ? blink::mojom::WebFeature::kDownloadInAdFrameWithUserGesture
-              : blink::mojom::WebFeature::kDownloadInAdFrameWithoutUserGesture;
-      RecordSingleFeatureUsage(ad_host, web_feature);
-    }
-    if (has_sandbox) {
-      blink::mojom::WebFeature web_feature =
-          has_gesture ? blink::mojom::WebFeature::
-                            kNavigationDownloadInSandboxWithUserGesture
-                      : blink::mojom::WebFeature::
-                            kNavigationDownloadInSandboxWithoutUserGesture;
-      RecordSingleFeatureUsage(ad_host, web_feature);
-    }
-
-    blink::DownloadStats::SubframeDownloadFlags flags;
-    flags.has_sandbox = has_sandbox;
-    flags.is_cross_origin =
-        !IsSubframeSameOriginToMainFrame(ad_host, /*use_parent_origin=*/false);
-    flags.is_ad_frame = is_adframe;
-    flags.has_gesture = has_gesture;
-    blink::DownloadStats::RecordSubframeDownloadFlags(
-        flags,
-        ukm::GetSourceIdForWebContentsDocument(
-            navigation_handle->GetWebContents()),
-        ukm::UkmRecorder::Get());
-  }
-
-  RecordAdFrameData(frame_tree_node_id, is_adframe, ad_host,
-                    /*frame_navigated=*/true);
-  ProcessOngoingNavigationResource(frame_tree_node_id);
-}
-
-void AdsPageLoadMetricsObserver::FrameReceivedFirstUserActivation(
-    content::RenderFrameHost* render_frame_host) {
-  const auto& id_and_data =
-      ad_frames_data_.find(render_frame_host->GetFrameTreeNodeId());
-  if (id_and_data == ad_frames_data_.end())
-    return;
-  AdFrameData* ancestor_data = id_and_data->second;
-  if (ancestor_data) {
-    ancestor_data->user_activation_status =
-        UserActivationStatus::kReceivedActivation;
-  }
-}
-
-void AdsPageLoadMetricsObserver::OnDidInternalNavigationAbort(
-    content::NavigationHandle* navigation_handle) {
-  // Main frame navigation
-  if (navigation_handle->IsDownload()) {
-    content::RenderFrameHost* rfh = FindFrameMaybeUnsafe(navigation_handle);
-    bool has_sandbox = rfh->IsSandboxed(blink::WebSandboxFlags::kDownloads);
-    bool has_gesture = navigation_handle->HasUserGesture();
-    if (has_sandbox) {
-      blink::mojom::WebFeature web_feature =
-          has_gesture ? blink::mojom::WebFeature::
-                            kNavigationDownloadInSandboxWithUserGesture
-                      : blink::mojom::WebFeature::
-                            kNavigationDownloadInSandboxWithoutUserGesture;
-      RecordSingleFeatureUsage(rfh, web_feature);
-    }
-
-    blink::DownloadStats::MainFrameDownloadFlags flags;
-    flags.has_sandbox = has_sandbox;
-    flags.has_gesture = has_gesture;
-    blink::DownloadStats::RecordMainFrameDownloadFlags(
-        flags,
-        ukm::GetSourceIdForWebContentsDocument(
-            navigation_handle->GetWebContents()),
-        ukm::UkmRecorder::Get());
-  }
-}
-
-page_load_metrics::PageLoadMetricsObserver::ObservePolicy
-AdsPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& extra_info) {
-  // The browser may come back, but there is no guarantee. To be safe, record
-  // what we have now and ignore future changes to this navigation.
-  if (extra_info.did_commit) {
-    if (timing.response_start)
-      time_commit_ = timing.navigation_start + *timing.response_start;
-    RecordHistograms(extra_info.source_id);
-  }
-
-  return STOP_OBSERVING;
-}
-
-void AdsPageLoadMetricsObserver::OnComplete(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (info.did_commit && timing.response_start)
-    time_commit_ = timing.navigation_start + *timing.response_start;
-  RecordHistograms(info.source_id);
-}
-
-void AdsPageLoadMetricsObserver::OnResourceDataUseObserved(
-    FrameTreeNodeId frame_tree_node_id,
-    const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
-        resources) {
-  for (auto const& resource : resources)
-    UpdateResource(frame_tree_node_id, resource);
-}
-
-void AdsPageLoadMetricsObserver::OnSubframeNavigationEvaluated(
-    content::NavigationHandle* navigation_handle,
-    subresource_filter::LoadPolicy load_policy,
-    bool is_ad_subframe) {
-  // We don't track DISALLOW frames because their resources won't be loaded
-  // and therefore would provide bad histogram data. Note that WOULD_DISALLOW
-  // is only seen in dry runs.
-  if (is_ad_subframe &&
-      load_policy != subresource_filter::LoadPolicy::DISALLOW) {
-    unfinished_subresource_ad_frames_.insert(
-        navigation_handle->GetFrameTreeNodeId());
-  }
-}
-
-void AdsPageLoadMetricsObserver::OnPageInteractive(
-    const page_load_metrics::mojom::PageLoadTiming& timing,
-    const page_load_metrics::PageLoadExtraInfo& info) {
-  if (timing.interactive_timing->interactive) {
-    time_interactive_ =
-        timing.navigation_start + *timing.interactive_timing->interactive;
-  }
-}
-
-void AdsPageLoadMetricsObserver::FrameDisplayStateChanged(
-    content::RenderFrameHost* render_frame_host,
-    bool is_display_none) {
-  if (!process_display_state_updates_)
-    return;
-  const auto& id_and_data =
-      ad_frames_data_.find(render_frame_host->GetFrameTreeNodeId());
-  if (id_and_data == ad_frames_data_.end())
-    return;
-  AdFrameData* ancestor_data = id_and_data->second;
-  if (ancestor_data && render_frame_host->GetFrameTreeNodeId() ==
-                           ancestor_data->frame_tree_node_id) {
-    ancestor_data->is_display_none = is_display_none;
-  }
-}
-
-void AdsPageLoadMetricsObserver::FrameSizeChanged(
-    content::RenderFrameHost* render_frame_host,
-    const gfx::Size& frame_size) {
-  const auto& id_and_data =
-      ad_frames_data_.find(render_frame_host->GetFrameTreeNodeId());
-  if (id_and_data == ad_frames_data_.end())
-    return;
-  AdFrameData* ancestor_data = id_and_data->second;
-  if (ancestor_data && render_frame_host->GetFrameTreeNodeId() ==
-                           ancestor_data->frame_tree_node_id) {
-    ancestor_data->frame_size = frame_size;
-  }
-}
-
-void AdsPageLoadMetricsObserver::OnAdSubframeDetected(
-    content::RenderFrameHost* render_frame_host) {
-  FrameTreeNodeId frame_tree_node_id = render_frame_host->GetFrameTreeNodeId();
-  RecordAdFrameData(frame_tree_node_id, true /* is_adframe */,
-                    render_frame_host,
-                    /*frame_navigated=*/false);
-}
-
-void AdsPageLoadMetricsObserver::OnSubresourceFilterGoingAway() {
-  subresource_observer_.RemoveAll();
-}
-
-bool AdsPageLoadMetricsObserver::DetectSubresourceFilterAd(
-    FrameTreeNodeId frame_tree_node_id) {
-  return unfinished_subresource_ad_frames_.erase(frame_tree_node_id);
-}
-
-bool AdsPageLoadMetricsObserver::DetectAds(
-    content::NavigationHandle* navigation_handle) {
-  return DetectSubresourceFilterAd(navigation_handle->GetFrameTreeNodeId());
-}
-
-void AdsPageLoadMetricsObserver::ProcessResourceForFrame(
-    FrameTreeNodeId frame_tree_node_id,
-    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
-  const auto& id_and_data = ad_frames_data_.find(frame_tree_node_id);
-  if (id_and_data == ad_frames_data_.end()) {
-    if (resource->is_primary_frame_resource) {
-      // Only hold onto primary resources if their load has finished.
-      if (!resource->is_complete)
-        return;
-
-      // This resource request is the primary resource load for a frame that
-      // hasn't yet finished navigating. Hang onto the request info and replay
-      // it once the frame finishes navigating.
-      ongoing_navigation_resources_.emplace(
-          std::piecewise_construct, std::forward_as_tuple(frame_tree_node_id),
-          std::forward_as_tuple(resource.Clone()));
-    } else {
-      // This is unexpected, it could be:
-      // 1. a resource from a previous navigation that started its resource
-      //    load after this page started navigation.
-      // 2. possibly a resource from a document.written frame whose frame
-      //    failure message has yet to arrive. (uncertain of this)
-    }
-    return;
-  }
-
-  // |delta_bytes| only includes bytes used by the network.
-  page_bytes_ += resource->delta_bytes;
-  page_network_bytes_ += resource->delta_bytes;
-  if (resource->is_complete && resource->was_fetched_via_cache)
-    page_bytes_ += resource->encoded_body_length;
-
-  // Determine if the frame (or its ancestor) is an ad, if so attribute the
-  // bytes to the highest ad ancestor.
-  AdFrameData* ancestor_data = id_and_data->second;
-  if (!ancestor_data)
-    return;
-
-  ancestor_data->frame_bytes += resource->delta_bytes;
-  ancestor_data->frame_network_bytes += resource->delta_bytes;
-
-  // Report cached resource body bytes to overall frame bytes.
-  if (resource->is_complete && resource->was_fetched_via_cache)
-    ancestor_data->frame_bytes += resource->encoded_body_length;
-}
-
-AdsPageLoadMetricsObserver::ResourceMimeType
-AdsPageLoadMetricsObserver::GetResourceMimeType(
-    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
-  if (blink::IsSupportedImageMimeType(resource->mime_type))
-    return ResourceMimeType::kImage;
-  if (blink::IsSupportedJavascriptMimeType(resource->mime_type))
-    return ResourceMimeType::kJavascript;
-
-  std::string top_level_type;
-  std::string subtype;
-  // Categorize invalid mime types as "Other".
-  if (!net::ParseMimeTypeWithoutParameter(resource->mime_type, &top_level_type,
-                                          &subtype)) {
-    return ResourceMimeType::kOther;
-  }
-  if (top_level_type.compare("video") == 0)
-    return ResourceMimeType::kVideo;
-  else if (top_level_type.compare("text") == 0 && subtype.compare("css") == 0)
-    return ResourceMimeType::kCss;
-  else if (top_level_type.compare("text") == 0 && subtype.compare("html") == 0)
-    return ResourceMimeType::kHtml;
-  else
-    return ResourceMimeType::kOther;
-}
-
-void AdsPageLoadMetricsObserver::UpdateResource(
-    FrameTreeNodeId frame_tree_node_id,
-    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
-  ProcessResourceForFrame(frame_tree_node_id, resource);
-  auto it = page_resources_.find(resource->request_id);
-  // A new resource has been observed.
-  if (it == page_resources_.end())
-    total_number_page_resources_++;
-
-  if (resource->reported_as_ad_resource) {
-    // If the resource had already started loading, and is now labeled as an ad,
-    // but was not before, we need to account for all the previously received
-    // bytes.
-    bool is_new_ad =
-        (it != page_resources_.end()) && !it->second->reported_as_ad_resource;
-    int unaccounted_ad_bytes =
-        is_new_ad ? resource->received_data_length - resource->delta_bytes : 0;
-    page_ad_resource_bytes_ += resource->delta_bytes + unaccounted_ad_bytes;
-    if (resource->is_main_frame_resource) {
-      page_main_frame_ad_resource_bytes_ +=
-          resource->delta_bytes + unaccounted_ad_bytes;
-    }
-    if (!time_interactive_.is_null()) {
-      page_ad_resource_bytes_since_interactive_ +=
-          resource->delta_bytes + unaccounted_ad_bytes;
-    }
-    ResourceMimeType mime_type = GetResourceMimeType(resource);
-    if (mime_type == ResourceMimeType::kVideo)
-      page_ad_video_bytes_ += resource->delta_bytes + unaccounted_ad_bytes;
-    if (mime_type == ResourceMimeType::kJavascript)
-      page_ad_javascript_bytes_ += resource->delta_bytes + unaccounted_ad_bytes;
-  }
-
-  // Update resource map.
-  if (resource->is_complete) {
-    RecordResourceHistograms(resource);
-    if (it != page_resources_.end())
-      page_resources_.erase(it);
-  } else {
-    // Must clone resource so it will be accessible when the observer is
-    // destroyed.
-    if (it != page_resources_.end()) {
-      it->second = resource->Clone();
-    } else {
-      page_resources_.emplace(std::piecewise_construct,
-                              std::forward_as_tuple(resource->request_id),
-                              std::forward_as_tuple(resource->Clone()));
-    }
-  }
-}
-
-void AdsPageLoadMetricsObserver::RecordResourceMimeHistograms(
-    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
-  int64_t data_length = resource->was_fetched_via_cache
-                            ? resource->encoded_body_length
-                            : resource->received_data_length;
-  ResourceMimeType mime_type = GetResourceMimeType(resource);
-  if (mime_type == ResourceMimeType::kImage) {
-    RESOURCE_BYTES_HISTOGRAM("Mime.Image", resource->was_fetched_via_cache,
-                             data_length);
-  } else if (mime_type == ResourceMimeType::kJavascript) {
-    RESOURCE_BYTES_HISTOGRAM("Mime.JS", resource->was_fetched_via_cache,
-                             data_length);
-  } else if (mime_type == ResourceMimeType::kVideo) {
-    RESOURCE_BYTES_HISTOGRAM("Mime.Video", resource->was_fetched_via_cache,
-                             data_length);
-  } else if (mime_type == ResourceMimeType::kCss) {
-    RESOURCE_BYTES_HISTOGRAM("Mime.CSS", resource->was_fetched_via_cache,
-                             data_length);
-  } else if (mime_type == ResourceMimeType::kHtml) {
-    RESOURCE_BYTES_HISTOGRAM("Mime.HTML", resource->was_fetched_via_cache,
-                             data_length);
-  } else if (mime_type == ResourceMimeType::kOther) {
-    RESOURCE_BYTES_HISTOGRAM("Mime.Other", resource->was_fetched_via_cache,
-                             data_length);
-  }
-}
-
-void AdsPageLoadMetricsObserver::RecordResourceHistograms(
-    const page_load_metrics::mojom::ResourceDataUpdatePtr& resource) {
-  int64_t data_length = resource->was_fetched_via_cache
-                            ? resource->encoded_body_length
-                            : resource->received_data_length;
-  if (resource->is_main_frame_resource && resource->reported_as_ad_resource) {
-    RESOURCE_BYTES_HISTOGRAM("Mainframe.AdResource",
-                             resource->was_fetched_via_cache, data_length);
-  } else if (resource->is_main_frame_resource) {
-    RESOURCE_BYTES_HISTOGRAM("Mainframe.VanillaResource",
-                             resource->was_fetched_via_cache, data_length);
-  } else if (resource->reported_as_ad_resource) {
-    RESOURCE_BYTES_HISTOGRAM("Subframe.AdResource",
-                             resource->was_fetched_via_cache, data_length);
-  } else {
-    RESOURCE_BYTES_HISTOGRAM("Subframe.VanillaResource",
-                             resource->was_fetched_via_cache, data_length);
-  }
-
-  // Only report sizes by mime type for ad resources.
-  if (resource->reported_as_ad_resource)
-    RecordResourceMimeHistograms(resource);
-}
-
-void AdsPageLoadMetricsObserver::RecordPageResourceTotalHistograms(
-    ukm::SourceId source_id) {
-  // Only records histograms on pages that have some ad bytes.
-  if (page_ad_resource_bytes_ == 0)
-    return;
-  PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Resources.Bytes.Ads",
-                       page_ad_resource_bytes_);
-  PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Resources.Bytes.TopLevelAds",
-                       page_main_frame_ad_resource_bytes_);
-  size_t unfinished_bytes = 0;
-  for (auto const& kv : page_resources_)
-    unfinished_bytes += kv.second->received_data_length;
-  PAGE_BYTES_HISTOGRAM("PageLoad.Clients.Ads.Resources.Bytes.Unfinished",
-                       unfinished_bytes);
-
-  auto* ukm_recorder = ukm::UkmRecorder::Get();
-  ukm::builders::AdPageLoad builder(source_id);
-  builder.SetTotalBytes(page_bytes_ >> 10)
-      .SetAdBytes(page_ad_resource_bytes_ >> 10)
-      .SetAdJavascriptBytes(page_ad_javascript_bytes_ >> 10)
-      .SetAdVideoBytes(page_ad_video_bytes_ >> 10);
-  base::Time current_time = base::Time::Now();
-  if (!time_commit_.is_null()) {
-    int time_since_commit = (current_time - time_commit_).InMicroseconds();
-    if (time_since_commit > 0) {
-      int ad_kbps_from_commit =
-          (page_ad_resource_bytes_ >> 10) * 1000 * 1000 / time_since_commit;
-      builder.SetAdBytesPerSecond(ad_kbps_from_commit);
-    }
-  }
-  if (!time_interactive_.is_null()) {
-    int time_since_interactive =
-        (current_time - time_interactive_).InMicroseconds();
-    if (time_since_interactive > 0) {
-      int ad_kbps_since_interactive =
-          (page_ad_resource_bytes_since_interactive_ >> 10) * 1000 * 1000 /
-          time_since_interactive;
-      builder.SetAdBytesPerSecondAfterInteractive(ad_kbps_since_interactive);
-    }
-  }
-  builder.Record(ukm_recorder->Get());
-}
-
-void AdsPageLoadMetricsObserver::RecordHistograms(ukm::SourceId source_id) {
-  RecordHistogramsForAdTagging();
-  RecordPageResourceTotalHistograms(source_id);
-  for (auto const& kv : page_resources_)
-    RecordResourceHistograms(kv.second);
-}
-
-void AdsPageLoadMetricsObserver::RecordHistogramsForAdTagging() {
-  if (page_bytes_ == 0)
-    return;
-
-  int non_zero_ad_frames = 0;
-  size_t total_ad_frame_bytes = 0;
-  size_t ad_frame_network_bytes = 0;
-
-  for (const AdFrameData& ad_frame_data : ad_frames_data_storage_) {
-    if (ad_frame_data.frame_bytes == 0)
-      continue;
-    AdFrameVisibility visibility = ad_frame_data.is_display_none
-                                       ? AdFrameVisibility::kDisplayNone
-                                       : AdFrameVisibility::kVisible;
-    ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.Visibility",
-                  UMA_HISTOGRAM_ENUMERATION, visibility);
-
-    // Record pixel metrics only for adframes that are displayed.
-    if (!ad_frame_data.is_display_none) {
-      ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.SqrtNumberOfPixels",
-                    UMA_HISTOGRAM_COUNTS_10000,
-                    std::sqrt(ad_frame_data.frame_size.width() *
-                              ad_frame_data.frame_size.height()));
-      ADS_HISTOGRAM("FrameCounts.AdFrames.PerFrame.SmallestDimension",
-                    UMA_HISTOGRAM_COUNTS_10000,
-                    std::min(ad_frame_data.frame_size.width(),
-                             ad_frame_data.frame_size.height()));
-    }
-
-    non_zero_ad_frames += 1;
-    total_ad_frame_bytes += ad_frame_data.frame_bytes;
-    ad_frame_network_bytes += ad_frame_data.frame_network_bytes;
-
-    ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.Total", PAGE_BYTES_HISTOGRAM,
-                  ad_frame_data.frame_bytes);
-    ADS_HISTOGRAM("Bytes.AdFrames.PerFrame.Network", PAGE_BYTES_HISTOGRAM,
-                  ad_frame_data.frame_network_bytes);
-    ADS_HISTOGRAM(
-        "Bytes.AdFrames.PerFrame.PercentNetwork", UMA_HISTOGRAM_PERCENTAGE,
-        ad_frame_data.frame_network_bytes * 100 / ad_frame_data.frame_bytes);
-    ADS_HISTOGRAM(
-        "SubresourceFilter.FrameCounts.AdFrames.PerFrame.OriginStatus",
-        UMA_HISTOGRAM_ENUMERATION, ad_frame_data.origin_status);
-    ADS_HISTOGRAM(
-        "SubresourceFilter.FrameCounts.AdFrames.PerFrame.UserActivation",
-        UMA_HISTOGRAM_ENUMERATION, ad_frame_data.user_activation_status);
-  }
-
-  // TODO(ericrobinson): Consider renaming this to match
-  //   'FrameCounts.AdFrames.PerFrame.OriginStatus'.
-  ADS_HISTOGRAM("SubresourceFilter.FrameCounts.AnyParentFrame.AdFrames",
-                UMA_HISTOGRAM_COUNTS_1000, non_zero_ad_frames);
-
-  // Don't post UMA for pages that don't have ads.
-  if (non_zero_ad_frames == 0)
-    return;
-
-  ADS_HISTOGRAM("Bytes.NonAdFrames.Aggregate.Total", PAGE_BYTES_HISTOGRAM,
-                page_bytes_ - total_ad_frame_bytes);
-
-  ADS_HISTOGRAM("Bytes.FullPage.Total", PAGE_BYTES_HISTOGRAM, page_bytes_);
-  ADS_HISTOGRAM("Bytes.FullPage.Network", PAGE_BYTES_HISTOGRAM,
-                page_network_bytes_);
-
-  if (page_bytes_) {
-    ADS_HISTOGRAM("Bytes.FullPage.Total.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
-                  total_ad_frame_bytes * 100 / page_bytes_);
-  }
-  if (page_network_bytes_) {
-    ADS_HISTOGRAM("Bytes.FullPage.Network.PercentAds", UMA_HISTOGRAM_PERCENTAGE,
-                  ad_frame_network_bytes * 100 / page_network_bytes_);
-  }
-
-  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Total", PAGE_BYTES_HISTOGRAM,
-                total_ad_frame_bytes);
-  ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.Network", PAGE_BYTES_HISTOGRAM,
-                ad_frame_network_bytes);
-
-  if (total_ad_frame_bytes) {
-    ADS_HISTOGRAM("Bytes.AdFrames.Aggregate.PercentNetwork",
-                  UMA_HISTOGRAM_PERCENTAGE,
-                  ad_frame_network_bytes * 100 / total_ad_frame_bytes);
-  }
-}
-
-void AdsPageLoadMetricsObserver::ProcessOngoingNavigationResource(
-    FrameTreeNodeId frame_tree_node_id) {
-  const auto& frame_id_and_request =
-      ongoing_navigation_resources_.find(frame_tree_node_id);
-  if (frame_id_and_request == ongoing_navigation_resources_.end())
-    return;
-
-  ProcessResourceForFrame(frame_tree_node_id, frame_id_and_request->second);
-  ongoing_navigation_resources_.erase(frame_id_and_request);
-}
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
deleted file mode 100644
index 353196b..0000000
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h
+++ /dev/null
@@ -1,238 +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_PAGE_LOAD_METRICS_OBSERVERS_ADS_PAGE_LOAD_METRICS_OBSERVER_H_
-#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_ADS_PAGE_LOAD_METRICS_OBSERVER_H_
-
-#include <bitset>
-#include <list>
-#include <map>
-#include <memory>
-
-#include "base/macros.h"
-#include "base/scoped_observer.h"
-#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
-#include "chrome/common/page_load_metrics/page_load_metrics.mojom.h"
-#include "components/subresource_filter/content/browser/subresource_filter_observer.h"
-#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
-#include "components/subresource_filter/core/common/load_policy.h"
-#include "net/http/http_response_info.h"
-#include "services/metrics/public/cpp/ukm_source.h"
-
-// This observer labels each sub-frame as an ad or not, and keeps track of
-// relevant per-frame and whole-page byte statistics.
-class AdsPageLoadMetricsObserver
-    : public page_load_metrics::PageLoadMetricsObserver,
-      public subresource_filter::SubresourceFilterObserver {
- public:
-  // The origin of the ad relative to the main frame's origin.
-  // Note: Logged to UMA, keep in sync with CrossOriginAdStatus in enums.xml.
-  //   Add new entries to the end, and do not renumber.
-  enum class AdOriginStatus {
-    kUnknown = 0,
-    kSame = 1,
-    kCross = 2,
-    kMaxValue = kCross,
-  };
-
-  // Whether or not the ad frame has a display: none styling. These values are
-  // persisted to logs. Entries should not be renumbered and numeric values
-  // should never be reused.
-  enum class AdFrameVisibility {
-    kDisplayNone = 0,
-    kVisible = 1,
-    kMaxValue = kVisible,
-  };
-
-  // High level categories of mime types for resources loaded by the page.
-  enum class ResourceMimeType {
-    kJavascript = 0,
-    kVideo = 1,
-    kImage = 2,
-    kCss = 3,
-    kHtml = 4,
-    kOther = 5,
-    kMaxValue = kOther,
-  };
-
-  // These values are persisted to logs. Entries should not be renumbered and
-  // numeric values should never be reused. For any additions, also update the
-  // corresponding PageEndReason enum in enums.xml.
-  enum class UserActivationStatus {
-    kNoActivation = 0,
-    kReceivedActivation = 1,
-    kMaxValue = kReceivedActivation
-  };
-
-  // Returns a new AdsPageLoadMetricObserver. If the feature is disabled it
-  // returns nullptr.
-  static std::unique_ptr<AdsPageLoadMetricsObserver> CreateIfNeeded();
-
-  AdsPageLoadMetricsObserver();
-  ~AdsPageLoadMetricsObserver() override;
-
-  // 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 RecordAdFrameData(FrameTreeNodeId ad_id,
-                         bool is_adframe,
-                         content::RenderFrameHost* ad_host,
-                         bool frame_navigated);
-  void ReadyToCommitNextNavigation(
-      content::NavigationHandle* navigation_handle) override;
-  void OnDidFinishSubFrameNavigation(
-      content::NavigationHandle* navigation_handle) override;
-  void OnDidInternalNavigationAbort(
-      content::NavigationHandle* navigation_handle) override;
-  ObservePolicy FlushMetricsOnAppEnterBackground(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  void OnComplete(const page_load_metrics::mojom::PageLoadTiming& timing,
-                  const page_load_metrics::PageLoadExtraInfo& info) override;
-  void OnResourceDataUseObserved(
-      FrameTreeNodeId frame_tree_node_id,
-      const std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr>&
-          resources) override;
-  void OnPageInteractive(
-      const page_load_metrics::mojom::PageLoadTiming& timing,
-      const page_load_metrics::PageLoadExtraInfo& extra_info) override;
-  void FrameReceivedFirstUserActivation(content::RenderFrameHost* rfh) override;
-  void FrameDisplayStateChanged(content::RenderFrameHost* render_frame_host,
-                                bool is_display_none) override;
-  void FrameSizeChanged(content::RenderFrameHost* render_frame_host,
-                        const gfx::Size& frame_size) override;
-
- private:
-  struct AdFrameData {
-    AdFrameData(FrameTreeNodeId frame_tree_node_id,
-                AdOriginStatus origin_status,
-                bool frame_navigated);
-    // Total bytes used to load resources on the page, including headers.
-    size_t frame_bytes;
-    size_t frame_network_bytes;
-
-    const FrameTreeNodeId frame_tree_node_id;
-    AdOriginStatus origin_status;
-    bool frame_navigated;
-
-    UserActivationStatus user_activation_status;
-    bool is_display_none;
-    gfx::Size frame_size;
-  };
-
-  // subresource_filter::SubresourceFilterObserver:
-  void OnSubframeNavigationEvaluated(
-      content::NavigationHandle* navigation_handle,
-      subresource_filter::LoadPolicy load_policy,
-      bool is_ad_subframe) override;
-  void OnAdSubframeDetected(
-      content::RenderFrameHost* render_frame_host) override;
-  void OnSubresourceFilterGoingAway() override;
-
-  // Determines if the URL of a frame matches the SubresourceFilter block
-  // list. Should only be called once per frame navigation.
-  bool DetectSubresourceFilterAd(FrameTreeNodeId frame_tree_node_id);
-
-  // This should only be called once per frame navigation, as the
-  // SubresourceFilter detector clears its state about detected frames after
-  // each call in order to free up memory.
-  bool DetectAds(content::NavigationHandle* navigation_handle);
-
-  void ProcessResourceForFrame(
-      FrameTreeNodeId frame_tree_node_id,
-      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
-
-  // Get the mime type of a resource. This only returns a subset of mime types,
-  // grouped at a higher level. For example, all video mime types return the
-  // same value.
-  ResourceMimeType GetResourceMimeType(
-      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
-
-  // Update all of the per-resource page counters given a new resource data
-  // update. Updates |page_resources_| to reflect the new state of the resource.
-  // Called once per ResourceDataUpdate.
-  void UpdateResource(
-      FrameTreeNodeId frame_tree_node_id,
-      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
-
-  // Records size of resources by mime type.
-  void RecordResourceMimeHistograms(
-      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
-
-  // Records per-resource histograms.
-  void RecordResourceHistograms(
-      const page_load_metrics::mojom::ResourceDataUpdatePtr& resource);
-  void RecordPageResourceTotalHistograms(ukm::SourceId source_id);
-  void RecordHistograms(ukm::SourceId source_id);
-  void RecordHistogramsForAdTagging();
-
-  // Checks to see if a resource is waiting for a navigation with the given
-  // |frame_tree_node_id| to commit before it can be processed. If so, call
-  // OnResourceDataUpdate for the delayed resource.
-  void ProcessOngoingNavigationResource(FrameTreeNodeId frame_tree_node_id);
-
-  // Stores the size data of each ad frame. Pointed to by ad_frames_ so use a
-  // data structure that won't move the data around.
-  std::list<AdFrameData> ad_frames_data_storage_;
-
-  // Maps a frame (by id) to the AdFrameData responsible for the frame.
-  // Multiple frame ids can point to the same AdFrameData. The responsible
-  // frame is the top-most frame labeled as an ad in the frame's ancestry,
-  // which may be itself. If no responsible frame is found, the data is
-  // nullptr.
-  std::map<FrameTreeNodeId, AdFrameData*> ad_frames_data_;
-
-  // The set of frames that have yet to finish but that the SubresourceFilter
-  // has reported are ads. Once DetectSubresourceFilterAd is called the id is
-  // removed from the set.
-  std::set<FrameTreeNodeId> unfinished_subresource_ad_frames_;
-
-  // When the observer receives report of a document resource loading for a
-  // sub-frame before the sub-frame commit occurs, hold onto the resource
-  // request info (delay it) until the sub-frame commits.
-  std::map<FrameTreeNodeId, page_load_metrics::mojom::ResourceDataUpdatePtr>
-      ongoing_navigation_resources_;
-
-  // Maps a request_id for a blink resource to the metadata for the resource
-  // load. Only contains resources that have not completed loading.
-  std::map<int, page_load_metrics::mojom::ResourceDataUpdatePtr>
-      page_resources_;
-
-  // Tallies for bytes and counts observed in resource data updates for the
-  // entire page.
-  size_t page_ad_javascript_bytes_ = 0u;
-  size_t page_ad_video_bytes_ = 0u;
-  size_t page_ad_resource_bytes_ = 0u;
-  size_t page_main_frame_ad_resource_bytes_ = 0u;
-  uint32_t total_number_page_resources_ = 0;
-
-  // Flag denoting that this observer should no longer monitor changes in
-  // display state for frames. This prevents us from receiving the updates when
-  // the frame elements are being destroyed in the renderer.
-  bool process_display_state_updates_ = true;
-
-  // Time the page was committed.
-  base::Time time_commit_;
-
-  // Time the page was observed to be interactive.
-  base::Time time_interactive_;
-
-  // Total ad bytes loaded by the page since it was observed to be interactive.
-  size_t page_ad_resource_bytes_since_interactive_ = 0u;
-
-  size_t page_bytes_ = 0u;
-  size_t page_network_bytes_ = 0u;
-  bool committed_ = false;
-
-  ScopedObserver<subresource_filter::SubresourceFilterObserverManager,
-                 subresource_filter::SubresourceFilterObserver>
-      subresource_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserver);
-};
-
-#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_ADS_PAGE_LOAD_METRICS_OBSERVER_H_
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
deleted file mode 100644
index a60e0563..0000000
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc
+++ /dev/null
@@ -1,1282 +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 <string>
-
-#include "base/bind.h"
-#include "base/macros.h"
-#include "base/strings/stringprintf.h"
-#include "base/strings/utf_string_conversions.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/test/scoped_feature_list.h"
-#include "chrome/browser/browser_process.h"
-#include "chrome/browser/metrics/subprocess_metrics_provider.h"
-#include "chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
-#include "chrome/browser/ui/browser.h"
-#include "chrome/test/base/in_process_browser_test.h"
-#include "chrome/test/base/ui_test_utils.h"
-#include "components/subresource_filter/content/browser/ruleset_service.h"
-#include "components/subresource_filter/core/browser/subresource_filter_features.h"
-#include "components/subresource_filter/core/common/activation_scope.h"
-#include "components/subresource_filter/core/common/common_features.h"
-#include "components/subresource_filter/core/common/test_ruleset_utils.h"
-#include "components/subresource_filter/core/mojom/subresource_filter.mojom.h"
-#include "components/ukm/test_ukm_recorder.h"
-#include "content/public/test/browser_test.h"
-#include "content/public/test/browser_test_utils.h"
-#include "content/public/test/download_test_observer.h"
-#include "content/public/test/test_navigation_observer.h"
-#include "net/dns/mock_host_resolver.h"
-#include "net/test/embedded_test_server/controllable_http_response.h"
-#include "net/test/embedded_test_server/embedded_test_server.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "services/metrics/public/cpp/ukm_source.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/download/download_stats.h"
-#include "url/gurl.h"
-#include "url/url_constants.h"
-
-namespace {
-
-const char kCrossOriginHistogramId[] =
-    "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AdFrames.PerFrame."
-    "OriginStatus";
-
-const char kAdUserActivationHistogramId[] =
-    "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AdFrames.PerFrame."
-    "UserActivation";
-
-const char kVisibilityHistogramId[] =
-    "PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame."
-    "Visibility";
-
-const char kSqrtNumberOfPixelsHistogramId[] =
-    "PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame."
-    "SqrtNumberOfPixels";
-
-const char kSmallestDimensionHistogramId[] =
-    "PageLoad.Clients.Ads.FrameCounts.AdFrames.PerFrame."
-    "SmallestDimension";
-
-enum class Origin {
-  kNavigation,
-  kAnchorAttribute,
-};
-
-std::ostream& operator<<(std::ostream& os, Origin origin) {
-  switch (origin) {
-    case Origin::kNavigation:
-      return os << "Navigation";
-    case Origin::kAnchorAttribute:
-      return os << "AnchorAttribute";
-  }
-}
-
-enum class SandboxOption {
-  kNoSandbox,
-  kDisallowDownloadsWithoutUserActivation,
-  kAllowDownloadsWithoutUserActivation,
-};
-
-std::ostream& operator<<(std::ostream& os, SandboxOption sandbox_option) {
-  switch (sandbox_option) {
-    case SandboxOption::kNoSandbox:
-      return os << "NoSandbox";
-    case SandboxOption::kDisallowDownloadsWithoutUserActivation:
-      return os << "DisallowDownloadsWithoutUserActivation";
-    case SandboxOption::kAllowDownloadsWithoutUserActivation:
-      return os << "AllowDownloadsWithoutUserActivation";
-  }
-}
-
-// Allow PageLoadMetricsTestWaiter to be initialized for a new web content
-// before the first commit.
-class PopupPageLoadMetricsWaiterInitializer : public TabStripModelObserver {
- public:
-  PopupPageLoadMetricsWaiterInitializer(
-      TabStripModel* tab_strip_model,
-      std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>* waiter)
-      : waiter_(waiter), scoped_observer_(this) {
-    scoped_observer_.Add(tab_strip_model);
-  }
-
-  void OnTabStripModelChanged(
-      TabStripModel* tab_strip_model,
-      const TabStripModelChange& change,
-      const TabStripSelectionChange& selection) override {
-    if (change.type() == TabStripModelChange::kInserted &&
-        selection.active_tab_changed()) {
-      DCHECK(waiter_ && !(*waiter_));
-      *waiter_ = std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
-          tab_strip_model->GetActiveWebContents());
-    }
-  }
-
- private:
-  std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>* waiter_ =
-      nullptr;
-  ScopedObserver<TabStripModel, PopupPageLoadMetricsWaiterInitializer>
-      scoped_observer_;
-
-  DISALLOW_COPY_AND_ASSIGN(PopupPageLoadMetricsWaiterInitializer);
-};
-
-}  // namespace
-
-class AdsPageLoadMetricsObserverBrowserTest
-    : public subresource_filter::SubresourceFilterBrowserTest {
- public:
-  AdsPageLoadMetricsObserverBrowserTest()
-      : subresource_filter::SubresourceFilterBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(subresource_filter::kAdTagging);
-  }
-  ~AdsPageLoadMetricsObserverBrowserTest() override {}
-
-  std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>
-  CreatePageLoadMetricsTestWaiter() {
-    content::WebContents* web_contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    return std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
-        web_contents);
-  }
-
-  void SetUpOnMainThread() override {
-    SubresourceFilterBrowserTest::SetUpOnMainThread();
-    SetRulesetWithRules(
-        {subresource_filter::testing::CreateSuffixRule("ad_iframe_writer.js"),
-         subresource_filter::testing::CreateSuffixRule("ad_script.js")});
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
-  DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserverBrowserTest);
-};
-
-// Test that an embedded ad is same origin.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       OriginStatusMetricEmbedded) {
-  base::HistogramTester histogram_tester;
-  auto waiter = CreatePageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(
-      browser(),
-      embedded_test_server()->GetURL("/ads_observer/srcdoc_embedded_ad.html"));
-  waiter->AddMinimumCompleteResourcesExpectation(4);
-  waiter->Wait();
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      kCrossOriginHistogramId,
-      AdsPageLoadMetricsObserver::AdOriginStatus::kSame, 1);
-}
-
-// Test that an empty embedded ad isn't reported at all.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       OriginStatusMetricEmbeddedEmpty) {
-  base::HistogramTester histogram_tester;
-  ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL(
-                     "/ads_observer/srcdoc_embedded_ad_empty.html"));
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  histogram_tester.ExpectTotalCount(kCrossOriginHistogramId, 0);
-}
-
-// Test that an ad with the same origin as the main page is same origin.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       OriginStatusMetricSame) {
-  base::HistogramTester histogram_tester;
-  auto waiter = CreatePageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(
-      browser(),
-      embedded_test_server()->GetURL("/ads_observer/same_origin_ad.html"));
-  waiter->AddMinimumCompleteResourcesExpectation(4);
-  waiter->Wait();
-
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      kCrossOriginHistogramId,
-      AdsPageLoadMetricsObserver::AdOriginStatus::kSame, 1);
-}
-
-// Test that an ad with a different origin as the main page is cross origin.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       OriginStatusMetricCross) {
-  // Note: Cannot navigate cross-origin without dynamically generating the URL.
-  base::HistogramTester histogram_tester;
-  auto waiter = CreatePageLoadMetricsTestWaiter();
-
-  ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL("/iframe_blank.html"));
-  // Note that the initial iframe is not an ad, so the metric doesn't observe
-  // it initially as same origin.  However, on re-navigating to a cross
-  // origin site that has an ad at its origin, the ad on that page is cross
-  // origin from the original page.
-  NavigateIframeToURL(web_contents(), "test",
-                      embedded_test_server()->GetURL(
-                          "a.com", "/ads_observer/same_origin_ad.html"));
-
-  // Wait until all resource data updates are sent.
-  waiter->AddPageExpectation(
-      page_load_metrics::PageLoadMetricsTestWaiter::TimingField::kLoadEvent);
-  waiter->Wait();
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      kCrossOriginHistogramId,
-      AdsPageLoadMetricsObserver::AdOriginStatus::kCross, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       UserActivationSetOnFrame) {
-  base::HistogramTester histogram_tester;
-  auto waiter = CreatePageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL(
-                     "foo.com", "/ad_tagging/frame_factory.html"));
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  // Create a second frame that will not receive activation.
-  EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
-      web_contents, "createAdFrame('/ad_tagging/frame_factory.html', '');"));
-  EXPECT_TRUE(content::ExecuteScriptWithoutUserGesture(
-      web_contents, "createAdFrame('/ad_tagging/frame_factory.html', '');"));
-
-  // Wait for the frames resources to be loaded as we only log histograms for
-  // frames that have non-zero bytes. Four resources per frame and one favicon.
-  waiter->AddMinimumCompleteResourcesExpectation(13);
-  waiter->Wait();
-
-  // Activate one frame by executing a dummy script.
-  content::RenderFrameHost* ad_frame =
-      ChildFrameAt(web_contents->GetMainFrame(), 0);
-  const std::string no_op_script = "// No-op script";
-  EXPECT_TRUE(ExecuteScript(ad_frame, no_op_script));
-
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  histogram_tester.ExpectBucketCount(
-      kAdUserActivationHistogramId,
-      AdsPageLoadMetricsObserver::UserActivationStatus::kReceivedActivation, 1);
-  histogram_tester.ExpectBucketCount(
-      kAdUserActivationHistogramId,
-      AdsPageLoadMetricsObserver::UserActivationStatus::kNoActivation, 1);
-}
-
-// Test that a subframe that aborts (due to doc.write) doesn't cause a crash
-// if it continues to load resources.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       DocOverwritesNavigation) {
-  content::DOMMessageQueue msg_queue;
-
-  base::HistogramTester histogram_tester;
-
-  ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL(
-                     "/ads_observer/docwrite_provisional_frame.html"));
-  std::string status;
-  EXPECT_TRUE(msg_queue.WaitForMessage(&status));
-  EXPECT_EQ("\"loaded\"", status);
-
-  // Navigate away to force the histogram recording.
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-
-  // TODO(johnidel): Check that the subresources of the new frame are reported
-  // correctly. Resources from a failed provisional load are not reported to
-  // resource data updates, causing this adframe to not be recorded. This is an
-  // uncommon case but should be reported. See crbug.com/914893.
-}
-
-// Test that a blank ad subframe that is docwritten correctly reports metrics.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       DocWriteAboutBlankAdframe) {
-  base::HistogramTester histogram_tester;
-  auto waiter = CreatePageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(browser(),
-                               embedded_test_server()->GetURL(
-                                   "/ads_observer/docwrite_blank_frame.html"));
-  waiter->AddMinimumCompleteResourcesExpectation(5);
-  waiter->Wait();
-  // Navigate away to force the histogram recording.
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AnyParentFrame."
-      "AdFrames",
-      1, 1);
-  histogram_tester.ExpectUniqueSample(
-      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 0 /* < 1 KB */, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       SubresourceFilter) {
-  ResetConfiguration(subresource_filter::Configuration(
-      subresource_filter::mojom::ActivationLevel::kDryRun,
-      subresource_filter::ActivationScope::ALL_SITES));
-  base::HistogramTester histogram_tester;
-
-  // cross_site_iframe_factory loads URLs like:
-  // http://b.com:40919/cross_site_iframe_factory.html?b()
-  SetRulesetToDisallowURLsWithPathSuffix("b()");
-  const GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b,b,c,d)"));
-
-  auto waiter = CreatePageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(browser(), main_url);
-
-  // One favicon resource and 2 resources for each frame.
-  waiter->AddMinimumCompleteResourcesExpectation(11);
-  waiter->Wait();
-
-  // Navigate away to force the histogram recording.
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-
-  histogram_tester.ExpectUniqueSample(
-      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AnyParentFrame."
-      "AdFrames",
-      2, 1);
-}
-
-// Test that a frame without display:none is reported as visible.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       VisibleAdframeRecorded) {
-  base::HistogramTester histogram_tester;
-  auto waiter = CreatePageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(browser(),
-                               embedded_test_server()->GetURL(
-                                   "/ads_observer/display_block_adframe.html"));
-  waiter->AddMinimumCompleteResourcesExpectation(4);
-  waiter->Wait();
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      kVisibilityHistogramId,
-      AdsPageLoadMetricsObserver::AdFrameVisibility::kVisible, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest,
-                       DisplayNoneAdframeRecorded) {
-  base::HistogramTester histogram_tester;
-  auto waiter = CreatePageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(browser(),
-                               embedded_test_server()->GetURL(
-                                   "/ads_observer/display_none_adframe.html"));
-  waiter->AddMinimumCompleteResourcesExpectation(4);
-  waiter->Wait();
-  // Navigate away to force the histogram recording.
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      kVisibilityHistogramId,
-      AdsPageLoadMetricsObserver::AdFrameVisibility::kDisplayNone, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverBrowserTest, FramePixelSize) {
-  base::HistogramTester histogram_tester;
-  auto waiter = CreatePageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL(
-                     "/ads_observer/blank_with_adiframe_writer.html"));
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  // Create a 100x100 iframe.
-  ASSERT_TRUE(ExecJs(
-      web_contents,
-      "let frame = createAdIframe(); frame.width=100; frame.height = 100; "
-      "frame.src = '/ads_observer/pixel.png';"));
-
-  // Create a 300x300 iframe with display none that should not be recorded.
-  ASSERT_TRUE(ExecJs(web_contents,
-                     "frame = createAdIframe(); frame.width=300; frame.src "
-                     "= '/ads_observer/pixel.png';"
-                     "frame.height = 300; frame.style.display= 'none';"));
-
-  // Create a 0x0 iframe.
-  ASSERT_TRUE(ExecJs(
-      web_contents,
-      "frame = createAdIframe(); frame.width=0; frame.height = 0; frame.src = "
-      "'/ads_observer/pixel.png';"));
-
-  // Create a 10 x 1000 iframe.
-  ASSERT_TRUE(
-      ExecJs(web_contents,
-             "frame = createAdIframe(); frame.width=10; frame.height = 1000; "
-             "frame.src = '/ads_observer/pixel.png';"));
-
-  // Wait for each frames resource to load so that they will have non-zero
-  // bytes.
-  waiter->AddMinimumCompleteResourcesExpectation(7);
-  waiter->Wait();
-
-  // Navigate away to force the histogram recording.
-  ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
-  histogram_tester.ExpectBucketCount(kSqrtNumberOfPixelsHistogramId, 100, 2);
-  histogram_tester.ExpectBucketCount(kSqrtNumberOfPixelsHistogramId, 0, 1);
-  histogram_tester.ExpectBucketCount(kSmallestDimensionHistogramId, 0, 1);
-  histogram_tester.ExpectBucketCount(kSmallestDimensionHistogramId, 10, 1);
-  histogram_tester.ExpectBucketCount(kSmallestDimensionHistogramId, 100, 1);
-
-  // Verify the display: none frame is not recorded.
-  histogram_tester.ExpectBucketCount(kSqrtNumberOfPixelsHistogramId, 300, 0);
-  histogram_tester.ExpectBucketCount(kSmallestDimensionHistogramId, 300, 0);
-}
-
-class AdsPageLoadMetricsTestWaiter
-    : public page_load_metrics::PageLoadMetricsTestWaiter {
- public:
-  explicit AdsPageLoadMetricsTestWaiter(content::WebContents* web_contents)
-      : page_load_metrics::PageLoadMetricsTestWaiter(web_contents) {}
-  void AddMinimumAdResourceExpectation(int num_ad_resources) {
-    expected_minimum_num_ad_resources_ = num_ad_resources;
-  }
-
- protected:
-  bool ExpectationsSatisfied() const override {
-    int num_ad_resources = 0;
-    for (auto& kv : page_resources_) {
-      if (kv.second->reported_as_ad_resource)
-        num_ad_resources++;
-    }
-    return num_ad_resources >= expected_minimum_num_ad_resources_ &&
-           PageLoadMetricsTestWaiter::ExpectationsSatisfied();
-  };
-
- private:
-  int expected_minimum_num_ad_resources_ = 0;
-};
-
-class AdsPageLoadMetricsObserverResourceBrowserTest
-    : public subresource_filter::SubresourceFilterBrowserTest {
- public:
-  AdsPageLoadMetricsObserverResourceBrowserTest() {
-    scoped_feature_list_.InitAndEnableFeature(subresource_filter::kAdTagging);
-  }
-
-  ~AdsPageLoadMetricsObserverResourceBrowserTest() override {}
-  void SetUpOnMainThread() override {
-    host_resolver()->AddRule("*", "127.0.0.1");
-    SetRulesetWithRules(
-        {subresource_filter::testing::CreateSuffixRule("ad_script.js"),
-         subresource_filter::testing::CreateSuffixRule("ad_script_2.js"),
-         subresource_filter::testing::CreateSuffixRule("disallow.zip")});
-  }
-
-  void OpenLinkInFrame(const content::ToRenderFrameHost& adapter,
-                       const std::string& link_id,
-                       bool has_gesture) {
-    std::string open_link_script = base::StringPrintf(
-        R"(
-            var evt = document.createEvent("MouseEvent");
-            evt.initMouseEvent('click', true, true);
-            document.getElementById('%s').dispatchEvent(evt);
-        )",
-        link_id.c_str());
-    if (has_gesture) {
-      EXPECT_TRUE(ExecuteScript(adapter, open_link_script));
-    } else {
-      EXPECT_TRUE(ExecuteScriptWithoutUserGesture(adapter, open_link_script));
-    }
-  }
-
- protected:
-  std::unique_ptr<AdsPageLoadMetricsTestWaiter>
-  CreateAdsPageLoadMetricsTestWaiter() {
-    content::WebContents* web_contents =
-        browser()->tab_strip_model()->GetActiveWebContents();
-    return std::make_unique<AdsPageLoadMetricsTestWaiter>(web_contents);
-  }
-
- private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-};
-
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
-                       ReceivedAdResources) {
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ad_tagging");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(
-      browser(),
-      embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
-  // Two subresources should have been reported as ads.
-  waiter->AddMinimumAdResourceExpectation(2);
-  waiter->Wait();
-}
-
-// Main resources for adframes are counted as ad resources.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
-                       ReceivedMainResourceAds) {
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ad_tagging");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
-
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  ui_test_utils::NavigateToURL(
-      browser(),
-      embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
-  contents->GetMainFrame()->ExecuteJavaScriptForTests(
-      base::ASCIIToUTF16("createAdFrame('frame_factory.html', '');"));
-  // Two pages subresources should have been reported as ad. The iframe resource
-  // and its three subresources should also be reported as ads.
-  waiter->AddMinimumAdResourceExpectation(6);
-  waiter->Wait();
-}
-
-// Subframe navigations report ad resources correctly.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
-                       ReceivedSubframeNavigationAds) {
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ad_tagging");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
-
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  ui_test_utils::NavigateToURL(
-      browser(),
-      embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
-  contents->GetMainFrame()->ExecuteJavaScriptForTests(
-      base::ASCIIToUTF16("createAdFrame('frame_factory.html', 'test');"));
-  waiter->AddMinimumAdResourceExpectation(6);
-  waiter->Wait();
-  NavigateIframeToURL(
-      web_contents(), "test",
-      embedded_test_server()->GetURL("foo.com", "/frame_factory.html"));
-  // The new subframe and its three subresources should be reported
-  // as ads.
-  waiter->AddMinimumAdResourceExpectation(10);
-  waiter->Wait();
-}
-
-// Verify that per-resource metrics are recorded correctly.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
-                       ReceivedAdResourceMetrics) {
-  base::HistogramTester histogram_tester;
-
-  const char kHttpResponseHeader[] =
-      "HTTP/1.1 200 OK\r\n"
-      "Content-Type: text/html; charset=utf-8\r\n"
-      "\r\n";
-  auto main_html_response =
-      std::make_unique<net::test_server::ControllableHttpResponse>(
-          embedded_test_server(), "/mock_page.html",
-          true /*relative_url_is_prefix*/);
-  auto ad_script_response =
-      std::make_unique<net::test_server::ControllableHttpResponse>(
-          embedded_test_server(), "/ad_script.js",
-          true /*relative_url_is_prefix*/);
-  auto iframe_response =
-      std::make_unique<net::test_server::ControllableHttpResponse>(
-          embedded_test_server(), "/iframe.html",
-          true /*relative_url_is_prefix*/);
-  auto vanilla_script_response =
-      std::make_unique<net::test_server::ControllableHttpResponse>(
-          embedded_test_server(), "/vanilla_script.js",
-          true /*relative_url_is_prefix*/);
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
-
-  browser()->OpenURL(content::OpenURLParams(
-      embedded_test_server()->GetURL("/mock_page.html"), content::Referrer(),
-      WindowOpenDisposition::CURRENT_TAB, ui::PAGE_TRANSITION_TYPED, false));
-
-  main_html_response->WaitForRequest();
-  main_html_response->Send(kHttpResponseHeader);
-  main_html_response->Send(
-      "<html><body></body><script src=\"ad_script.js\"></script></html>");
-  main_html_response->Done();
-
-  ad_script_response->WaitForRequest();
-  ad_script_response->Send(kHttpResponseHeader);
-  ad_script_response->Send(
-      "var iframe = document.createElement(\"iframe\");"
-      "iframe.src =\"iframe.html\";"
-      "document.body.appendChild(iframe);");
-  ad_script_response->Send(std::string(1000, ' '));
-  ad_script_response->Done();
-
-  iframe_response->WaitForRequest();
-  iframe_response->Send(kHttpResponseHeader);
-  iframe_response->Send("<html><script src=\"vanilla_script.js\"></script>");
-  iframe_response->Send(std::string(2000, ' '));
-  iframe_response->Send("</html>");
-  iframe_response->Done();
-
-  vanilla_script_response->WaitForRequest();
-  vanilla_script_response->Send(kHttpResponseHeader);
-  vanilla_script_response->Send(std::string(1024, ' '));
-  waiter->AddMinimumNetworkBytesExpectation(4000);
-  waiter->Wait();
-
-  // Verify correct numbers of resources are recorded.
-  histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Network.Mainframe.VanillaResource", 1);
-  histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Network.Mainframe.AdResource", 1);
-  histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Network.Subframe.AdResource", 1);
-  // Verify unfinished resource not yet recorded.
-  histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Network.Subframe.VanillaResource", 0);
-
-  // Close all tabs instead of navigating as the embedded_test_server will
-  // hang waiting for loads to finish when we have an unfinished
-  // ControlledHttpReseonse.
-  browser()->tab_strip_model()->CloseAllTabs();
-
-  // Verify unfinished resource recorded when page is destroyed.
-  histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Network.Subframe.AdResource", 2);
-
-  histogram_tester.ExpectBucketCount(
-      "PageLoad.Clients.Ads.Bytes.FullPage.Network", 4, 1);
-  // We have received 4 KB of ads and 1 KB of toplevel ads.
-  histogram_tester.ExpectBucketCount("PageLoad.Clients.Ads.Resources.Bytes.Ads",
-                                     4, 1);
-  histogram_tester.ExpectBucketCount(
-      "PageLoad.Clients.Ads.Resources.Bytes.TopLevelAds", 1, 1);
-
-  // 4 resources loaded, one unfinished.
-  histogram_tester.ExpectBucketCount(
-      "PageLoad.Clients.Ads.Resources.Bytes.Unfinished", 1, 1);
-}
-
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
-                       IncompleteResourcesRecordedToFrameMetrics) {
-  base::HistogramTester histogram_tester;
-  SetRulesetWithRules(
-      {subresource_filter::testing::CreateSuffixRule("ad_iframe_writer.js")});
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ads_observer");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-
-  const char kHttpResponseHeader[] =
-      "HTTP/1.1 200 OK\r\n"
-      "Content-Type: text/html; charset=utf-8\r\n"
-      "\r\n";
-  auto incomplete_resource_response =
-      std::make_unique<net::test_server::ControllableHttpResponse>(
-          embedded_test_server(), "/incomplete_resource.js",
-          true /*relative_url_is_prefix*/);
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
-
-  browser()->OpenURL(content::OpenURLParams(
-      embedded_test_server()->GetURL("/ad_with_incomplete_resource.html"),
-      content::Referrer(), WindowOpenDisposition::CURRENT_TAB,
-      ui::PAGE_TRANSITION_TYPED, false));
-
-  waiter->AddMinimumCompleteResourcesExpectation(3);
-  waiter->Wait();
-  int64_t initial_page_bytes = waiter->current_network_bytes();
-
-  // Ad resource will not finish loading but should be reported to metrics.
-  incomplete_resource_response->WaitForRequest();
-  incomplete_resource_response->Send(kHttpResponseHeader);
-  incomplete_resource_response->Send(std::string(2048, ' '));
-
-  // Wait for the resource update to be received for the incomplete response.
-  waiter->AddMinimumNetworkBytesExpectation(2048);
-  waiter->Wait();
-
-  // Close all tabs instead of navigating as the embedded_test_server will
-  // hang waiting for loads to finish when we have an unfinished
-  // ControlledHttpResponse.
-  browser()->tab_strip_model()->CloseAllTabs();
-
-  int expected_page_kilobytes = (initial_page_bytes + 2048) / 1024;
-
-  histogram_tester.ExpectBucketCount(
-      "PageLoad.Clients.Ads.Bytes.FullPage.Network", expected_page_kilobytes,
-      1);
-  histogram_tester.ExpectBucketCount(
-      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Network", 2, 1);
-  histogram_tester.ExpectBucketCount(
-      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 2, 1);
-  histogram_tester.ExpectBucketCount(
-      "PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Network", 2, 1);
-  histogram_tester.ExpectBucketCount(
-      "PageLoad.Clients.Ads.Bytes.AdFrames.PerFrame.Total", 2, 1);
-}
-
-// Verify that per-resource metrics are reported for cached resources and
-// resources loaded by the network.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
-                       RecordedCacheResourceMetrics) {
-  base::HistogramTester histogram_tester;
-  SetRulesetWithRules(
-      {subresource_filter::testing::CreateSuffixRule("create_frame.js")});
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ad_tagging");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL("foo.com", "/cachetime"));
-
-  // Wait for the favicon to be fetched.
-  waiter->AddMinimumCompleteResourcesExpectation(2);
-  waiter->Wait();
-
-  // All resources should have been loaded by network.
-  histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Network.Mainframe.VanillaResource", 2);
-
-  // Open a new tab and navigate so that resources are fetched via the disk
-  // cache. Navigating to the same URL in the same tab triggers a refresh which
-  // will not check the disk cache.
-  ui_test_utils::NavigateToURLWithDisposition(
-      browser(), GURL("about:blank"), WindowOpenDisposition::NEW_FOREGROUND_TAB,
-      ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB |
-          ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
-  waiter = CreateAdsPageLoadMetricsTestWaiter();
-  ui_test_utils::NavigateToURL(
-      browser(), embedded_test_server()->GetURL("foo.com", "/cachetime"));
-
-  // Wait for the resource to be fetched.
-  waiter->AddMinimumCompleteResourcesExpectation(1);
-  waiter->Wait();
-
-  // Resource should be recorded as loaded from the cache. Favicon not
-  // fetched this time.
-  histogram_tester.ExpectTotalCount(
-      "Ads.ResourceUsage.Size.Cache.Mainframe.VanillaResource", 1);
-}
-
-// Verify that Mime type metrics are recorded correctly.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
-                       RecordedMimeMetrics) {
-  base::HistogramTester histogram_tester;
-  ukm::TestAutoSetUkmRecorder ukm_recorder;
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ad_tagging");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  auto waiter = CreateAdsPageLoadMetricsTestWaiter();
-
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  GURL url = embedded_test_server()->GetURL("foo.com", "/frame_factory.html");
-  ui_test_utils::NavigateToURL(browser(), url);
-  contents->GetMainFrame()->ExecuteJavaScriptForTests(
-      base::ASCIIToUTF16("createAdFrame('multiple_mimes.html', 'test');"));
-  waiter->AddMinimumAdResourceExpectation(8);
-  waiter->Wait();
-
-  // Close all tabs to log metrics, as the video resource request is incomplete.
-  browser()->tab_strip_model()->CloseAllTabs();
-
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.HTML",
-                                    1);
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.CSS",
-                                    1);
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.JS",
-                                    3);
-
-  // Note: png and video/webm mime types are not set explicitly by the
-  // embedded_test_server.
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Image",
-                                    1);
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Video",
-                                    1);
-  histogram_tester.ExpectTotalCount("Ads.ResourceUsage.Size.Network.Mime.Other",
-                                    1);
-
-  // Verify UKM Metrics recorded.
-  auto entries =
-      ukm_recorder.GetEntriesByName(ukm::builders::AdPageLoad::kEntryName);
-  EXPECT_EQ(1u, entries.size());
-  ukm_recorder.ExpectEntrySourceHasUrl(entries.front(), url);
-  EXPECT_GT(*ukm_recorder.GetEntryMetric(
-                entries.front(), ukm::builders::AdPageLoad::kAdBytesName),
-            0);
-  EXPECT_GT(
-      *ukm_recorder.GetEntryMetric(
-          entries.front(), ukm::builders::AdPageLoad::kAdBytesPerSecondName),
-      0);
-
-  // TTI is not reached by this page and thus should not have this recorded.
-  EXPECT_FALSE(ukm_recorder.EntryHasMetric(
-      entries.front(),
-      ukm::builders::AdPageLoad::kAdBytesPerSecondAfterInteractiveName));
-  EXPECT_GT(
-      *ukm_recorder.GetEntryMetric(
-          entries.front(), ukm::builders::AdPageLoad::kAdJavascriptBytesName),
-      0);
-  EXPECT_GT(*ukm_recorder.GetEntryMetric(
-                entries.front(), ukm::builders::AdPageLoad::kAdVideoBytesName),
-            0);
-}
-
-// Download gets blocked when LoadPolicy is DISALLOW for the navigation
-// to download.
-IN_PROC_BROWSER_TEST_F(AdsPageLoadMetricsObserverResourceBrowserTest,
-                       SubframeNavigationDownloadBlockedByLoadPolicy) {
-  ResetConfiguration(subresource_filter::Configuration(
-      subresource_filter::mojom::ActivationLevel::kEnabled,
-      subresource_filter::ActivationScope::ALL_SITES));
-
-  base::HistogramTester histogram_tester;
-
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ad_tagging");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-  ASSERT_TRUE(embedded_test_server()->Start());
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  std::string host_name = "foo.com";
-  ui_test_utils::NavigateToURL(
-      browser(),
-      embedded_test_server()->GetURL(host_name, "/frame_factory.html"));
-  content::TestNavigationObserver navigation_observer(web_contents());
-  contents->GetMainFrame()->ExecuteJavaScriptForTests(
-      base::ASCIIToUTF16("createFrame('download.html', 'test');"));
-  navigation_observer.Wait();
-
-  content::RenderFrameHost* rfh = content::FrameMatchingPredicate(
-      web_contents(), base::BindRepeating(&content::FrameMatchesName, "test"));
-  OpenLinkInFrame(rfh, "blocked_nav_download_id", false /* gesture*/);
-
-  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-  histogram_tester.ExpectTotalCount("Download.Subframe.SandboxOriginAdGesture",
-                                    0);
-}
-
-class RemoteFrameNavigationBrowserTest
-    : public AdsPageLoadMetricsObserverResourceBrowserTest {
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitchASCII(
-        "enable-blink-features",
-        "BlockingDownloadsInSandboxWithoutUserActivation");
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(RemoteFrameNavigationBrowserTest,
-                       DownloadsBlockedInSandbox) {
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ad_tagging");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  base::HistogramTester histogram_tester;
-  std::string origin1 = "foo.com";
-  std::string origin2 = "bar.com";
-  GURL tab1_url =
-      embedded_test_server()->GetURL(origin1, "/frame_factory.html");
-
-  auto subframe_navigation_waiter =
-      std::make_unique<page_load_metrics::PageLoadMetricsTestWaiter>(
-          web_contents());
-  subframe_navigation_waiter->AddSubframeNavigationExpectation(2);
-
-  ui_test_utils::NavigateToURL(browser(), tab1_url);
-
-  std::string subframe_url =
-      embedded_test_server()->GetURL(origin2, "/frame_factory.html").spec();
-  content::TestNavigationObserver new_subframe_waiter(web_contents());
-  std::string script =
-      base::StringPrintf("createFrame('%s','test','');", subframe_url.c_str());
-  web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
-      base::ASCIIToUTF16(script));
-  new_subframe_waiter.Wait();
-
-  GURL dld_url = embedded_test_server()->GetURL(origin1, "/allow.zip");
-  EXPECT_TRUE(ExecuteScriptWithoutUserGesture(
-      web_contents(),
-      "document.getElementById('test').src = \"" + dld_url.spec() + "\";"));
-
-  subframe_navigation_waiter->Wait();
-  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-  histogram_tester.ExpectTotalCount("Download.Subframe.SandboxOriginAdGesture",
-                                    0 /* expected_count */);
-}
-
-class MainFrameDownloadFlagsBrowserTest
-    : public AdsPageLoadMetricsObserverResourceBrowserTest,
-      public ::testing::WithParamInterface<std::tuple<
-          Origin,
-          bool /* enable_blocking_downloads_in_sandbox_without_user_activation
-                */
-          ,
-          SandboxOption,
-          bool /* has_gesture */>> {
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    bool enable_blocking_downloads_in_sandbox_without_user_activation;
-    std::tie(std::ignore,
-             enable_blocking_downloads_in_sandbox_without_user_activation,
-             std::ignore, std::ignore) = GetParam();
-    std::string cmd =
-        enable_blocking_downloads_in_sandbox_without_user_activation
-            ? "enable-blink-features"
-            : "disable-blink-features";
-    command_line->AppendSwitchASCII(
-        cmd, "BlockingDownloadsInSandboxWithoutUserActivation");
-  }
-};
-
-// Main frame download events are reported correctly.
-IN_PROC_BROWSER_TEST_P(MainFrameDownloadFlagsBrowserTest, Download) {
-  Origin origin;
-  bool enable_blocking_downloads_in_sandbox_without_user_activation;
-  SandboxOption sandbox_option;
-  bool has_gesture;
-  std::tie(origin, enable_blocking_downloads_in_sandbox_without_user_activation,
-           sandbox_option, has_gesture) = GetParam();
-  SCOPED_TRACE(
-      ::testing::Message()
-      << "origin = " << origin << ", "
-      << "enable_blocking_downloads_in_sandbox_without_user_activation = "
-      << enable_blocking_downloads_in_sandbox_without_user_activation << ", "
-      << "sandbox_option = " << sandbox_option << ", "
-      << "has_gesture = " << has_gesture);
-
-  bool expected_download =
-      !enable_blocking_downloads_in_sandbox_without_user_activation ||
-      has_gesture ||
-      sandbox_option != SandboxOption::kDisallowDownloadsWithoutUserActivation;
-  bool expected_sandbox_bit =
-      enable_blocking_downloads_in_sandbox_without_user_activation
-          ? has_gesture &&
-                sandbox_option ==
-                    SandboxOption::kDisallowDownloadsWithoutUserActivation
-          : sandbox_option != SandboxOption::kNoSandbox;
-
-  base::HistogramTester histogram_tester;
-  ukm::TestAutoSetUkmRecorder ukm_recorder;
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ad_tagging");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  std::string host_name = "foo.com";
-  GURL main_url = embedded_test_server()->GetURL(host_name, "/download.html");
-
-  std::unique_ptr<page_load_metrics::PageLoadMetricsTestWaiter>
-      web_feature_waiter;
-
-  if (sandbox_option == SandboxOption::kNoSandbox) {
-    ui_test_utils::NavigateToURL(browser(), main_url);
-  } else {
-    GURL first_tab_url =
-        embedded_test_server()->GetURL(host_name, "/frame_factory.html");
-    ui_test_utils::NavigateToURL(browser(), first_tab_url);
-    const char* method = "createFrame";
-    std::string subframe_url =
-        embedded_test_server()->GetURL(host_name, "/frame_factory.html").spec();
-    const char* id = "test";
-    const char* sandbox_param =
-        sandbox_option == SandboxOption::kDisallowDownloadsWithoutUserActivation
-            ? "'allow-scripts allow-same-origin allow-popups'"
-            : "'allow-scripts allow-same-origin allow-popups "
-              "allow-downloads-without-user-activation'";
-    content::TestNavigationObserver navigation_observer(web_contents());
-    std::string script = base::StringPrintf(
-        "%s('%s','%s',%s);", method, subframe_url.c_str(), id, sandbox_param);
-    web_contents()->GetMainFrame()->ExecuteJavaScriptForTests(
-        base::ASCIIToUTF16(script));
-    navigation_observer.Wait();
-
-    content::RenderFrameHost* child = content::FrameMatchingPredicate(
-        web_contents(), base::BindRepeating(&content::FrameMatchesName, id));
-
-    std::unique_ptr<PopupPageLoadMetricsWaiterInitializer> waiter_initializer;
-    if (expected_sandbox_bit) {
-      waiter_initializer =
-          std::make_unique<PopupPageLoadMetricsWaiterInitializer>(
-              browser()->tab_strip_model(), &web_feature_waiter);
-    }
-    content::TestNavigationObserver popup_observer(main_url);
-    popup_observer.StartWatchingNewWebContents();
-    EXPECT_TRUE(
-        ExecuteScript(child, "window.open(\"" + main_url.spec() + "\");"));
-    popup_observer.Wait();
-    ASSERT_EQ(2, browser()->tab_strip_model()->count());
-  }
-
-  DCHECK(!expected_sandbox_bit || web_feature_waiter);
-  if (expected_sandbox_bit) {
-    blink::mojom::WebFeature feature =
-        origin == Origin::kNavigation
-            ? has_gesture ? blink::mojom::WebFeature::
-                                kNavigationDownloadInSandboxWithUserGesture
-                          : blink::mojom::WebFeature::
-                                kNavigationDownloadInSandboxWithoutUserGesture
-            : has_gesture
-                  ? blink::mojom::WebFeature::
-                        kHTMLAnchorElementDownloadInSandboxWithUserGesture
-                  : blink::mojom::WebFeature::
-                        kHTMLAnchorElementDownloadInSandboxWithoutUserGesture;
-    web_feature_waiter->AddWebFeatureExpectation(feature);
-  }
-
-  std::string link_id =
-      origin == Origin::kNavigation ? "nav_download_id" : "anchor_download_id";
-
-  std::unique_ptr<content::DownloadTestObserver> download_observer(
-      new content::DownloadTestObserverTerminal(
-          content::BrowserContext::GetDownloadManager(browser()->profile()),
-          expected_download /* wait_count */,
-          content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL));
-  OpenLinkInFrame(web_contents(), link_id, has_gesture);
-  download_observer->WaitForFinished();
-  if (web_feature_waiter)
-    web_feature_waiter->Wait();
-  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  if (!expected_download) {
-    histogram_tester.ExpectTotalCount("Download.MainFrame.SandboxGesture",
-                                      0 /* expected_count */);
-    return;
-  }
-
-  blink::DownloadStats::MainFrameDownloadFlags expected_flags;
-  expected_flags.has_sandbox = expected_sandbox_bit;
-  expected_flags.has_gesture = has_gesture;
-  histogram_tester.ExpectUniqueSample("Download.MainFrame.SandboxGesture",
-                                      expected_flags.ToUmaValue(),
-                                      1 /* expected_count */);
-
-  auto entries = ukm_recorder.GetEntriesByName(
-      ukm::builders::MainFrameDownload::kEntryName);
-  EXPECT_EQ(1u, entries.size());
-  ukm_recorder.ExpectEntrySourceHasUrl(entries.back(), main_url);
-  ukm_recorder.ExpectEntryMetric(
-      entries.back(), ukm::builders::MainFrameDownload::kHasSandboxName,
-      expected_sandbox_bit);
-  ukm_recorder.ExpectEntryMetric(
-      entries.back(), ukm::builders::MainFrameDownload::kHasGestureName,
-      has_gesture);
-}
-
-INSTANTIATE_TEST_CASE_P(
-    /* no prefix */,
-    MainFrameDownloadFlagsBrowserTest,
-    ::testing::Combine(
-        ::testing::Values(Origin::kNavigation, Origin::kAnchorAttribute),
-        ::testing::Bool(),
-        ::testing::Values(
-            SandboxOption::kNoSandbox,
-            SandboxOption::kDisallowDownloadsWithoutUserActivation,
-            SandboxOption::kAllowDownloadsWithoutUserActivation),
-        ::testing::Bool()));
-
-class SubframeDownloadFlagsBrowserTest
-    : public AdsPageLoadMetricsObserverResourceBrowserTest,
-      public ::testing::WithParamInterface<std::tuple<
-          Origin,
-          bool /* enable_blocking_downloads_in_sandbox_without_user_activation
-                */
-          ,
-          SandboxOption,
-          bool /* is_cross_origin */,
-          bool /* is_ad_frame */,
-          bool /* has_gesture */>> {
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    bool enable_blocking_downloads_in_sandbox_without_user_activation;
-    std::tie(std::ignore,
-             enable_blocking_downloads_in_sandbox_without_user_activation,
-             std::ignore, std::ignore, std::ignore, std::ignore) = GetParam();
-    std::string cmd =
-        enable_blocking_downloads_in_sandbox_without_user_activation
-            ? "enable-blink-features"
-            : "disable-blink-features";
-    command_line->AppendSwitchASCII(
-        cmd, "BlockingDownloadsInSandboxWithoutUserActivation");
-  }
-};
-
-// Subframe download events are reported correctly.
-IN_PROC_BROWSER_TEST_P(SubframeDownloadFlagsBrowserTest, Download) {
-  Origin origin;
-  bool enable_blocking_downloads_in_sandbox_without_user_activation;
-  SandboxOption sandbox_option;
-  bool is_cross_origin;
-  bool is_ad_frame;
-  bool has_gesture;
-  std::tie(origin, enable_blocking_downloads_in_sandbox_without_user_activation,
-           sandbox_option, is_cross_origin, is_ad_frame, has_gesture) =
-      GetParam();
-  SCOPED_TRACE(
-      ::testing::Message()
-      << "origin = " << origin << ", "
-      << "enable_blocking_downloads_in_sandbox_without_user_activation = "
-      << enable_blocking_downloads_in_sandbox_without_user_activation << ", "
-      << "sandbox_option = " << sandbox_option << ", "
-      << "is_cross_origin = " << is_cross_origin << ", "
-      << "is_ad_frame = " << is_ad_frame << ", "
-      << "has_gesture = " << has_gesture);
-
-  base::HistogramTester histogram_tester;
-  ukm::TestAutoSetUkmRecorder ukm_recorder;
-  bool expected_download =
-      !enable_blocking_downloads_in_sandbox_without_user_activation ||
-      has_gesture ||
-      sandbox_option != SandboxOption::kDisallowDownloadsWithoutUserActivation;
-  bool expected_sandbox_bit =
-      enable_blocking_downloads_in_sandbox_without_user_activation
-          ? has_gesture &&
-                sandbox_option ==
-                    SandboxOption::kDisallowDownloadsWithoutUserActivation
-          : sandbox_option != SandboxOption::kNoSandbox;
-
-  std::unique_ptr<content::DownloadTestObserver> download_observer(
-      new content::DownloadTestObserverTerminal(
-          content::BrowserContext::GetDownloadManager(browser()->profile()),
-          expected_download /* wait_count */,
-          content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL));
-
-  embedded_test_server()->ServeFilesFromSourceDirectory(
-      "chrome/test/data/ad_tagging");
-  content::SetupCrossSiteRedirector(embedded_test_server());
-  ASSERT_TRUE(embedded_test_server()->Start());
-
-  content::WebContents* contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  std::unique_ptr<AdsPageLoadMetricsTestWaiter> waiter;
-  if (origin == Origin::kNavigation) {
-    waiter = std::make_unique<AdsPageLoadMetricsTestWaiter>(contents);
-    waiter->AddSubframeNavigationExpectation(2);
-  }
-  if (expected_download && is_ad_frame) {
-    if (!waiter)
-      waiter = std::make_unique<AdsPageLoadMetricsTestWaiter>(contents);
-    blink::mojom::WebFeature feature =
-        has_gesture
-            ? blink::mojom::WebFeature::kDownloadInAdFrameWithUserGesture
-            : blink::mojom::WebFeature::kDownloadInAdFrameWithoutUserGesture;
-    waiter->AddWebFeatureExpectation(feature);
-  }
-  if (expected_sandbox_bit) {
-    if (!waiter)
-      waiter = std::make_unique<AdsPageLoadMetricsTestWaiter>(contents);
-    blink::mojom::WebFeature feature =
-        origin == Origin::kNavigation
-            ? has_gesture ? blink::mojom::WebFeature::
-                                kNavigationDownloadInSandboxWithUserGesture
-                          : blink::mojom::WebFeature::
-                                kNavigationDownloadInSandboxWithoutUserGesture
-            : has_gesture
-                  ? blink::mojom::WebFeature::
-                        kHTMLAnchorElementDownloadInSandboxWithUserGesture
-                  : blink::mojom::WebFeature::
-                        kHTMLAnchorElementDownloadInSandboxWithoutUserGesture;
-    waiter->AddWebFeatureExpectation(feature);
-  }
-
-  std::string host_name = "foo.com";
-  GURL main_url =
-      embedded_test_server()->GetURL(host_name, "/frame_factory.html");
-  ui_test_utils::NavigateToURL(browser(), main_url);
-
-  std::string link_id =
-      origin == Origin::kNavigation ? "nav_download_id" : "anchor_download_id";
-
-  const char* method = is_ad_frame ? "createAdFrame" : "createFrame";
-  std::string url =
-      embedded_test_server()
-          ->GetURL(is_cross_origin ? "bar.com" : host_name, "/download.html")
-          .spec();
-  const char* id = "test";
-  const char* sandbox_param =
-      sandbox_option == SandboxOption::kNoSandbox
-          ? "undefined"
-          : sandbox_option ==
-                    SandboxOption::kDisallowDownloadsWithoutUserActivation
-                ? "'allow-scripts allow-same-origin'"
-                : "'allow-scripts allow-same-origin "
-                  "allow-downloads-without-user-activation'";
-
-  content::TestNavigationObserver navigation_observer(web_contents());
-  std::string script = base::StringPrintf("%s('%s','%s',%s);", method,
-                                          url.c_str(), id, sandbox_param);
-
-  contents->GetMainFrame()->ExecuteJavaScriptForTests(
-      base::ASCIIToUTF16(script));
-  navigation_observer.Wait();
-
-  content::RenderFrameHost* rfh = content::FrameMatchingPredicate(
-      web_contents(), base::BindRepeating(&content::FrameMatchesName, id));
-  OpenLinkInFrame(rfh, link_id, has_gesture);
-
-  download_observer->WaitForFinished();
-  if (waiter)
-    waiter->Wait();
-  SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
-
-  if (!expected_download) {
-    histogram_tester.ExpectTotalCount(
-        "Download.Subframe.SandboxOriginAdGesture", 0 /* expected_count */);
-    return;
-  }
-
-  blink::DownloadStats::SubframeDownloadFlags expected_flags;
-  expected_flags.has_sandbox = expected_sandbox_bit;
-  expected_flags.is_cross_origin = is_cross_origin;
-  expected_flags.is_ad_frame = is_ad_frame;
-  expected_flags.has_gesture = has_gesture;
-  histogram_tester.ExpectUniqueSample(
-      "Download.Subframe.SandboxOriginAdGesture", expected_flags.ToUmaValue(),
-      1 /* expected_count */);
-
-  auto entries = ukm_recorder.GetEntriesByName(
-      ukm::builders::SubframeDownload::kEntryName);
-  EXPECT_EQ(1u, entries.size());
-
-  switch (origin) {
-    case Origin::kAnchorAttribute: {
-      const ukm::mojom::UkmEntry* dc_entry =
-          ukm_recorder.GetDocumentCreatedEntryForSourceId(
-              entries.back()->source_id);
-      const ukm::UkmSource* navigation_source =
-          ukm_recorder.GetSourceForSourceId(*ukm_recorder.GetEntryMetric(
-              dc_entry,
-              ukm::builders::DocumentCreated::kNavigationSourceIdName));
-      EXPECT_EQ(main_url, navigation_source->url());
-    } break;
-    case Origin::kNavigation: {
-      ukm_recorder.ExpectEntrySourceHasUrl(entries.back(), main_url);
-    } break;
-  }
-
-  ukm_recorder.ExpectEntryMetric(
-      entries.back(), ukm::builders::SubframeDownload::kHasSandboxName,
-      expected_flags.has_sandbox);
-  ukm_recorder.ExpectEntryMetric(
-      entries.back(), ukm::builders::SubframeDownload::kIsCrossOriginName,
-      is_cross_origin);
-  ukm_recorder.ExpectEntryMetric(
-      entries.back(), ukm::builders::SubframeDownload::kIsAdFrameName,
-      is_ad_frame);
-  ukm_recorder.ExpectEntryMetric(
-      entries.back(), ukm::builders::SubframeDownload::kHasGestureName,
-      has_gesture);
-}
-
-INSTANTIATE_TEST_CASE_P(
-    /* no prefix */,
-    SubframeDownloadFlagsBrowserTest,
-    ::testing::Combine(
-        ::testing::Values(Origin::kNavigation, Origin::kAnchorAttribute),
-        ::testing::Bool(),
-        ::testing::Values(
-            SandboxOption::kNoSandbox,
-            SandboxOption::kDisallowDownloadsWithoutUserActivation,
-            SandboxOption::kAllowDownloadsWithoutUserActivation),
-        ::testing::Bool(),
-        ::testing::Bool(),
-        ::testing::Bool()));
diff --git a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
deleted file mode 100644
index 5e40344d..0000000
--- a/chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc
+++ /dev/null
@@ -1,707 +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/page_load_metrics/observers/ads_page_load_metrics_observer.h"
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/macros.h"
-#include "base/sequenced_task_runner.h"
-#include "base/strings/stringprintf.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "base/threading/sequenced_task_runner_handle.h"
-#include "base/time/time.h"
-#include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h"
-#include "chrome/browser/page_load_metrics/page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/page_load_tracker.h"
-#include "chrome/browser/subresource_filter/subresource_filter_test_harness.h"
-#include "chrome/common/page_load_metrics/test/page_load_metrics_test_util.h"
-#include "components/subresource_filter/content/browser/subresource_filter_observer_manager.h"
-#include "components/subresource_filter/core/common/load_policy.h"
-#include "components/ukm/test_ukm_recorder.h"
-#include "content/public/browser/global_request_id.h"
-#include "content/public/browser/navigation_handle.h"
-#include "content/public/browser/navigation_throttle.h"
-#include "content/public/browser/render_frame_host.h"
-#include "content/public/browser/web_contents.h"
-#include "content/public/browser/web_contents_observer.h"
-#include "content/public/common/resource_type.h"
-#include "content/public/test/navigation_simulator.h"
-#include "content/public/test/test_navigation_throttle.h"
-#include "content/public/test/test_navigation_throttle_inserter.h"
-#include "content/public/test/test_renderer_host.h"
-#include "net/base/host_port_pair.h"
-#include "services/metrics/public/cpp/ukm_builders.h"
-#include "url/gurl.h"
-
-using content::TestNavigationThrottle;
-using content::RenderFrameHost;
-using content::RenderFrameHostTester;
-using content::NavigationSimulator;
-
-namespace {
-
-struct ExpectedFrameBytes {
-  ExpectedFrameBytes(size_t cached_kb, size_t uncached_kb)
-      : cached_kb(cached_kb), uncached_kb(uncached_kb) {}
-  size_t cached_kb;
-  size_t uncached_kb;
-};
-
-enum class ResourceCached { NOT_CACHED = false, CACHED = true };
-enum class FrameType { AD = 0, NON_AD };
-
-const char kAdUrl[] = "https://ads.com/ad/disallowed.html";
-const char kNonAdUrl[] = "https://foo.com/";
-const char kNonAdUrlSameOrigin[] = "https://ads.com/foo";
-
-// Asynchronously cancels the navigation at WillProcessResponse. Before
-// cancelling, simulates loading a main frame resource.
-class ResourceLoadingCancellingThrottle
-    : public content::TestNavigationThrottle {
- public:
-  static std::unique_ptr<content::NavigationThrottle> Create(
-      content::NavigationHandle* handle) {
-    return std::make_unique<ResourceLoadingCancellingThrottle>(handle);
-  }
-
-  explicit ResourceLoadingCancellingThrottle(
-      content::NavigationHandle* navigation_handle)
-      : content::TestNavigationThrottle(navigation_handle) {
-    SetResponse(TestNavigationThrottle::WILL_PROCESS_RESPONSE,
-                TestNavigationThrottle::ASYNCHRONOUS, CANCEL);
-  }
-
- private:
-  // content::TestNavigationThrottle:
-  void OnWillRespond(NavigationThrottle::ThrottleCheckResult result) {
-    if (result.action() != CANCEL) {
-      return;
-    }
-
-    auto* observer =
-        page_load_metrics::MetricsWebContentsObserver::FromWebContents(
-            navigation_handle()->GetWebContents());
-    DCHECK(observer);
-
-    // Load a resource for the main frame before it commits.
-    std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources;
-    page_load_metrics::mojom::ResourceDataUpdatePtr resource =
-        page_load_metrics::mojom::ResourceDataUpdate::New();
-    resource->received_data_length = 10 * 1024;
-    resource->delta_bytes = 10 * 1024;
-    resource->encoded_body_length = 10 * 1024;
-    resource->was_fetched_via_cache = false;
-    resource->is_complete = true;
-    resource->is_primary_frame_resource = true;
-    resources.push_back(std::move(resource));
-    auto timing = page_load_metrics::mojom::PageLoadTimingPtr(base::in_place);
-    page_load_metrics::InitPageLoadTimingForTest(timing.get());
-    observer->OnTimingUpdated(
-        navigation_handle()->GetRenderFrameHost(), std::move(timing),
-        page_load_metrics::mojom::PageLoadMetadataPtr(base::in_place),
-        page_load_metrics::mojom::PageLoadFeaturesPtr(base::in_place),
-        resources, page_load_metrics::mojom::PageRenderDataPtr(base::in_place));
-  }
-
-  DISALLOW_COPY_AND_ASSIGN(ResourceLoadingCancellingThrottle);
-};
-
-std::string SuffixedHistogram(const std::string& suffix) {
-  return base::StringPrintf("PageLoad.Clients.Ads.%s", suffix.c_str());
-}
-
-// Verifies that the histograms match what is expected.
-void TestHistograms(const base::HistogramTester& histograms,
-                    const std::vector<ExpectedFrameBytes>& ad_frames,
-                    size_t non_ad_cached_kb,
-                    size_t non_ad_uncached_kb) {
-  size_t total_ad_cached_kb = 0;
-  size_t total_ad_uncached_kb = 0;
-  size_t total_ad_kb = 0;
-  size_t ad_frame_count = 0;
-
-  std::map<size_t, int> frames_with_total_byte_count;
-  std::map<size_t, int> frames_with_network_byte_count;
-  std::map<size_t, int> frames_with_percent_network_count;
-
-  // Perform some initial calculations on the number of bytes, of each type,
-  // in each ad frame.
-  for (const ExpectedFrameBytes& bytes : ad_frames) {
-    total_ad_cached_kb += bytes.cached_kb;
-    total_ad_uncached_kb += bytes.uncached_kb;
-    total_ad_kb += bytes.cached_kb + bytes.uncached_kb;
-
-    if (total_ad_kb == 0)
-      continue;
-
-    ad_frame_count += 1;
-
-    size_t total_frame_kb = bytes.cached_kb + bytes.uncached_kb;
-
-    frames_with_total_byte_count[total_frame_kb] += 1;
-    frames_with_network_byte_count[bytes.uncached_kb] += 1;
-    frames_with_percent_network_count[(bytes.uncached_kb * 100) /
-                                      total_frame_kb] += 1;
-  }
-
-  // Test the histograms.
-  histograms.ExpectUniqueSample(
-      SuffixedHistogram(
-          "SubresourceFilter.FrameCounts.AnyParentFrame.AdFrames"),
-      ad_frame_count, 1);
-
-  if (ad_frame_count == 0)
-    return;
-
-  for (const auto& total_bytes_and_count : frames_with_total_byte_count) {
-    histograms.ExpectBucketCount(
-        SuffixedHistogram("Bytes.AdFrames.PerFrame.Total"),
-        total_bytes_and_count.first, total_bytes_and_count.second);
-  }
-  for (const auto& network_bytes_and_count : frames_with_network_byte_count) {
-    histograms.ExpectBucketCount(
-        SuffixedHistogram("Bytes.AdFrames.PerFrame.Network"),
-        network_bytes_and_count.first, network_bytes_and_count.second);
-  }
-  for (const auto& percent_network_and_count :
-       frames_with_percent_network_count) {
-    histograms.ExpectBucketCount(
-        SuffixedHistogram("Bytes.AdFrames.PerFrame.PercentNetwork"),
-        percent_network_and_count.first, percent_network_and_count.second);
-  }
-
-  histograms.ExpectUniqueSample(
-      SuffixedHistogram("Bytes.AdFrames.Aggregate.Total"), total_ad_kb, 1);
-  histograms.ExpectUniqueSample(
-      SuffixedHistogram("Bytes.AdFrames.Aggregate.Network"),
-      total_ad_uncached_kb, 1);
-  histograms.ExpectUniqueSample(
-      SuffixedHistogram("Bytes.FullPage.Total"),
-      non_ad_cached_kb + non_ad_uncached_kb + total_ad_kb, 1);
-  histograms.ExpectUniqueSample(SuffixedHistogram("Bytes.FullPage.Network"),
-                                non_ad_uncached_kb + total_ad_uncached_kb, 1);
-  histograms.ExpectUniqueSample(
-      SuffixedHistogram("Bytes.NonAdFrames.Aggregate.Total"),
-      non_ad_cached_kb + non_ad_uncached_kb, 1);
-  histograms.ExpectUniqueSample(
-      SuffixedHistogram("Bytes.FullPage.Total.PercentAds"),
-      (total_ad_kb * 100) /
-          (total_ad_kb + non_ad_cached_kb + non_ad_uncached_kb),
-      1);
-  histograms.ExpectUniqueSample(
-      SuffixedHistogram("Bytes.AdFrames.Aggregate.PercentNetwork"),
-      ((total_ad_uncached_kb * 100) / total_ad_kb), 1);
-  histograms.ExpectUniqueSample(
-      SuffixedHistogram("Bytes.FullPage.Network.PercentAds"),
-      (total_ad_uncached_kb * 100) /
-          (total_ad_uncached_kb + non_ad_uncached_kb),
-      1);
-}
-
-}  // namespace
-
-class AdsPageLoadMetricsObserverTest : public SubresourceFilterTestHarness {
- public:
-  AdsPageLoadMetricsObserverTest() {}
-
-  void SetUp() override {
-    SubresourceFilterTestHarness::SetUp();
-    tester_ =
-        std::make_unique<page_load_metrics::PageLoadMetricsObserverTester>(
-            web_contents(),
-            base::BindRepeating(
-                &AdsPageLoadMetricsObserverTest::RegisterObservers,
-                base::Unretained(this)));
-    ConfigureAsSubresourceFilterOnlyURL(GURL(kAdUrl));
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* NavigateFrame(const std::string& url,
-                                 content::RenderFrameHost* frame) {
-    auto navigation_simulator =
-        NavigationSimulator::CreateRendererInitiated(GURL(url), frame);
-    navigation_simulator->Commit();
-    return navigation_simulator->GetFinalRenderFrameHost();
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* NavigateMainFrame(const std::string& url) {
-    return NavigateFrame(url, web_contents()->GetMainFrame());
-  }
-
-  // Frame creation doesn't trigger a mojo call since unit tests have no render
-  // process. Just mock them for now.
-  void OnAdSubframeDetected(RenderFrameHost* render_frame_host) {
-    subresource_filter::SubresourceFilterObserverManager::FromWebContents(
-        web_contents())
-        ->NotifyAdSubframeDetected(render_frame_host);
-  }
-
-  // Returns the final RenderFrameHost after navigation commits.
-  RenderFrameHost* CreateAndNavigateSubFrame(const std::string& url,
-                                             content::RenderFrameHost* parent) {
-    RenderFrameHost* subframe =
-        RenderFrameHostTester::For(parent)->AppendChild("frame_name");
-    auto navigation_simulator =
-        NavigationSimulator::CreateRendererInitiated(GURL(url), subframe);
-    navigation_simulator->Commit();
-    return navigation_simulator->GetFinalRenderFrameHost();
-  }
-
-  void ResourceDataUpdate(RenderFrameHost* render_frame_host,
-                          ResourceCached resource_cached,
-                          int resource_size_in_kbyte,
-                          std::string mime_type = "",
-                          bool is_ad_resource = false) {
-    std::vector<page_load_metrics::mojom::ResourceDataUpdatePtr> resources;
-    page_load_metrics::mojom::ResourceDataUpdatePtr resource =
-        page_load_metrics::mojom::ResourceDataUpdate::New();
-    resource->received_data_length =
-        static_cast<bool>(resource_cached) ? 0 : resource_size_in_kbyte << 10;
-    resource->delta_bytes = resource->received_data_length;
-    resource->encoded_body_length = resource_size_in_kbyte << 10;
-    resource->reported_as_ad_resource = is_ad_resource;
-    resource->is_complete = true;
-    resource->was_fetched_via_cache = static_cast<bool>(resource_cached);
-    resource->mime_type = mime_type;
-    resource->is_primary_frame_resource = true;
-    resources.push_back(std::move(resource));
-    tester_->SimulateResourceDataUseUpdate(resources, render_frame_host);
-  }
-
-  void TimingUpdate(const page_load_metrics::mojom::PageLoadTiming& timing) {
-    tester_->SimulateTimingUpdate(timing);
-  }
-
-  page_load_metrics::PageLoadMetricsObserverTester* tester() {
-    return tester_.get();
-  }
-
-  base::HistogramTester& histogram_tester() { return histogram_tester_; }
-
-  AdsPageLoadMetricsObserver* ads_observer_ = nullptr;
-
- private:
-  void RegisterObservers(page_load_metrics::PageLoadTracker* tracker) {
-    auto observer = std::make_unique<AdsPageLoadMetricsObserver>();
-    ads_observer_ = observer.get();
-    tracker->AddObserver(std::move(observer));
-  }
-
-  base::HistogramTester histogram_tester_;
-  std::unique_ptr<page_load_metrics::PageLoadMetricsObserverTester> tester_;
-
-  DISALLOW_COPY_AND_ASSIGN(AdsPageLoadMetricsObserverTest);
-};
-
-TEST_F(AdsPageLoadMetricsObserverTest, PageWithNoAds) {
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* frame1 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
-  RenderFrameHost* frame2 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
-  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(frame1, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(frame2, ResourceCached::NOT_CACHED, 10);
-
-  // Navigate again to trigger histograms.
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  TestHistograms(histogram_tester(), std::vector<ExpectedFrameBytes>(),
-                 0 /* non_ad_cached_kb */, 30 /* non_ad_uncached_kb */);
-
-  // Verify that other UMA wasn't written.
-  histogram_tester().ExpectTotalCount(
-      "PageLoad.Clients.Ads.Bytes.AdFrames.Aggregate.Total", 0);
-}
-
-TEST_F(AdsPageLoadMetricsObserverTest, PageWithAds) {
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* frame1 = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
-  RenderFrameHost* frame2 = CreateAndNavigateSubFrame(kAdUrl, main_frame);
-  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(frame1, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(frame2, ResourceCached::NOT_CACHED, 10);
-
-  // Navigate again to trigger histograms.
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  TestHistograms(histogram_tester(), {{0, 10}}, 0 /* non_ad_cached_kb */,
-                 20 /* non_ad_uncached_kb */);
-}
-
-TEST_F(AdsPageLoadMetricsObserverTest, ResourceBeforeAdFrameCommits) {
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-
-  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-
-  // Create subframe and load resource before commit.
-  RenderFrameHost* subframe =
-      RenderFrameHostTester::For(main_frame)->AppendChild("foo");
-  auto navigation_simulator =
-      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe);
-  ResourceDataUpdate(subframe, ResourceCached::NOT_CACHED, 10);
-  navigation_simulator->Commit();
-
-  // Navigate again to trigger histograms.
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  TestHistograms(histogram_tester(), {{0, 10}}, 0 /* non_ad_cached_kb */,
-                 10 /*non_ad_uncached_kb*/);
-}
-
-// Test that the cross-origin ad subframe navigation metric works as it's
-// supposed to, triggering a false addition with each ad that's in the same
-// origin as the main page, and a true when when the ad has a separate origin.
-TEST_F(AdsPageLoadMetricsObserverTest, AdsOriginStatusMetrics) {
-  const char kCrossOriginHistogramId[] =
-      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AdFrames.PerFrame."
-      "OriginStatus";
-
-  // Test that when the main frame origin is different from a direct ad
-  // subframe it is correctly identified as cross-origin, but do not count
-  // indirect ad subframes.
-  {
-    base::HistogramTester histograms;
-    RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-    RenderFrameHost* ad_sub_frame =
-        CreateAndNavigateSubFrame(kAdUrl, main_frame);
-    ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-    ResourceDataUpdate(ad_sub_frame, ResourceCached::NOT_CACHED, 10);
-    ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, ad_sub_frame),
-                       ResourceCached::NOT_CACHED, 10);
-    // Trigger histograms by navigating away, then test them.
-    NavigateFrame(kAdUrl, main_frame);
-    histograms.ExpectUniqueSample(
-        kCrossOriginHistogramId,
-        AdsPageLoadMetricsObserver::AdOriginStatus::kCross, 1);
-  }
-
-  // Add a non-ad subframe and an ad subframe and make sure the total count
-  // only adjusts by one.
-  {
-    base::HistogramTester histograms;
-    RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-    ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-    ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, main_frame),
-                       ResourceCached::NOT_CACHED, 10);
-    ResourceDataUpdate(CreateAndNavigateSubFrame(kNonAdUrl, main_frame),
-                       ResourceCached::NOT_CACHED, 10);
-    // Trigger histograms by navigating away, then test them.
-    NavigateFrame(kAdUrl, main_frame);
-    histograms.ExpectUniqueSample(
-        kCrossOriginHistogramId,
-        AdsPageLoadMetricsObserver::AdOriginStatus::kCross, 1);
-  }
-
-  // Add an ad subframe in the same origin as the parent frame and make sure it
-  // gets identified as non-cross-origin. Note: top-level navigations are never
-  // considered to be ads.
-  {
-    base::HistogramTester histograms;
-    RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrlSameOrigin);
-    ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-    ResourceDataUpdate(CreateAndNavigateSubFrame(kAdUrl, main_frame),
-                       ResourceCached::NOT_CACHED, 10);
-    // Trigger histograms by navigating away, then test them.
-    NavigateFrame(kAdUrl, main_frame);
-    histograms.ExpectUniqueSample(
-        kCrossOriginHistogramId,
-        AdsPageLoadMetricsObserver::AdOriginStatus::kSame, 1);
-  }
-}
-
-TEST_F(AdsPageLoadMetricsObserverTest, PageWithAdFrameThatRenavigates) {
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* ad_frame = CreateAndNavigateSubFrame(kAdUrl, main_frame);
-
-  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED, 10);
-
-  // Navigate the ad frame again.
-  ad_frame = NavigateFrame(kNonAdUrl, ad_frame);
-
-  // In total, 30KB for entire page and 20 in one ad frame.
-  ResourceDataUpdate(ad_frame, ResourceCached::NOT_CACHED, 10);
-
-  // Navigate again to trigger histograms.
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
-                 10 /* non_ad_uncached_kb */);
-}
-
-TEST_F(AdsPageLoadMetricsObserverTest, PageWithNonAdFrameThatRenavigatesToAd) {
-  // Main frame.
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-
-  // Sub frame that is not an ad.
-  RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
-
-  // Child of the sub-frame that is an ad.
-  RenderFrameHost* sub_frame_child_ad =
-      CreateAndNavigateSubFrame(kAdUrl, sub_frame);
-
-  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(sub_frame_child_ad, ResourceCached::NOT_CACHED, 10);
-
-  // Navigate the subframe again, this time it's an ad.
-  sub_frame = NavigateFrame(kAdUrl, sub_frame);
-  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
-
-  // In total, 40KB was loaded for the entire page and 20KB from ad
-  // frames (the original child ad frame and the renavigated frame which
-  // turned into an ad).
-
-  // Navigate again to trigger histograms.
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  TestHistograms(histogram_tester(), {{0, 10}, {0, 10}},
-                 0 /* non_ad_cached_kb */, 20 /* non_ad_uncached_kb */);
-}
-
-TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedNavigation) {
-  // If the first navigation in a frame is aborted, keep track of its bytes.
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-
-  // Create an ad subframe that aborts before committing.
-  RenderFrameHost* subframe_ad =
-      RenderFrameHostTester::For(main_frame)->AppendChild("foo");
-  auto navigation_simulator =
-      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe_ad);
-  // The sub-frame renavigates before it commits.
-  navigation_simulator->Start();
-  OnAdSubframeDetected(subframe_ad);
-  navigation_simulator->Fail(net::ERR_ABORTED);
-
-  // Load resources for the aborted frame (e.g., simulate the navigation
-  // aborting due to a doc.write during provisional navigation). They should
-  // be counted.
-  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
-
-  // Navigate again to trigger histograms.
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
-                 10 /* non_ad_uncached_kb */);
-}
-
-TEST_F(AdsPageLoadMetricsObserverTest, CountAbortedSecondNavigationForFrame) {
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-
-  // Sub frame that is not an ad.
-  RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
-  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
-
-  // Now navigate (and abort) the subframe to an ad.
-  auto navigation_simulator =
-      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), sub_frame);
-  // The sub-frame renavigates before it commits.
-  navigation_simulator->Start();
-  OnAdSubframeDetected(sub_frame);
-  navigation_simulator->Fail(net::ERR_ABORTED);
-
-  // Load resources for the aborted frame (e.g., simulate the navigation
-  // aborting due to a doc.write during provisional navigation). Since the
-  // frame attempted to load an ad, the frame is tagged forever as an ad.
-  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
-  ResourceDataUpdate(sub_frame, ResourceCached::NOT_CACHED, 10);
-
-  // Navigate again to trigger histograms.
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
-                 20 /* non_ad_uncached_kb */);
-}
-
-TEST_F(AdsPageLoadMetricsObserverTest, TwoResourceLoadsBeforeCommit) {
-  // Main frame.
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  ResourceDataUpdate(main_frame, ResourceCached::NOT_CACHED, 10);
-
-  // Now open a subframe and have its resource load before notification of
-  // navigation finishing.
-  RenderFrameHost* subframe_ad =
-      RenderFrameHostTester::For(main_frame)->AppendChild("foo");
-  auto navigation_simulator =
-      NavigationSimulator::CreateRendererInitiated(GURL(kAdUrl), subframe_ad);
-  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
-
-  // The sub-frame renavigates before it commits.
-  navigation_simulator->Start();
-  OnAdSubframeDetected(subframe_ad);
-  navigation_simulator->Fail(net::ERR_ABORTED);
-
-  // Renavigate the subframe to a successful commit. But again, the resource
-  // loads before the observer sees the finished navigation.
-  ResourceDataUpdate(subframe_ad, ResourceCached::NOT_CACHED, 10);
-  NavigateFrame(kNonAdUrl, subframe_ad);
-
-  // Navigate again to trigger histograms.
-  NavigateFrame(kNonAdUrl, main_frame);
-
-  TestHistograms(histogram_tester(), {{0, 20}}, 0 /* non_ad_cached_kb */,
-                 10 /* non_ad_uncached_kb */);
-}
-
-// This tests an issue that is believed to be the cause of
-// https://crbug.com/721369. The issue is that a frame from a previous
-// navigation might commit during a new navigation, and the ads metrics won't
-// know about the frame's parent (because it doesn't exist in the page).
-TEST_F(AdsPageLoadMetricsObserverTest, FrameWithNoParent) {
-  RenderFrameHost* main_frame = NavigateMainFrame(kNonAdUrl);
-  RenderFrameHost* sub_frame = CreateAndNavigateSubFrame(kNonAdUrl, main_frame);
-
-  // Renavigate the child, but, while navigating, the main frame renavigates.
-  RenderFrameHost* child_of_subframe =
-      RenderFrameHostTester::For(sub_frame)->AppendChild("foo");
-  auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
-      GURL(kAdUrl), child_of_subframe);
-  navigation_simulator->Start();
-
-  // Main frame renavigates.
-  NavigateMainFrame(kNonAdUrl);
-
-  // Child frame commits.
-  navigation_simulator->Commit();
-  child_of_subframe = navigation_simulator->GetFinalRenderFrameHost();
-
-  // Test that a resource loaded into an unknown frame doesn't cause any
-  // issues.
-  ResourceDataUpdate(child_of_subframe, ResourceCached::NOT_CACHED, 10);
-}
-
-TEST_F(AdsPageLoadMetricsObserverTest, MainFrameResource) {
-  // Start main-frame navigation
-  auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
-      GURL(kNonAdUrl), web_contents()->GetMainFrame());
-  navigation_simulator->Start();
-  navigation_simulator->Commit();
-
-  ResourceDataUpdate(navigation_simulator->GetFinalRenderFrameHost(),
-                     ResourceCached::NOT_CACHED, 10);
-
-  NavigateMainFrame(kNonAdUrl);
-
-  // We only log histograms if we observed bytes for the page. Verify that the
-  // main frame resource was properly tracked and attributed.
-  histogram_tester().ExpectUniqueSample(
-      "PageLoad.Clients.Ads.SubresourceFilter.FrameCounts.AnyParentFrame."
-      "AdFrames",
-      0, 1);
-  // There shouldn't be any other histograms for a page with no ad
-  // resources.
-  EXPECT_EQ(1u, histogram_tester()
-                    .GetTotalCountsForPrefix("PageLoad.Clients.Ads.")
-                    .size());
-}
-
-// Make sure that ads histograms aren't recorded if the tracker never commits
-// (see https://crbug.com/723219).
-TEST_F(AdsPageLoadMetricsObserverTest, NoHistogramWithoutCommit) {
-  {
-    // Once the metrics observer has the GlobalRequestID, throttle.
-    content::TestNavigationThrottleInserter throttle_inserter(
-        web_contents(),
-        base::BindRepeating(&ResourceLoadingCancellingThrottle::Create));
-
-    // Start main-frame navigation. The commit will defer after calling
-    // WillProcessNavigationResponse, it will load a resource, and then the
-    // throttle will cancel the commit.
-    SimulateNavigateAndCommit(GURL(kNonAdUrl), main_rfh());
-  }
-
-  // Force navigation to a new page to make sure OnComplete() runs for the
-  // previous failed navigation.
-  NavigateMainFrame(kNonAdUrl);
-
-  // There shouldn't be any histograms for an aborted main frame.
-  EXPECT_EQ(0u, histogram_tester()
-                    .GetTotalCountsForPrefix("PageLoad.Clients.Ads.")
-                    .size());
-}
-
-// Frames that are disallowed (and filtered) by the subresource filter should
-// not be counted.
-TEST_F(AdsPageLoadMetricsObserverTest, FilterAds_DoNotLogMetrics) {
-  ConfigureAsSubresourceFilterOnlyURL(GURL(kNonAdUrl));
-  NavigateMainFrame(kNonAdUrl);
-
-  ResourceDataUpdate(main_rfh(), ResourceCached::NOT_CACHED, 10);
-
-  RenderFrameHost* subframe =
-      RenderFrameHostTester::For(main_rfh())->AppendChild("foo");
-  std::unique_ptr<NavigationSimulator> simulator =
-      NavigationSimulator::CreateRendererInitiated(GURL(kDefaultDisallowedUrl),
-                                                   subframe);
-  ResourceDataUpdate(subframe, ResourceCached::CACHED, 10);
-  simulator->Commit();
-
-  EXPECT_NE(content::NavigationThrottle::PROCEED,
-            simulator->GetLastThrottleCheckResult());
-
-  NavigateMainFrame(kNonAdUrl);
-  TestHistograms(histogram_tester(), std::vector<ExpectedFrameBytes>(),
-                 0u /* non_ad_cached_kb */, 0u /* non_ad_uncached_kb */);
-}
-
-// UKM metrics for ad page load are recorded correctly.
-TEST_F(AdsPageLoadMetricsObserverTest, AdPageLoadUKM) {
-  ukm::TestAutoSetUkmRecorder ukm_recorder;
-  NavigateMainFrame(kNonAdUrl);
-
-  page_load_metrics::mojom::PageLoadTiming timing;
-  page_load_metrics::InitPageLoadTimingForTest(&timing);
-  timing.navigation_start = base::Time::Now();
-  timing.response_start = base::TimeDelta::FromSeconds(0);
-  timing.interactive_timing->interactive = base::TimeDelta::FromSeconds(0);
-  PopulateRequiredTimingFields(&timing);
-  TimingUpdate(timing);
-  ResourceDataUpdate(
-      main_rfh(), ResourceCached::NOT_CACHED, 10 /* resource_size_in_kbyte */,
-      "application/javascript" /* mime_type */, false /* is_ad_resource */);
-  ResourceDataUpdate(
-      main_rfh(), ResourceCached::NOT_CACHED, 10 /* resource_size_in_kbyte */,
-      "application/javascript" /* mime_type */, true /* is_ad_resource */);
-  ResourceDataUpdate(main_rfh(), ResourceCached::NOT_CACHED,
-                     10 /* resource_size_in_kbyte */,
-                     "video/webm" /* mime_type */, true /* is_ad_resource */);
-  NavigateMainFrame(kNonAdUrl);
-
-  auto entries =
-      ukm_recorder.GetEntriesByName(ukm::builders::AdPageLoad::kEntryName);
-  EXPECT_EQ(1u, entries.size());
-
-  EXPECT_EQ(*ukm_recorder.GetEntryMetric(
-                entries.front(), ukm::builders::AdPageLoad::kTotalBytesName),
-            30);
-  EXPECT_EQ(*ukm_recorder.GetEntryMetric(
-                entries.front(), ukm::builders::AdPageLoad::kAdBytesName),
-            20);
-  EXPECT_EQ(
-      *ukm_recorder.GetEntryMetric(
-          entries.front(), ukm::builders::AdPageLoad::kAdJavascriptBytesName),
-      10);
-  EXPECT_EQ(*ukm_recorder.GetEntryMetric(
-                entries.front(), ukm::builders::AdPageLoad::kAdVideoBytesName),
-            10);
-  EXPECT_GT(
-      *ukm_recorder.GetEntryMetric(
-          entries.front(), ukm::builders::AdPageLoad::kAdBytesPerSecondName),
-      0);
-  EXPECT_GT(
-      *ukm_recorder.GetEntryMetric(
-          entries.front(),
-          ukm::builders::AdPageLoad::kAdBytesPerSecondAfterInteractiveName),
-      0);
-}
diff --git a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc
index 6efc217..59c49b89 100644
--- a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc
@@ -259,8 +259,8 @@
   base::test::ScopedFeatureList scoped_feature_list;
 
   std::map<std::string, std::string> parameters = {
-      {"NoScriptInflationPercent", base::IntToString(inflation)},
-      {"NoScriptInflationBytes", base::IntToString(constant_savings)}};
+      {"NoScriptInflationPercent", base::NumberToString(inflation)},
+      {"NoScriptInflationBytes", base::NumberToString(constant_savings)}};
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       previews::features::kNoScriptPreviews, parameters);
 
@@ -297,9 +297,9 @@
   base::test::ScopedFeatureList scoped_feature_list;
 
   std::map<std::string, std::string> parameters = {
-      {"ResourceLoadingHintsInflationPercent", base::IntToString(inflation)},
+      {"ResourceLoadingHintsInflationPercent", base::NumberToString(inflation)},
       {"ResourceLoadingHintsInflationBytes",
-       base::IntToString(constant_savings)}};
+       base::NumberToString(constant_savings)}};
   scoped_feature_list.InitAndEnableFeatureWithParameters(
       previews::features::kResourceLoadingHints, parameters);
 
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 2aed9f82..323407e3 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_initialize.cc
@@ -12,7 +12,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/page_load_metrics/metrics_web_contents_observer.h"
 #include "chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer.h"
-#include "chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer.h"
 #include "chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer.h"
diff --git a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
index f33e298e..60d0d1a 100644
--- a/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
+++ b/chrome/browser/page_load_metrics/page_load_metrics_update_dispatcher.cc
@@ -370,16 +370,29 @@
           new_interactive_timing.first_invalidating_input;
       target_interactive_timing->interactive_detection =
           new_interactive_timing.interactive_detection;
+    }
 
-      // first/longest input delay are currently only tracked in the main frame.
+    if (MaybeUpdateTimeDelta(&target_interactive_timing->first_input_timestamp,
+                             navigation_start_offset,
+                             new_interactive_timing.first_input_timestamp)) {
+      // If we updated the first input timestamp, also update the
+      // associated first input delay.
       target_interactive_timing->first_input_delay =
           new_interactive_timing.first_input_delay;
-      target_interactive_timing->first_input_timestamp =
-          new_interactive_timing.first_input_timestamp;
-      target_interactive_timing->longest_input_delay =
-          new_interactive_timing.longest_input_delay;
-      target_interactive_timing->longest_input_timestamp =
-          new_interactive_timing.longest_input_timestamp;
+    }
+
+    if (new_interactive_timing.longest_input_delay.has_value()) {
+      base::TimeDelta new_longest_input_timestamp =
+          navigation_start_offset +
+          new_interactive_timing.longest_input_timestamp.value();
+      if (!target_interactive_timing->longest_input_delay.has_value() ||
+          new_interactive_timing.longest_input_delay.value() >
+              target_interactive_timing->longest_input_delay.value()) {
+        target_interactive_timing->longest_input_delay =
+            new_interactive_timing.longest_input_delay;
+        target_interactive_timing->longest_input_timestamp =
+            new_longest_input_timestamp;
+      }
     }
   }
 
diff --git a/chrome/browser/password_manager/native_backend_gnome_x.cc b/chrome/browser/password_manager/native_backend_gnome_x.cc
index 0fe1c175..617ccf09c 100644
--- a/chrome/browser/password_manager/native_backend_gnome_x.cc
+++ b/chrome/browser/password_manager/native_backend_gnome_x.cc
@@ -312,12 +312,12 @@
       "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
       "signon_realm", form.signon_realm.c_str(),
       "preferred", form.preferred,
-      "date_created", base::Int64ToString(date_created).c_str(),
+      "date_created", base::NumberToString(date_created).c_str(),
       "blacklisted_by_user", form.blacklisted_by_user,
       "type", form.type,
       "times_used", form.times_used,
       "scheme", form.scheme,
-      "date_synced", base::Int64ToString(date_synced).c_str(),
+      "date_synced", base::NumberToString(date_synced).c_str(),
       "display_name", UTF16ToUTF8(form.display_name).c_str(),
       "avatar_url", form.icon_url.spec().c_str(),
       // We serialize unique origins as "", in order to make other systems that
diff --git a/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc b/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
index eb82b7e..1d8786aba 100644
--- a/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
+++ b/chrome/browser/password_manager/native_backend_gnome_x_unittest.cc
@@ -462,8 +462,9 @@
     CheckUint32Attribute(item, "type", form.type);
     CheckUint32Attribute(item, "times_used", form.times_used);
     CheckUint32Attribute(item, "scheme", form.scheme);
-    CheckStringAttribute(item, "date_synced", base::Int64ToString(
-        form.date_synced.ToInternalValue()));
+    CheckStringAttribute(
+        item, "date_synced",
+        base::NumberToString(form.date_synced.ToInternalValue()));
     CheckStringAttribute(item, "display_name", UTF16ToUTF8(form.display_name));
     CheckStringAttribute(item, "avatar_url", form.icon_url.spec());
     // We serialize unique origins as "", in order to make other systems that
diff --git a/chrome/browser/password_manager/native_backend_libsecret.cc b/chrome/browser/password_manager/native_backend_libsecret.cc
index 3426c52e..789ecec 100644
--- a/chrome/browser/password_manager/native_backend_libsecret.cc
+++ b/chrome/browser/password_manager/native_backend_libsecret.cc
@@ -342,12 +342,12 @@
       "submit_element", UTF16ToUTF8(form.submit_element).c_str(),
       "signon_realm", form.signon_realm.c_str(),
       "preferred", form.preferred,
-      "date_created", base::Int64ToString(date_created).c_str(),
+      "date_created", base::NumberToString(date_created).c_str(),
       "blacklisted_by_user", form.blacklisted_by_user,
       "type", form.type,
       "times_used", form.times_used,
       "scheme", form.scheme,
-      "date_synced", base::Int64ToString(date_synced).c_str(),
+      "date_synced", base::NumberToString(date_synced).c_str(),
       "display_name", UTF16ToUTF8(form.display_name).c_str(),
       "avatar_url", form.icon_url.spec().c_str(),
       // We serialize unique origins as "", in order to make other systems that
diff --git a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc b/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
index 9fd1a80..9651a533 100644
--- a/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
+++ b/chrome/browser/password_manager/native_backend_libsecret_unittest.cc
@@ -415,7 +415,7 @@
     CheckUint32Attribute(item, "scheme", form.scheme);
     CheckStringAttribute(
         item, "date_synced",
-        base::Int64ToString(form.date_synced.ToInternalValue()));
+        base::NumberToString(form.date_synced.ToInternalValue()));
     CheckStringAttribute(item, "display_name", UTF16ToUTF8(form.display_name));
     CheckStringAttribute(item, "avatar_url", form.icon_url.spec());
     // We serialize unique origins as "", in order to make other systems that
diff --git a/chrome/browser/password_manager/password_accessory_metrics_util.h b/chrome/browser/password_manager/password_accessory_metrics_util.h
index 57bb8ed..e65267e 100644
--- a/chrome/browser/password_manager/password_accessory_metrics_util.h
+++ b/chrome/browser/password_manager/password_accessory_metrics_util.h
@@ -44,6 +44,7 @@
 enum class AccessoryTabType {
   ALL = 0,
   PASSWORDS = 1,
+  CREDIT_CARDS = 2,
   COUNT,
 };
 
@@ -69,6 +70,7 @@
 enum class AccessorySuggestionType {
   USERNAME = 0,
   PASSWORD = 1,
+  CREDIT_CARDS = 2,
   COUNT,
 };
 
diff --git a/chrome/browser/pdf/pdf_extension_test.cc b/chrome/browser/pdf/pdf_extension_test.cc
index 56e99a0f..05ce090 100644
--- a/chrome/browser/pdf/pdf_extension_test.cc
+++ b/chrome/browser/pdf/pdf_extension_test.cc
@@ -82,7 +82,6 @@
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/test/test_clipboard.h"
 #include "ui/base/resource/resource_bundle.h"
-#include "ui/events/keycodes/dom/keycode_converter.h"
 #include "ui/gfx/geometry/point.h"
 #include "url/gurl.h"
 
@@ -311,7 +310,8 @@
 
   void ConvertPageCoordToScreenCoord(WebContents* contents, gfx::Point* point) {
     ASSERT_TRUE(contents);
-    ASSERT_TRUE(content::ExecuteScript(contents,
+    ASSERT_TRUE(content::ExecuteScript(
+        contents,
         "var visiblePage = viewer.viewport.getMostVisiblePage();"
         "var visiblePageDimensions ="
         "    viewer.viewport.getPageScreenRect(visiblePage);"
@@ -319,10 +319,14 @@
         "var screenOffsetX = visiblePageDimensions.x - viewportPosition.x;"
         "var screenOffsetY = visiblePageDimensions.y - viewportPosition.y;"
         "var linkScreenPositionX ="
-        "    Math.floor(" + base::IntToString(point->x()) + " + screenOffsetX);"
-        "var linkScreenPositionY ="
-        "    Math.floor(" + base::IntToString(point->y()) + " +"
-        "    screenOffsetY);"));
+        "    Math.floor(" +
+            base::NumberToString(point->x()) +
+            " + screenOffsetX);"
+            "var linkScreenPositionY ="
+            "    Math.floor(" +
+            base::NumberToString(point->y()) +
+            " +"
+            "    screenOffsetY);"));
 
     int x;
     ASSERT_TRUE(content::ExecuteScriptAndExtractInt(
@@ -483,6 +487,20 @@
         content::BrowserContext::GetDownloadManager(browser_context);
     download_awaiter_ = std::make_unique<DownloadAwaiter>();
     download_manager->AddObserver(download_awaiter_.get());
+
+    // TODO(tommycli): PDFIFrameNavigationThrottle currently doesn't wait for
+    // the plugin list to be loaded, and this causes some unpredictable (but not
+    // catastrophic) behavior on startup. Remove this after we fix that.
+    base::RunLoop run_loop;
+    content::PluginService::GetInstance()->GetPlugins(base::BindOnce(
+        &PDFPluginDisabledTest::PluginsLoadedCallback, run_loop.QuitClosure()));
+    run_loop.Run();
+  }
+
+  static void PluginsLoadedCallback(
+      base::OnceClosure callback,
+      const std::vector<content::WebPluginInfo>& plugins) {
+    std::move(callback).Run();
   }
 
   void TearDownOnMainThread() override {
@@ -502,6 +520,28 @@
     PDFExtensionTest::TearDownOnMainThread();
   }
 
+  void ClickOpenButtonInIframe() {
+    int iframes_found = 0;
+    for (auto* host : GetActiveWebContents()->GetAllFrames()) {
+      if (host != GetActiveWebContents()->GetMainFrame()) {
+        ASSERT_TRUE(content::ExecJs(
+            host, "document.getElementById('open-button').click();"));
+        ++iframes_found;
+      }
+    }
+    ASSERT_EQ(1, iframes_found);
+  }
+
+  void ValidateSingleSuccessfulDownloadAndNoPDFPluginLaunch() {
+    // Validate that we downloaded a single PDF and didn't launch the PDF
+    // plugin.
+    GURL pdf_url(embedded_test_server()->GetURL("/pdf/test.pdf"));
+    EXPECT_EQ(pdf_url, AwaitAndGetLastDownloadedUrl());
+    EXPECT_EQ(1u, GetNumberOfDownloads());
+    EXPECT_EQ(0, CountPDFProcesses());
+  }
+
+ private:
   size_t GetNumberOfDownloads() {
     content::BrowserContext* browser_context =
         GetActiveWebContents()->GetBrowserContext();
@@ -517,7 +557,6 @@
     return download_awaiter_->GetLastUrl();
   }
 
- private:
   std::unique_ptr<DownloadAwaiter> download_awaiter_;
 };
 
@@ -526,10 +565,7 @@
   GURL pdf_url(embedded_test_server()->GetURL("/pdf/test.pdf"));
   ui_test_utils::NavigateToURL(browser(), pdf_url);
 
-  // Validate that we downloaded a single PDF and didn't launch the PDF plugin.
-  EXPECT_EQ(pdf_url, AwaitAndGetLastDownloadedUrl());
-  EXPECT_EQ(1u, GetNumberOfDownloads());
-  EXPECT_EQ(0, CountPDFProcesses());
+  ValidateSingleSuccessfulDownloadAndNoPDFPluginLaunch();
 }
 
 IN_PROC_BROWSER_TEST_F(PDFPluginDisabledTest, EmbedPdfPlaceholderWithCSP) {
@@ -548,11 +584,7 @@
                             ui::DomCode::ENTER, ui::VKEY_RETURN, false, false,
                             false, false);
 
-  // Validate that we downloaded a single PDF and didn't launch the PDF plugin.
-  GURL pdf_url(embedded_test_server()->GetURL("/pdf/test.pdf"));
-  EXPECT_EQ(pdf_url, AwaitAndGetLastDownloadedUrl());
-  EXPECT_EQ(1u, GetNumberOfDownloads());
-  EXPECT_EQ(0, CountPDFProcesses());
+  ValidateSingleSuccessfulDownloadAndNoPDFPluginLaunch();
 }
 
 IN_PROC_BROWSER_TEST_F(PDFPluginDisabledTest, IframePdfPlaceholderWithCSP) {
@@ -561,29 +593,30 @@
       embedded_test_server()->GetURL("/pdf/pdf_iframe_csp.html");
   ui_test_utils::NavigateToURL(browser(), iframe_page_url);
 
-  // Pass an Enter keystroke to the child <iframe>.
-  int keys_passed = 0;
-  for (auto* host : GetActiveWebContents()->GetAllFrames()) {
-    if (host != GetActiveWebContents()->GetMainFrame()) {
-      content::NativeWebKeyboardEvent key_event(
-          blink::WebKeyboardEvent::kChar, blink::WebInputEvent::kNoModifiers,
-          blink::WebInputEvent::GetStaticTimeStampForTests());
-      key_event.windows_key_code = ui::VKEY_RETURN;
-      key_event.native_key_code =
-          ui::KeycodeConverter::DomCodeToNativeKeycode(ui::DomCode::ENTER);
-      key_event.dom_code = static_cast<int>(ui::DomCode::ENTER);
-      key_event.dom_key = ui::DomKey::ENTER;
-      host->GetView()->GetRenderWidgetHost()->ForwardKeyboardEvent(key_event);
-      keys_passed++;
-    }
-  }
-  ASSERT_EQ(1, keys_passed);
+  ClickOpenButtonInIframe();
+  ValidateSingleSuccessfulDownloadAndNoPDFPluginLaunch();
+}
 
-  // Validate that we downloaded a single PDF and didn't launch the PDF plugin.
-  GURL pdf_url(embedded_test_server()->GetURL("/pdf/test.pdf"));
-  EXPECT_EQ(pdf_url, AwaitAndGetLastDownloadedUrl());
-  EXPECT_EQ(1u, GetNumberOfDownloads());
-  EXPECT_EQ(0, CountPDFProcesses());
+IN_PROC_BROWSER_TEST_F(PDFPluginDisabledTest,
+                       IframePlaceholderInjectedIntoNewWindow) {
+  // This is an unusual test to verify crbug.com/924823. We are injecting the
+  // HTML for a PDF IFRAME into a newly created popup with an undefined URL.
+  ASSERT_TRUE(
+      content::EvalJs(
+          GetActiveWebContents(),
+          content::JsReplace(
+              "new Promise((resolve) => {"
+              "  var popup = window.open();"
+              "  popup.document.writeln("
+              "      '<iframe id=\"pdf_iframe\" src=\"' + $1 + '\"></iframe>');"
+              "  var iframe = popup.document.getElementById('pdf_iframe');"
+              "  iframe.onload = () => resolve(true);"
+              "});",
+              embedded_test_server()->GetURL("/pdf/test.pdf").spec()))
+          .ExtractBool());
+
+  ClickOpenButtonInIframe();
+  ValidateSingleSuccessfulDownloadAndNoPDFPluginLaunch();
 }
 
 // We break PDFExtensionLoadTest up into kNumberLoadTestParts.
diff --git a/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc b/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
index b6da8e84..c22ac01 100644
--- a/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
+++ b/chrome/browser/plugins/pdf_iframe_navigation_throttle.cc
@@ -7,15 +7,20 @@
 #include <string>
 
 #include "base/feature_list.h"
+#include "base/memory/weak_ptr.h"
+#include "base/task/post_task.h"
 #include "chrome/common/chrome_content_client.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/pdf_util.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/download_utils.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_user_data.h"
 #include "net/base/escape.h"
 #include "net/http/http_response_headers.h"
 #include "ppapi/buildflags/buildflags.h"
@@ -25,6 +30,36 @@
 #include "content/public/browser/plugin_service.h"
 #endif
 
+namespace {
+
+// Used to scope the posted navigation task to the lifetime of |web_contents|.
+class WebContentsLifetimeHelper
+    : public content::WebContentsUserData<WebContentsLifetimeHelper> {
+ public:
+  explicit WebContentsLifetimeHelper(content::WebContents* web_contents)
+      : web_contents_(web_contents) {}
+
+  base::WeakPtr<WebContentsLifetimeHelper> GetWeakPtr() {
+    return weak_factory_.GetWeakPtr();
+  }
+
+  void NavigateIFrameToPlaceholder(const content::OpenURLParams& url_params) {
+    web_contents_->OpenURL(url_params);
+  }
+
+ private:
+  friend class content::WebContentsUserData<WebContentsLifetimeHelper>;
+
+  content::WebContents* const web_contents_;
+  base::WeakPtrFactory<WebContentsLifetimeHelper> weak_factory_{this};
+
+  WEB_CONTENTS_USER_DATA_KEY_DECL();
+};
+
+WEB_CONTENTS_USER_DATA_KEY_IMPL(WebContentsLifetimeHelper)
+
+}  // namespace
+
 PDFIFrameNavigationThrottle::PDFIFrameNavigationThrottle(
     content::NavigationHandle* navigation_handle)
     : content::NavigationThrottle(navigation_handle) {}
@@ -91,14 +126,28 @@
   if (!base::FeatureList::IsEnabled(features::kClickToOpenPDFPlaceholder))
     return content::NavigationThrottle::PROCEED;
 
+  // Prepare the params to navigate to the placeholder.
   std::string html = GetPDFPlaceholderHTML(navigation_handle()->GetURL());
   GURL data_url("data:text/html," + net::EscapePath(html));
+  content::OpenURLParams params(data_url, navigation_handle()->GetReferrer(),
+                                navigation_handle()->GetFrameTreeNodeId(),
+                                WindowOpenDisposition::CURRENT_TAB,
+                                ui::PAGE_TRANSITION_AUTO_SUBFRAME,
+                                navigation_handle()->IsRendererInitiated());
+  params.initiator_origin = navigation_handle()->GetInitiatorOrigin();
 
-  navigation_handle()->GetWebContents()->OpenURL(
-      content::OpenURLParams(data_url, navigation_handle()->GetReferrer(),
-                             navigation_handle()->GetFrameTreeNodeId(),
-                             WindowOpenDisposition::CURRENT_TAB,
-                             ui::PAGE_TRANSITION_AUTO_SUBFRAME, false));
+  // Post a task to navigate to the placeholder HTML. We don't navigate
+  // synchronously here, as starting a navigation within a navigation is
+  // an antipattern. Use a helper object scoped to the WebContents lifetime to
+  // scope the navigation task to the WebContents lifetime.
+  content::WebContents* web_contents = navigation_handle()->GetWebContents();
+  WebContentsLifetimeHelper::CreateForWebContents(web_contents);
+  WebContentsLifetimeHelper* helper =
+      WebContentsLifetimeHelper::FromWebContents(web_contents);
+  base::PostTaskWithTraits(
+      FROM_HERE, {content::BrowserThread::UI},
+      base::BindOnce(&WebContentsLifetimeHelper::NavigateIFrameToPlaceholder,
+                     helper->GetWeakPtr(), params));
 
   return content::NavigationThrottle::CANCEL_AND_IGNORE;
 }
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index ced0ba0..4c78b9ba 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -535,6 +535,9 @@
 #endif  // !defined(OS_MACOSX)
 
 #if defined(OS_CHROMEOS)
+  { key::kCertificateManagementAllowed,
+    prefs::kCertificateManagementAllowed,
+    base::Value::Type::INTEGER },
   { key::kChromeOsLockOnIdleSuspend,
     ash::prefs::kEnableAutoScreenLock,
     base::Value::Type::BOOLEAN },
diff --git a/chrome/browser/policy/developer_tools_policy_handler.cc b/chrome/browser/policy/developer_tools_policy_handler.cc
index d32c6f90..8559ca9 100644
--- a/chrome/browser/policy/developer_tools_policy_handler.cc
+++ b/chrome/browser/policy/developer_tools_policy_handler.cc
@@ -95,7 +95,8 @@
   if (!IsValidDeveloperToolsAvailabilityValue(value)) {
     if (errors) {
       errors->AddError(key::kDeveloperToolsAvailability,
-                       IDS_POLICY_OUT_OF_RANGE_ERROR, base::IntToString(value));
+                       IDS_POLICY_OUT_OF_RANGE_ERROR,
+                       base::NumberToString(value));
     }
     return PolicyCheckResult::kInvalid;
   }
diff --git a/chrome/browser/policy/policy_browsertest.cc b/chrome/browser/policy/policy_browsertest.cc
index 8d1ccf3..b41c53f68 100644
--- a/chrome/browser/policy/policy_browsertest.cc
+++ b/chrome/browser/policy/policy_browsertest.cc
@@ -1203,11 +1203,11 @@
           security_interstitials::SecurityInterstitialTabHelper::
               FromWebContents(tab);
       helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting()
-          ->CommandReceived(base::IntToString(command));
+          ->CommandReceived(base::NumberToString(command));
       return;
     }
     tab->GetInterstitialPage()->GetDelegateForTesting()->CommandReceived(
-        base::IntToString(command));
+        base::NumberToString(command));
   }
 
  private:
diff --git a/chrome/browser/policy/policy_conversions.cc b/chrome/browser/policy/policy_conversions.cc
index 8340561..007dc90 100644
--- a/chrome/browser/policy/policy_conversions.cc
+++ b/chrome/browser/policy/policy_conversions.cc
@@ -242,8 +242,12 @@
 #if defined(OS_CHROMEOS)
 void GetDeviceLocalAccountPolicies(bool convert_values, Value* values) {
   // DeviceLocalAccount policies are not available for not affiliated users
-  if (!user_manager::UserManager::Get()->GetPrimaryUser()->IsAffiliated())
+  if (!user_manager::UserManager::IsInitialized() ||
+      !user_manager::UserManager::Get()->GetPrimaryUser() ||
+      !user_manager::UserManager::Get()->GetPrimaryUser()->IsAffiliated()) {
     return;
+  }
+
   BrowserPolicyConnectorChromeOS* connector =
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
   DCHECK(connector);  // always not-null
diff --git a/chrome/browser/prefetch/prefetch_browsertest.cc b/chrome/browser/prefetch/prefetch_browsertest.cc
index a4069db..e3528763 100644
--- a/chrome/browser/prefetch/prefetch_browsertest.cc
+++ b/chrome/browser/prefetch/prefetch_browsertest.cc
@@ -35,6 +35,15 @@
 const char kRedirectPrefetchUrl[] = "/redirect";
 const char kRedirectedPrefetchUrl[] = "/redirected";
 
+bool HasVariationsHeader(
+    const net::test_server::HttpRequest::HeaderMap& headers) {
+  for (const auto& pair : headers) {
+    if (variations::IsVariationsHeader(pair.first))
+      return true;
+  }
+  return false;
+}
+
 class MockNetworkChangeNotifierWIFI : public NetworkChangeNotifier {
  public:
   ConnectionType GetCurrentConnectionType() const override {
@@ -181,23 +190,20 @@
             requests[0].headers["Host"]);
   EXPECT_EQ(kRedirectPrefetchPage, requests[0].relative_url);
   // The navigation request to Google host must have X-Client-Data header.
-  EXPECT_TRUE(requests[0].headers.find(variations::kClientDataHeader) !=
-              requests[0].headers.end());
+  EXPECT_TRUE(HasVariationsHeader(requests[0].headers));
 
   EXPECT_EQ(base::StringPrintf("www.google.com:%u", https_server.port()),
             requests[1].headers["Host"]);
   EXPECT_EQ(kRedirectPrefetchUrl, requests[1].relative_url);
   // The prefetch request to Google host must have X-Client-Data header.
-  EXPECT_TRUE(requests[1].headers.find(variations::kClientDataHeader) !=
-              requests[1].headers.end());
+  EXPECT_TRUE(HasVariationsHeader(requests[1].headers));
 
   EXPECT_EQ(base::StringPrintf("example.com:%u", https_server.port()),
             requests[2].headers["Host"]);
   EXPECT_EQ(kRedirectedPrefetchUrl, requests[2].relative_url);
   // The redirected prefetch request to non-Google host must not have
   // X-Client-Data header.
-  EXPECT_TRUE(requests[2].headers.find(variations::kClientDataHeader) ==
-              requests[2].headers.end());
+  EXPECT_FALSE(HasVariationsHeader(requests[2].headers));
 }
 
 }  // namespace
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index 30731dc..eceb7d5 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -279,6 +279,7 @@
 #include "chrome/browser/media/protected_media_identifier_permission_context.h"
 #include "chrome/browser/metrics/chromeos_metrics_provider.h"
 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
+#include "chrome/browser/ui/webui/certificates_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/enable_debugging_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/hid_detection_screen_handler.h"
 #include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
@@ -727,6 +728,7 @@
 
 #if defined(OS_CHROMEOS)
   arc::prefs::RegisterProfilePrefs(registry);
+  certificate_manager::CertificatesHandler::RegisterProfilePrefs(registry);
   chromeos::CupsPrintersManager::RegisterProfilePrefs(registry);
   chromeos::first_run::RegisterProfilePrefs(registry);
   chromeos::file_system_provider::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
index 8e45024..f65b170 100644
--- a/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
+++ b/chrome/browser/prefs/tracked/pref_hash_browsertest.cc
@@ -372,7 +372,8 @@
 
       num_tracked_prefs_ += num_split_tracked_prefs;
 
-      std::string num_tracked_prefs_str = base::IntToString(num_tracked_prefs_);
+      std::string num_tracked_prefs_str =
+          base::NumberToString(num_tracked_prefs_);
       EXPECT_EQ(static_cast<int>(num_tracked_prefs_str.size()),
                 base::WriteFile(num_tracked_prefs_file,
                                 num_tracked_prefs_str.c_str(),
diff --git a/chrome/browser/prerender/prerender_history.cc b/chrome/browser/prerender/prerender_history.cc
index fef80168..1bb6597 100644
--- a/chrome/browser/prerender/prerender_history.cc
+++ b/chrome/browser/prerender/prerender_history.cc
@@ -48,7 +48,7 @@
     // integers.
     entry_dict->SetString(
         "end_time",
-        base::Int64ToString((entry.end_time - epoch_start).InMilliseconds()));
+        base::NumberToString((entry.end_time - epoch_start).InMilliseconds()));
     return_list->Append(std::move(entry_dict));
   }
   return std::move(return_list);
diff --git a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
index 62300436..1b38521 100644
--- a/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
+++ b/chrome/browser/prerender/prerender_nostate_prefetch_browsertest.cc
@@ -425,7 +425,7 @@
   GURL second_script_url(std::string("http://foo.bar") + kPrefetchScript2);
   GURL prefetch_response_header_csp = GetURLWithReplacement(
       kPrefetchResponseHeaderCSP, "REPLACE_WITH_PORT",
-      base::IntToString(src_server()->host_port_pair().port()));
+      base::NumberToString(src_server()->host_port_pair().port()));
 
   PrefetchFromURL(prefetch_response_header_csp,
                   FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
@@ -444,7 +444,7 @@
   GURL second_script_url(std::string("http://foo.bar") + kPrefetchScript2);
   GURL prefetch_meta_tag_csp = GetURLWithReplacement(
       kPrefetchMetaCSP, "REPLACE_WITH_PORT",
-      base::IntToString(src_server()->host_port_pair().port()));
+      base::NumberToString(src_server()->host_port_pair().port()));
 
   PrefetchFromURL(prefetch_meta_tag_csp,
                   FINAL_STATUS_NOSTATE_PREFETCH_FINISHED);
diff --git a/chrome/browser/previews/previews_infobar_delegate_unittest.cc b/chrome/browser/previews/previews_infobar_delegate_unittest.cc
index 78e80d9..925cdb7 100644
--- a/chrome/browser/previews/previews_infobar_delegate_unittest.cc
+++ b/chrome/browser/previews/previews_infobar_delegate_unittest.cc
@@ -585,7 +585,7 @@
   TestStalePreviews(
       staleness_in_minutes, false /* is_reload */,
       l10n_util::GetStringFUTF16(IDS_PREVIEWS_INFOBAR_TIMESTAMP_MINUTES,
-                                 base::IntToString16(staleness_in_minutes)),
+                                 base::NumberToString16(staleness_in_minutes)),
       PreviewsUITabHelper::PreviewsStalePreviewTimestamp::kTimestampShown);
 }
 
@@ -612,7 +612,7 @@
   TestStalePreviews(
       staleness_in_hours * 60, false /* is_reload */,
       l10n_util::GetStringFUTF16(IDS_PREVIEWS_INFOBAR_TIMESTAMP_HOURS,
-                                 base::IntToString16(staleness_in_hours)),
+                                 base::NumberToString16(staleness_in_hours)),
       PreviewsUITabHelper::PreviewsStalePreviewTimestamp::kTimestampShown);
 }
 
@@ -626,7 +626,7 @@
   TestStalePreviews(
       2, false /* is_reload */,
       l10n_util::GetStringFUTF16(IDS_PREVIEWS_INFOBAR_TIMESTAMP_MINUTES,
-                                 base::IntToString16(2)),
+                                 base::NumberToString16(2)),
       PreviewsUITabHelper::PreviewsStalePreviewTimestamp::kTimestampShown);
 
   TestStalePreviews(6, false /* is_reload */, base::string16(),
@@ -661,7 +661,7 @@
   TestStalePreviews(
       staleness_in_minutes, false /* is_reload */,
       l10n_util::GetStringFUTF16(IDS_PREVIEWS_INFOBAR_TIMESTAMP_MINUTES,
-                                 base::IntToString16(staleness_in_minutes)),
+                                 base::NumberToString16(staleness_in_minutes)),
       PreviewsUITabHelper::PreviewsStalePreviewTimestamp::kTimestampShown);
 
   staleness_in_minutes = 1;
diff --git a/chrome/browser/previews/previews_lite_page_browsertest.cc b/chrome/browser/previews/previews_lite_page_browsertest.cc
index 22b4e396..f180d92 100644
--- a/chrome/browser/previews/previews_lite_page_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_browsertest.cc
@@ -205,7 +205,7 @@
         {"blacklisted_path_suffixes", ".mp4,.jpg"},
         {"trigger_on_localhost", "true"},
         {"navigation_timeout_milliseconds",
-         use_timeout ? base::IntToString(kTimeoutMs) : "0"},
+         use_timeout ? base::NumberToString(kTimeoutMs) : "0"},
         {"control_group", is_control ? "true" : "false"}};
 
     scoped_parameterized_feature_list_.InitAndEnableFeatureWithParameters(
@@ -420,9 +420,9 @@
   GURL HttpLitePageURL(PreviewsServerAction action,
                        std::string* headers = nullptr,
                        int delay_ms = 0) const {
-    std::string query = "resp=" + base::IntToString(action);
+    std::string query = "resp=" + base::NumberToString(action);
     if (delay_ms != 0)
-      query += "&delay_ms=" + base::IntToString(delay_ms);
+      query += "&delay_ms=" + base::NumberToString(delay_ms);
     if (headers)
       query += "&headers=" + *headers;
     GURL::Replacements replacements;
@@ -437,9 +437,9 @@
   GURL HttpsLitePageURL(PreviewsServerAction action,
                         std::string* headers = nullptr,
                         int delay_ms = 0) const {
-    std::string query = "resp=" + base::IntToString(action);
+    std::string query = "resp=" + base::NumberToString(action);
     if (delay_ms != 0)
-      query += "&delay_ms=" + base::IntToString(delay_ms);
+      query += "&delay_ms=" + base::NumberToString(delay_ms);
     if (headers)
       query += "&headers=" + *headers;
     GURL::Replacements replacements;
@@ -580,7 +580,7 @@
 
     GURL subresource_url(
         "https://foo.litepages.googlezip.net:" +
-        base::IntToString(previews_server().EffectiveIntPort()) +
+        base::NumberToString(previews_server().EffectiveIntPort()) +
         "/subresource.png");
     std::string subresource_body = "<html><body><img src=\"" +
                                    subresource_url.spec() +
diff --git a/chrome/browser/previews/previews_lite_page_decider_unittest.cc b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
index 0bde54a..e34c91de 100644
--- a/chrome/browser/previews/previews_lite_page_decider_unittest.cc
+++ b/chrome/browser/previews/previews_lite_page_decider_unittest.cc
@@ -64,7 +64,7 @@
   manager->BlacklistBypassedHost(kHost, kOneDay);
   EXPECT_TRUE(manager->HostBlacklistedFromBypass(kHost));
   for (int i = 1; i <= kBlacklistDurationDays; i++) {
-    manager->BlacklistBypassedHost(kHost + base::IntToString(i),
+    manager->BlacklistBypassedHost(kHost + base::NumberToString(i),
                                    kOneDay + base::TimeDelta::FromSeconds(i));
   }
   EXPECT_FALSE(manager->HostBlacklistedFromBypass(kHost));
@@ -74,7 +74,7 @@
   manager->BlacklistBypassedHost(kHost, kOneDay);
   EXPECT_TRUE(manager->HostBlacklistedFromBypass(kHost));
   for (int i = 1; i <= kBlacklistDurationDays - 1; i++) {
-    manager->BlacklistBypassedHost(kHost + base::IntToString(i),
+    manager->BlacklistBypassedHost(kHost + base::NumberToString(i),
                                    kOneDay + base::TimeDelta::FromSeconds(i));
   }
   manager->BlacklistBypassedHost(kOtherHost, kYesterday);
diff --git a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
index 5beb187..e970572e 100644
--- a/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
+++ b/chrome/browser/previews/previews_lite_page_navigation_throttle.cc
@@ -406,7 +406,7 @@
   std::string origin_hash = base::ToLowerASCII(base32::Base32Encode(
       crypto::SHA256HashString(
           original_url.scheme() + "://" + original_url.host() + ":" +
-          base::IntToString(original_url.EffectiveIntPort())),
+          base::NumberToString(original_url.EffectiveIntPort())),
       base32::Base32EncodePolicy::OMIT_PADDING));
   GURL previews_host = previews::params::GetLitePagePreviewsDomainURL();
   GURL previews_url = GURL(
diff --git a/chrome/browser/previews/previews_ui_tab_helper.cc b/chrome/browser/previews/previews_ui_tab_helper.cc
index a1a0f9b..49bbee5 100644
--- a/chrome/browser/previews/previews_ui_tab_helper.cc
+++ b/chrome/browser/previews/previews_ui_tab_helper.cc
@@ -210,13 +210,13 @@
     DCHECK_GE(staleness_in_minutes, 2);
     return l10n_util::GetStringFUTF16(
         IDS_PREVIEWS_INFOBAR_TIMESTAMP_MINUTES,
-        base::IntToString16(staleness_in_minutes));
+        base::NumberToString16(staleness_in_minutes));
   } else if (staleness_in_minutes < 120) {
     return l10n_util::GetStringUTF16(IDS_PREVIEWS_INFOBAR_TIMESTAMP_ONE_HOUR);
   } else {
     return l10n_util::GetStringFUTF16(
         IDS_PREVIEWS_INFOBAR_TIMESTAMP_HOURS,
-        base::IntToString16(staleness_in_minutes / 60));
+        base::NumberToString16(staleness_in_minutes / 60));
   }
 }
 
diff --git a/chrome/browser/printing/cloud_print/privet_http_impl.cc b/chrome/browser/printing/cloud_print/privet_http_impl.cc
index 57cb9dc4..0114bbf9 100644
--- a/chrome/browser/printing/cloud_print/privet_http_impl.cc
+++ b/chrome/browser/printing/cloud_print/privet_http_impl.cc
@@ -703,7 +703,7 @@
   GURL::Replacements replacements;
   std::string host = host_port_.HostForURL();
   replacements.SetHostStr(host);
-  std::string port = base::UintToString(host_port_.port());
+  std::string port = base::NumberToString(host_port_.port());
   replacements.SetPortStr(port);
 
   net::NetworkTrafficAnnotationTag traffic_annotation =
diff --git a/chrome/browser/process_singleton_posix.cc b/chrome/browser/process_singleton_posix.cc
index 5ae6482..349cb00 100644
--- a/chrome/browser/process_singleton_posix.cc
+++ b/chrome/browser/process_singleton_posix.cc
@@ -317,8 +317,7 @@
                               const std::string& hostname,
                               int pid) {
   base::string16 error = l10n_util::GetStringFUTF16(
-      IDS_PROFILE_IN_USE_POSIX,
-      base::IntToString16(pid),
+      IDS_PROFILE_IN_USE_POSIX, base::NumberToString16(pid),
       base::ASCIIToUTF16(hostname));
   LOG(ERROR) << error;
 
diff --git a/chrome/browser/profiles/incognito_mode_policy_handler.cc b/chrome/browser/profiles/incognito_mode_policy_handler.cc
index d95db82..7fa2b3f 100644
--- a/chrome/browser/profiles/incognito_mode_policy_handler.cc
+++ b/chrome/browser/profiles/incognito_mode_policy_handler.cc
@@ -37,7 +37,7 @@
                                                &availability_enum_value)) {
       errors->AddError(key::kIncognitoModeAvailability,
                        IDS_POLICY_OUT_OF_RANGE_ERROR,
-                       base::IntToString(int_value));
+                       base::NumberToString(int_value));
       return false;
     }
     return true;
diff --git a/chrome/browser/profiles/profile_attributes_storage.cc b/chrome/browser/profiles/profile_attributes_storage.cc
index d22716cb..44b3964 100644
--- a/chrome/browser/profiles/profile_attributes_storage.cc
+++ b/chrome/browser/profiles/profile_attributes_storage.cc
@@ -213,7 +213,7 @@
     // it uses sscanf.
     // TODO(jshin): fix IsDefaultProfileName to handle native digits.
     name = l10n_util::GetStringFUTF16(IDS_NEW_NUMBERED_PROFILE_NAME,
-                                      base::IntToString16(name_index));
+                                      base::NumberToString16(name_index));
 #else
     if (icon_index < profiles::GetGenericAvatarIconCount()) {
       name = l10n_util::GetStringFUTF16Int(IDS_NUMBERED_PROFILE_NAME,
diff --git a/chrome/browser/profiles/profile_io_data.cc b/chrome/browser/profiles/profile_io_data.cc
index dedc927d..3733e33 100644
--- a/chrome/browser/profiles/profile_io_data.cc
+++ b/chrome/browser/profiles/profile_io_data.cc
@@ -161,9 +161,9 @@
 #include "net/ssl/client_cert_store_mac.h"
 #endif  // defined(OS_MACOSX)
 
-#if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_MACOSX)
-#include "chrome/browser/net/trial_comparison_cert_verifier.h"
+#if BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
 #include "net/cert/cert_verify_proc_builtin.h"
+#include "services/network/trial_comparison_cert_verifier_mojo.h"
 #endif
 
 using content::BrowserContext;
@@ -982,16 +982,24 @@
             std::make_unique<net::MultiThreadedCertVerifier>(
                 verify_proc.get()));
       }
-#elif defined(OS_LINUX) || defined(OS_MACOSX)
-      cert_verifier = std::make_unique<net::CachingCertVerifier>(
-          std::make_unique<TrialComparisonCertVerifier>(
-              profile_params_->profile, net::CertVerifyProc::CreateDefault(),
-              net::CreateCertVerifyProcBuiltin()));
-#else
-      cert_verifier = std::make_unique<net::CachingCertVerifier>(
-          std::make_unique<net::MultiThreadedCertVerifier>(
-              net::CertVerifyProc::CreateDefault()));
+#elif BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
+      auto& trial_params = profile_params_->main_network_context_params
+                               ->trial_comparison_cert_verifier_params;
+      if (trial_params) {
+        cert_verifier = std::make_unique<net::CachingCertVerifier>(
+            std::make_unique<network::TrialComparisonCertVerifierMojo>(
+                trial_params->initial_allowed,
+                std::move(trial_params->config_client_request),
+                std::move(trial_params->report_client),
+                net::CertVerifyProc::CreateDefault(),
+                net::CreateCertVerifyProcBuiltin()));
+      }
 #endif
+      if (!cert_verifier) {
+        cert_verifier = std::make_unique<net::CachingCertVerifier>(
+            std::make_unique<net::MultiThreadedCertVerifier>(
+                net::CertVerifyProc::CreateDefault()));
+      }
       const base::CommandLine& command_line =
           *base::CommandLine::ForCurrentProcess();
       cert_verifier = network::IgnoreErrorsCertVerifier::MaybeWrapCertVerifier(
diff --git a/chrome/browser/profiles/profile_manager.cc b/chrome/browser/profiles/profile_manager.cc
index be3d8ff4..5dda405 100644
--- a/chrome/browser/profiles/profile_manager.cc
+++ b/chrome/browser/profiles/profile_manager.cc
@@ -793,7 +793,7 @@
   // Create the next profile in the next available directory slot.
   int next_directory = local_state->GetInteger(prefs::kProfilesNumCreated);
   std::string profile_name = chrome::kMultiProfileDirPrefix;
-  profile_name.append(base::IntToString(next_directory));
+  profile_name.append(base::NumberToString(next_directory));
   base::FilePath new_path = user_data_dir_;
 #if defined(OS_WIN)
   new_path = new_path.Append(base::ASCIIToUTF16(profile_name));
diff --git a/chrome/browser/profiling_host/memlog_browsertest.cc b/chrome/browser/profiling_host/memlog_browsertest.cc
index a4a9bd9c..00a2008 100644
--- a/chrome/browser/profiling_host/memlog_browsertest.cc
+++ b/chrome/browser/profiling_host/memlog_browsertest.cc
@@ -55,6 +55,9 @@
         NOTREACHED();
       }
 
+      if (!GetParam().stream_samples)
+        command_line->AppendSwitch(heap_profiling::kMemlogInProcess);
+
       if (!GetParam().should_sample) {
         command_line->AppendSwitchASCII(heap_profiling::kMemlogSamplingRate,
                                         "1");
diff --git a/chrome/browser/push_messaging/push_messaging_app_identifier.cc b/chrome/browser/push_messaging/push_messaging_app_identifier.cc
index 94f3428..8a3fae2 100644
--- a/chrome/browser/push_messaging/push_messaging_app_identifier.cc
+++ b/chrome/browser/push_messaging/push_messaging_app_identifier.cc
@@ -33,7 +33,7 @@
 std::string MakePrefValue(const GURL& origin,
                           int64_t service_worker_registration_id) {
   return origin.spec() + kSeparator +
-         base::Int64ToString(service_worker_registration_id);
+         base::NumberToString(service_worker_registration_id);
 }
 
 bool GetOriginAndSWRFromPrefValue(const std::string& pref_value,
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.cc b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
index 7b62eb9..0375a66 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.cc
@@ -320,7 +320,7 @@
     {63, -1, IDC_WRITING_DIRECTION_DEFAULT},
     {64, -1, IDC_WRITING_DIRECTION_LTR},
     {65, -1, IDC_WRITING_DIRECTION_RTL},
-    {66, -1, IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE},
+    {66, -1, IDC_CONTENT_CONTEXT_LOAD_IMAGE},
     {68, -1, IDC_ROUTE_MEDIA},
     {69, -1, IDC_CONTENT_CONTEXT_COPYLINKTEXT},
     {70, -1, IDC_CONTENT_CONTEXT_OPENLINKINPROFILE},
@@ -1237,11 +1237,13 @@
   std::map<std::string, std::string>::const_iterator it =
       params_.properties.find(
           data_reduction_proxy::chrome_proxy_content_transform_header());
-  if (it != params_.properties.end() && it->second ==
-      data_reduction_proxy::empty_image_directive()) {
-    menu_model_.AddItemWithStringId(
-        IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE,
-        IDS_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE);
+  if ((it != params_.properties.end() &&
+       it->second == data_reduction_proxy::empty_image_directive()) ||
+      (!params_.has_image_contents &&
+       base::FeatureList::IsEnabled(
+           features::kLoadBrokenImagesFromContextMenu))) {
+    menu_model_.AddItemWithStringId(IDC_CONTENT_CONTEXT_LOAD_IMAGE,
+                                    IDS_CONTENT_CONTEXT_LOAD_IMAGE);
   }
   DataReductionProxyChromeSettings* settings =
       DataReductionProxyChromeSettingsFactory::GetForBrowserContext(
@@ -1730,7 +1732,7 @@
     // The images shown in the most visited thumbnails can't be opened or
     // searched for conventionally.
     case IDC_CONTENT_CONTEXT_OPEN_ORIGINAL_IMAGE_NEW_TAB:
-    case IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE:
+    case IDC_CONTENT_CONTEXT_LOAD_IMAGE:
     case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
     case IDC_CONTENT_CONTEXT_SEARCHWEBFORIMAGE:
       return params_.src_url.is_valid() &&
@@ -1983,8 +1985,8 @@
           data_reduction_proxy::chrome_proxy_pass_through_header(), false);
       break;
 
-    case IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE:
-      ExecLoadOriginalImage();
+    case IDC_CONTENT_CONTEXT_LOAD_IMAGE:
+      ExecLoadImage();
       break;
 
     case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB:
@@ -2585,7 +2587,7 @@
   core_tab_helper->SearchByImageInNewTab(render_frame_host, params().src_url);
 }
 
-void RenderViewContextMenu::ExecLoadOriginalImage() {
+void RenderViewContextMenu::ExecLoadImage() {
   RenderFrameHost* render_frame_host = GetRenderFrameHost();
   if (!render_frame_host)
     return;
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu.h b/chrome/browser/renderer_context_menu/render_view_context_menu.h
index 75f7e88..0b20ef40 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu.h
@@ -211,7 +211,7 @@
   void ExecCopyLinkText();
   void ExecCopyImageAt();
   void ExecSearchWebForImage();
-  void ExecLoadOriginalImage();
+  void ExecLoadImage();
   void ExecPlayPause();
   void ExecMute();
   void ExecLoop();
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
index 1c195de..dd66153 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest.cc
@@ -72,6 +72,8 @@
 #include "media/base/media_switches.h"
 #include "net/base/load_flags.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 #include "third_party/blink/public/platform/web_input_event.h"
@@ -1202,8 +1204,11 @@
       content::RenderFrameHost* render_frame_host,
       const content::GlobalRequestID& request_id,
       const content::mojom::ResourceLoadInfo& resource_load_info) override {
-    if (resource_load_info.url.path() == path_)
+    if (resource_load_info.url.path() == path_) {
+      ASSERT_GT(resource_load_info.raw_body_bytes, 0);
+      ASSERT_EQ(resource_load_info.mime_type, "image/png");
       run_loop_.Quit();
+    }
   }
 
   void WaitForRequest() { run_loop_.Run(); }
@@ -1215,42 +1220,74 @@
 
 class LoadImageBrowserTest : public InProcessBrowserTest {
  protected:
-  void SetupAndLoadImagePage(const std::string& image_path) {
+  void SetupAndLoadImagePage(const std::string& page_path,
+                             const std::string& image_path) {
+    image_path_ = image_path;
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kLoadBrokenImagesFromContextMenu);
+
+    embedded_test_server()->RegisterRequestHandler(base::BindRepeating(
+        &LoadImageBrowserTest::HandleRequest, base::Unretained(this)));
     ASSERT_TRUE(embedded_test_server()->Start());
-    // Go to a page with an image in it. The test server doesn't serve the image
-    // with the right MIME type, so use a data URL to make a page containing it.
-    GURL image_url(embedded_test_server()->GetURL(image_path));
-    GURL page(
-        "data:text/html,<img width=50 height=50 "
-        "src='" +
-        image_url.spec() + "'>");
-    ui_test_utils::NavigateToURL(browser(), page);
+
+    // Go to a page with an image in it
+    GURL page_url(embedded_test_server()->GetURL(page_path));
+    ui_test_utils::NavigateToURL(browser(), page_url);
   }
 
   void AttemptLoadImage() {
-    // Right-click where the image should be.
-    // |menu_observer_| will cause the "Load image" menu item to be clicked.
-    menu_observer_ = std::make_unique<ContextMenuNotificationObserver>(
-        IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE);
-    content::WebContents* tab =
+    WebContents* web_contents =
         browser()->tab_strip_model()->GetActiveWebContents();
-    content::SimulateMouseClickAt(tab, 0, blink::WebMouseEvent::Button::kRight,
+
+    LoadImageRequestObserver request_observer(web_contents, image_path_);
+
+    // Simulate right click and invoke load image command.
+    ContextMenuWaiter menu_observer(IDC_CONTENT_CONTEXT_LOAD_IMAGE);
+    content::SimulateMouseClickAt(web_contents, 0,
+                                  blink::WebMouseEvent::Button::kRight,
                                   gfx::Point(15, 15));
+    menu_observer.WaitForMenuOpenAndClose();
+
+    ASSERT_EQ(menu_observer.params().media_type,
+              blink::WebContextMenuData::kMediaTypeImage);
+    ASSERT_EQ(menu_observer.params().src_url.path(), image_path_);
+    ASSERT_FALSE(menu_observer.params().has_image_contents);
+
+    request_observer.WaitForRequest();
   }
 
  private:
-  std::unique_ptr<ContextMenuNotificationObserver> menu_observer_;
+  // Returns Not Found on first request for image, pass on to
+  // default handler on second, third, etc.
+  std::unique_ptr<net::test_server::HttpResponse> HandleRequest(
+      const net::test_server::HttpRequest& request) {
+    if (request.relative_url != image_path_)
+      return nullptr;
+
+    ++request_attempts_;
+    if (request_attempts_ > 1)
+      return nullptr;
+
+    std::unique_ptr<net::test_server::BasicHttpResponse> not_found_response =
+        std::make_unique<net::test_server::BasicHttpResponse>();
+    not_found_response->set_code(net::HTTP_NOT_FOUND);
+    return not_found_response;
+  }
+
+  std::string image_path_;
+  size_t request_attempts_ = 0u;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 IN_PROC_BROWSER_TEST_F(LoadImageBrowserTest, LoadImage) {
-  static const char kValidImage[] = "/load_image/image.png";
-  SetupAndLoadImagePage(kValidImage);
-
-  WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  LoadImageRequestObserver observer(web_contents, kValidImage);
+  SetupAndLoadImagePage("/load_image/image.html", "/load_image/image.png");
   AttemptLoadImage();
-  observer.WaitForRequest();
+}
+
+IN_PROC_BROWSER_TEST_F(LoadImageBrowserTest, LoadImageWithMap) {
+  SetupAndLoadImagePage("/load_image/image_with_map.html",
+                        "/load_image/image.png");
+  AttemptLoadImage();
 }
 
 IN_PROC_BROWSER_TEST_F(ContextMenuBrowserTest,
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.cc
index 67e7041..da2bb1f 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.cc
@@ -42,6 +42,11 @@
       base::BindOnce(&ContextMenuWaiter::MenuShown, base::Unretained(this)));
 }
 
+ContextMenuWaiter::ContextMenuWaiter(int command_to_execute)
+    : ContextMenuWaiter() {
+  maybe_command_to_execute_ = command_to_execute;
+}
+
 ContextMenuWaiter::~ContextMenuWaiter() {
 }
 
@@ -61,6 +66,8 @@
 
 void ContextMenuWaiter::Cancel(RenderViewContextMenu* context_menu) {
   params_ = context_menu->params();
+  if (maybe_command_to_execute_)
+    context_menu->ExecuteCommand(*maybe_command_to_execute_, 0);
   context_menu->Cancel();
   run_loop_.Quit();
 }
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h
index 985f1367..2580b47d 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_browsertest_util.h
@@ -32,6 +32,7 @@
 class ContextMenuWaiter {
  public:
   ContextMenuWaiter();
+  explicit ContextMenuWaiter(int command_to_execute);
   ~ContextMenuWaiter();
 
   content::ContextMenuParams& params();
@@ -46,6 +47,7 @@
 
   content::ContextMenuParams params_;
   base::RunLoop run_loop_;
+  base::Optional<int> maybe_command_to_execute_;
 
   DISALLOW_COPY_AND_ASSIGN(ContextMenuWaiter);
 };
diff --git a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
index cab4e16b..1948d37 100644
--- a/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
+++ b/chrome/browser/renderer_context_menu/render_view_context_menu_unittest.cc
@@ -567,11 +567,26 @@
       web_contents()->GetMainFrame(), params);
   AppendImageItems(menu.get());
 
-  ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE));
+  ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_LOAD_IMAGE));
 
   DestroyDataReductionProxySettings();
 }
 
+// Check that if image is broken "Load image" menu item is present.
+TEST_F(RenderViewContextMenuPrefsTest, LoadBrokenImage) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      features::kLoadBrokenImagesFromContextMenu);
+  content::ContextMenuParams params = CreateParams(MenuItem::IMAGE);
+  params.unfiltered_link_url = params.link_url;
+  params.has_image_contents = false;
+  auto menu = std::make_unique<TestRenderViewContextMenu>(
+      web_contents()->GetMainFrame(), params);
+  AppendImageItems(menu.get());
+
+  ASSERT_TRUE(menu->IsItemPresent(IDC_CONTENT_CONTEXT_LOAD_IMAGE));
+}
+
 // Verify that the suggested file name is propagated to web contents when save a
 // media file in context menu.
 TEST_F(RenderViewContextMenuPrefsTest, SaveMediaSuggestedFileName) {
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 4df8a9e9..0edcc50 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -140,16 +140,13 @@
     int render_frame_id,
     const GURL& origin_url,
     const GURL& top_origin_url,
-    const base::string16& name,
-    const base::string16& display_name,
     bool* allowed) {
   *allowed =
       cookie_settings_->IsCookieAccessAllowed(origin_url, top_origin_url);
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
       base::Bind(&TabSpecificContentSettings::WebDatabaseAccessed,
-                 render_process_id_, render_frame_id, origin_url, name,
-                 display_name, !*allowed));
+                 render_process_id_, render_frame_id, origin_url, !*allowed));
 }
 
 void ChromeRenderMessageFilter::OnAllowDOMStorage(int render_frame_id,
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.h b/chrome/browser/renderer_host/chrome_render_message_filter.h
index 9872dcb..1b8b9c5 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.h
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.h
@@ -54,8 +54,6 @@
   void OnAllowDatabase(int render_frame_id,
                        const GURL& origin_url,
                        const GURL& top_origin_url,
-                       const base::string16& name,
-                       const base::string16& display_name,
                        bool* allowed);
   void OnAllowDOMStorage(int render_frame_id,
                          const GURL& origin_url,
diff --git a/chrome/browser/resource_coordinator/background_tab_navigation_throttle_unittest.cc b/chrome/browser/resource_coordinator/background_tab_navigation_throttle_unittest.cc
index 13e0f11..a979866 100644
--- a/chrome/browser/resource_coordinator/background_tab_navigation_throttle_unittest.cc
+++ b/chrome/browser/resource_coordinator/background_tab_navigation_throttle_unittest.cc
@@ -115,7 +115,7 @@
   EXPECT_EQ(expect_instantiation, throttle != nullptr);
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     InstantiateThrottle,
     BackgroundTabNavigationThrottleTest,
     ::testing::Values(std::make_tuple(EXPECT_INSTANTIATION,
diff --git a/chrome/browser/resource_coordinator/tab_manager.cc b/chrome/browser/resource_coordinator/tab_manager.cc
index c840b4b09..602629d7 100644
--- a/chrome/browser/resource_coordinator/tab_manager.cc
+++ b/chrome/browser/resource_coordinator/tab_manager.cc
@@ -233,7 +233,7 @@
   // MemoryCoordinator is disabled. When MemoryCoordinator is enabled
   // it asks TabManager to do tab discarding.
   base::MemoryPressureMonitor* monitor = base::MemoryPressureMonitor::Get();
-  if (monitor && !base::FeatureList::IsEnabled(features::kMemoryCoordinator)) {
+  if (monitor) {
     memory_pressure_listener_.reset(new base::MemoryPressureListener(
         base::Bind(&TabManager::OnMemoryPressure, base::Unretained(this))));
     base::MemoryPressureListener::MemoryPressureLevel level =
diff --git a/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc b/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc
index 5849c448..2e954d1b 100644
--- a/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc
+++ b/chrome/browser/resource_coordinator/tab_manager_stats_collector_unittest.cc
@@ -464,7 +464,7 @@
                                                                            : 0);
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ,
     TabManagerStatsCollectorTabSwitchTest,
     ::testing::Values(std::make_pair(false,   // Session restore
@@ -472,7 +472,7 @@
                       std::make_pair(true, false),
                       std::make_pair(false, true),
                       std::make_pair(true, true)));
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ,
     TabManagerStatsCollectorParameterizedTest,
     ::testing::Values(std::make_pair(false,   // Session restore
diff --git a/chrome/browser/resources/chromeos/arc_support/background.js b/chrome/browser/resources/chromeos/arc_support/background.js
index 0d1f161fd..44e3c44 100644
--- a/chrome/browser/resources/chromeos/arc_support/background.js
+++ b/chrome/browser/resources/chromeos/arc_support/background.js
@@ -34,6 +34,12 @@
 var lastFocusedElement = null;
 
 /**
+ * Stores locale set for the current browser process.
+ * @type {string}
+ */
+var locale = null;
+
+/**
  * Host window outer default width.
  * @const {number}
  */
@@ -320,14 +326,15 @@
         this.onTermsViewRequestCompleted_.bind(this), requestFilter);
     this.countryCode = countryCode.toLowerCase();
 
-    var scriptSetCountryCode =
+    var scriptInitTermsView =
         'document.countryCode = \'' + this.countryCode + '\';';
-    scriptSetCountryCode += 'document.viewMode = \'large-view\';';
+    scriptInitTermsView += 'document.language = \'' + locale + '\';';
+    scriptInitTermsView += 'document.viewMode = \'large-view\';';
     this.termsView_.addContentScripts([
       {
         name: 'preProcess',
         matches: ['https://play.google.com/*'],
-        js: {code: scriptSetCountryCode},
+        js: {code: scriptInitTermsView},
         run_at: 'document_start'
       },
       {
@@ -441,7 +448,7 @@
       return;
     }
 
-    var defaultLocation = 'https://play.google.com/about/play-terms.html';
+    var defaultLocation = 'https://play.google.com/about/play-terms/';
     if (this.termsView_.src) {
       // This is reloading the page, typically clicked RETRY on error page.
       this.fastLocation_ = undefined;
@@ -455,7 +462,7 @@
       this.fastLocation_ = this.getFastLocation_();
       if (this.fastLocation_) {
         this.termsView_.src = 'https://play.google.com/intl/' +
-            this.fastLocation_ + '/about/play-terms.html';
+            this.fastLocation_ + '/about/play-terms/';
       } else {
         this.termsView_.src = defaultLocation;
       }
@@ -470,12 +477,12 @@
    * Returns undefined in case the fast location cannot be found.
    */
   getFastLocation_() {
-    var matchByLangZone = navigator.language + '_' + this.countryCode;
+    var matchByLangZone = locale + '_' + this.countryCode;
     if (PLAYSTORE_TOS_LOCALIZATIONS.indexOf(matchByLangZone) >= 0) {
       return matchByLangZone;
     }
 
-    var langSegments = navigator.language.split('-');
+    var langSegments = locale.split('-');
     if (langSegments.length == 2) {
       var matchByShortLangZone = langSegments[0] + '_' + this.countryCode;
       if (PLAYSTORE_TOS_LOCALIZATIONS.indexOf(matchByShortLangZone) >= 0) {
@@ -540,7 +547,7 @@
         // For fast location load make sure we have right terms displayed.
         this.fastLocation_ = undefined;
         var checkInitialLangZoneTerms = 'processLangZoneTerms(true, \'' +
-            navigator.language + '\', \'' + this.countryCode + '\');';
+            locale + '\', \'' + this.countryCode + '\');';
         var details = {code: checkInitialLangZoneTerms};
         termsPage.termsView_.executeScript(details, function(results) {});
       }
@@ -566,7 +573,7 @@
     // In case we failed with fast location let retry default scheme.
     if (this.fastLocation_) {
       this.fastLocation_ = undefined;
-      this.termsView_.src = 'https://play.google.com/about/play-terms.html';
+      this.termsView_.src = 'https://play.google.com/about/play-terms/';
       return;
     }
     this.onTermsViewLoadAborted_(
@@ -718,6 +725,7 @@
   var loadTimeData = appWindow.contentWindow.loadTimeData;
   loadTimeData.data = data;
   appWindow.contentWindow.i18nTemplate.process(doc, loadTimeData);
+  locale = loadTimeData.getString('locale');
 
   // Initialize preference connected checkboxes in terms of service page.
   termsPage = new TermsOfServicePage(
@@ -922,8 +930,8 @@
  * the content of terms view.
  */
 function showPrivacyPolicyOverlay() {
-  var defaultLink = 'https://www.google.com/intl/' + navigator.language +
-      '/policies/privacy/';
+  var defaultLink =
+      'https://www.google.com/intl/' + locale + '/policies/privacy/';
   if (termsPage.isManaged_) {
     showURLOverlay(defaultLink);
     return;
diff --git a/chrome/browser/resources/chromeos/arc_support/playstore.css b/chrome/browser/resources/chromeos/arc_support/playstore.css
index 42cf2cb6..04fdc7d 100644
--- a/chrome/browser/resources/chromeos/arc_support/playstore.css
+++ b/chrome/browser/resources/chromeos/arc_support/playstore.css
@@ -111,11 +111,12 @@
   padding: 0;
 }
 
-#play-footer ul {
+.play-footer ul,
+.play-header {
   display: none;
 }
 
-#play-footer {
+.play-footer {
   border: 0;
   margin: 0;
   padding: 0;
diff --git a/chrome/browser/resources/chromeos/arc_support/playstore.js b/chrome/browser/resources/chromeos/arc_support/playstore.js
index cfeb35d0..3335d977 100644
--- a/chrome/browser/resources/chromeos/arc_support/playstore.js
+++ b/chrome/browser/resources/chromeos/arc_support/playstore.js
@@ -3,33 +3,73 @@
 // found in the LICENSE file.
 
 /**
+ * Returns the Play Store footer element that can be detected by id or class
+ * name.
+ */
+function getPlayFooterElement() {
+  var elements = document.getElementsByClassName('play-footer');
+  if (!elements || elements.length == 0) {
+    console.error('Failed to find play-footer element in ToS.');
+    return null;
+  }
+  if (elements.length != 1) {
+    console.error('Found more than one play-footer element in ToS.');
+  }
+  return elements[0];
+}
+
+/**
+ * Returns the select element that controls zone/language selection.
+ */
+function getLangZoneSelect() {
+  var footer = getPlayFooterElement();
+  if (!footer) {
+    return null;
+  }
+
+  var elements = footer.getElementsByTagName('select');
+  if (!elements || elements.length == 0) {
+    console.error('Cannot find zone/language select select element');
+    return null;
+  }
+  if (elements.length != 1) {
+    console.error('Found more than one zone/language select element in ToS.');
+  }
+  return elements[0];
+}
+
+
+/**
  * Analyzes current document and tries to find the link to the Play Store ToS
  * that matches requested |language| and |countryCode|. Once found, navigate
  * to this link and returns True. If no match was found then returns False.
  */
 function navigateToLanguageAndCountryCode(language, countryCode) {
-  var doc = document;
-  var selectLangZoneTerms =
-      doc.getElementById('play-footer').getElementsByTagName('select')[0];
+  var selectLangZoneTerms = getLangZoneSelect();
+
+  if (!selectLangZoneTerms) {
+    // Layout is not recognized, cannot check document structure.
+    return false;
+  }
 
   var applyTermsForLangAndZone = function(termsLang) {
     // Check special case for en_us which may be mapped to en.
     var matchDefaultUs = null;
-    if (window.location.href ==
-            'https://play.google.com/intl/en_us/about/play-terms.html' &&
+    if (window.location.href.startsWith(
+            'https://play.google.com/intl/en_us/about/play-terms') &&
         termsLang == 'en' && countryCode == 'us' &&
-        selectLangZoneTerms.value == '/intl/en/about/play-terms.html') {
+        selectLangZoneTerms.value.startsWith('/intl/en/about/play-terms')) {
       return true;
     }
     var matchByLangZone =
-        '/intl/' + termsLang + '_' + countryCode + '/about/play-terms.html';
-    if (selectLangZoneTerms.value == matchByLangZone) {
+        '/intl/' + termsLang + '_' + countryCode + '/about/play-terms';
+    if (selectLangZoneTerms.value.startsWith(matchByLangZone)) {
       // Already selected what is needed.
       return true;
     }
     for (var i = selectLangZoneTerms.options.length - 1; i >= 0; --i) {
       var option = selectLangZoneTerms.options[i];
-      if (option.value == matchByLangZone) {
+      if (option.value.startsWith(matchByLangZone)) {
         window.location.href = option.value;
         return true;
       }
@@ -68,23 +108,33 @@
     return true;
   }
 
+  var footer = getPlayFooterElement();
+  if (!footer) {
+    // Layout is not recognized, show content and stop processing.
+    document.body.hidden = false;
+    return true;
+  }
+
   var matchByLang = '/intl/' + language + '_';
   var matchByLangShort = null;
   if (langSegments.length == 2) {
     matchByLangShort = '/intl/' + langSegments[0] + '_';
   }
 
-  var matchByZone = '_' + countryCode + '/about/play-terms.html';
-  var matchByDefault = '/intl/en/about/play-terms.html';
+  var matchByZone = '_' + countryCode + '/about/play-terms';
+  var matchByDefault = '/intl/en/about/play-terms';
 
   // We are allowed to display terms by default only in language that matches
   // current UI language. In other cases we have to switch to default version.
   var langMatch = false;
   var defaultExist = false;
 
-  var doc = document;
-  var selectLangZoneTerms =
-      doc.getElementById('play-footer').getElementsByTagName('select')[0];
+  var selectLangZoneTerms = getLangZoneSelect();
+  if (!selectLangZoneTerms) {
+    document.body.hidden = false;
+    return;
+  }
+
   for (var i = selectLangZoneTerms.options.length - 1; i >= 0; --i) {
     var option = selectLangZoneTerms.options[i];
     if (selectLangZoneTerms.selectedIndex == i) {
@@ -92,13 +142,13 @@
           (matchByLangShort && option.value.startsWith(matchByLangShort));
       continue;
     }
-    if (option.value == matchByDefault) {
+    if (option.value.startsWith(matchByDefault)) {
       defaultExist = true;
       continue;
     }
 
     option.hidden = !option.value.startsWith(matchByLang) &&
-        !option.value.endsWith(matchByZone) &&
+        !option.value.includes(matchByZone) &&
         !(matchByLangShort && option.value.startsWith(matchByLangShort)) &&
         option.text != 'English';
   }
@@ -140,11 +190,6 @@
   base.target = '_blank';
   document.head.appendChild(base);
 
-  // Remove header element that contains logo image we don't want to show in
-  // our view.
-  var doc = document;
-  document.body.removeChild(doc.getElementById('play-header'));
-
   // Hide content at this point. We might want to redirect our view to terms
   // that exactly match current language and country code.
   document.body.hidden = true;
@@ -156,12 +201,14 @@
  *                  document or link to the default policy if it is not found.
  */
 function getPrivacyPolicyLink() {
-  var doc = document;
-  var links = doc.getElementById('play-footer').getElementsByTagName('a');
-  for (var i = 0; i < links.length; ++i) {
-    var targetURL = links[i].href;
-    if (targetURL.endsWith('/policies/privacy/')) {
-      return targetURL;
+  var footer = getPlayFooterElement();
+  if (footer) {
+    var links = footer.getElementsByTagName('a');
+    for (var i = 0; i < links.length; ++i) {
+      var targetURL = links[i].href;
+      if (targetURL.endsWith('/policies/privacy/')) {
+        return targetURL;
+      }
     }
   }
   return 'https://www.google.com/policies/privacy/';
@@ -178,8 +225,8 @@
   }
   formatDocument();
 
-  var initialLoad =
-      window.location.href == 'https://play.google.com/about/play-terms.html';
+  var initialLoad = window.location.href.startsWith(
+      'https://play.google.com/about/play-terms');
   var language = document.language;
   if (!language) {
     language = navigator.language;
diff --git a/chrome/browser/resources/chromeos/login/header_bar.css b/chrome/browser/resources/chromeos/login/header_bar.css
deleted file mode 100644
index 2b5b356..0000000
--- a/chrome/browser/resources/chromeos/login/header_bar.css
+++ /dev/null
@@ -1,178 +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. */
-
-#login-header-bar {
-  bottom: 0;
-  left: 0;
-  min-height: 34px;  /* Should be consistent with .header-bar-item's height. */
-  padding-bottom: 6px;
-  padding-inline-start: 15px;
-  padding-top: 7px;
-  position: absolute;
-  right: 0;
-}
-
-#login-header-bar.full-header-background {
-  background-color: #000;
-}
-
-html[screen=lock] .login-header-bar-hidden,
-html[screen=oobe] .login-header-bar-hidden {
-  opacity: 0;
-}
-
-html[screen=lock] .login-header-bar-animate-fast,
-html[screen=oobe] .login-header-bar-animate-fast {
-  transition: opacity 200ms ease-out;
-}
-
-html[screen=lock] .login-header-bar-animate-slow,
-html[screen=oobe] .login-header-bar-animate-slow {
-  transition: opacity 2s ease-out;
-}
-
-#login-header-bar button,
-#login-header-bar button:active,
-#login-header-bar button:focus,
-#login-header-bar button:hover {
-  background: transparent none;
-  box-shadow: none;
-  cursor: pointer;
-  height: 34px;
-  margin: 0;
-  min-width: 0;
-  opacity: 0.6;
-  padding: 5px 8px;
-  vertical-align: middle;
-}
-
-#login-header-bar button:not(.button-restricted),
-#login-header-bar button:active:not(.button-restricted),
-#login-header-bar button:focus:not(.button-restricted),
-#login-header-bar button:hover:not(.button-restricted) {
-  color: white !important;
-  opacity: 1 !important;
-}
-
-.header-bar-item {
-  height: 34px;
-}
-
-.add-supervised-user-menu {
-  display: none;
-}
-
-#more-settings-header-bar-item.active button.add-supervised-user-menu {
-  background-color: white;
-  border: 1px solid lightgray;
-  border-radius: 2px;
-  bottom: 0;
-  color: black !important;
-  display: block;
-  font-size: 13px;
-  height: auto;
-  min-height: 34px;
-  padding: 0 16px;
-  position: absolute;
-  text-align: center;
-  white-space: nowrap;
-}
-
-#more-settings-header-bar-item.active button.add-supervised-user-menu:focus {
-  border: 2px solid var(--google-blue-500);
-  border-radius: 2px;
-  padding: 0 15px;
-}
-
-html[dir=rtl] .header-bar-item {
-  background-position: right center;
-}
-
-#login-header-bar #shutdown-button-text,
-#login-header-bar #restart-button-text,
-#login-header-bar #add-user-button-text,
-#login-header-bar #guest-user-button-text,
-#login-header-bar #more-settings-button-text,
-#login-header-bar #cancel-multiple-sign-in-button-text,
-#login-header-bar #sign-out-user-button-text,
-#login-header-bar #unlock-user-button-text {
-  background-position: left center;
-  background-repeat: no-repeat;
-  background-size: 20px;
-  padding-inline-start: 28px;
-}
-
-html[dir=rtl] #login-header-bar #shutdown-button-text,
-html[dir=rtl] #login-header-bar #restart-button-text,
-html[dir=rtl] #login-header-bar #add-user-button-text,
-html[dir=rtl] #login-header-bar #more-settings-button-text,
-html[dir=rtl] #login-header-bar #guest-user-button-text,
-html[dir=rtl] #login-header-bar #cancel-multiple-sign-in-button-text,
-html[dir=rtl] #login-header-bar #sign-out-user-button-text,
-html[dir=rtl] #login-header-bar #unlock-user-button-text {
-  background-position: right center;
-}
-
-#login-header-bar #shutdown-button,
-#login-header-bar #restart-button,
-#login-header-bar #add-user-button,
-#login-header-bar #guest-user-button,
-#login-header-bar #more-settings-button,
-#login-header-bar #cancel-multiple-sign-in-button,
-#login-header-bar #unlock-user-button {
-  padding: 0 14px;
-}
-
-#login-header-bar #more-settings-header-bar-item {
-  position: relative;
-}
-
-#login-header-bar #shutdown-button-text,
-#login-header-bar #restart-button-text {
-  background-image: -webkit-image-set(
-      url(images/1x/shutdown.svg) 1x,
-      url(images/2x/shutdown.svg) 2x );
-}
-
-#login-header-bar #add-user-button-text {
-  background-image: -webkit-image-set(
-      url(images/1x/add-person.svg) 1x,
-      url(images/2x/add-person.svg) 2x );
-}
-
-#login-header-bar #more-settings-button-text {
-  background-image: -webkit-image-set(
-      url(images/1x/more-settings.svg) 1x,
-      url(images/2x/more-settings.svg) 2x );
-}
-
-#login-header-bar #guest-user-button-text {
-  background-image: -webkit-image-set(
-      url(images/1x/browse-as-guest.svg) 1x,
-      url(images/2x/browse-as-guest.svg) 2x );
-}
-
-#login-header-bar #cancel-multiple-sign-in-button-text {
-    background-image: -webkit-image-set(
-      url(images/1x/cancel.svg) 1x,
-      url(images/2x/cancel.svg) 2x );
-}
-
-#login-header-bar #sign-out-user-button-text {
-  background-image: -webkit-image-set(
-      url(images/1x/signout.svg) 1x,
-      url(images/2x/signout.svg) 2x );
-}
-
-#login-header-bar #unlock-user-button-text {
-  background-image: -webkit-image-set(
-      url(images/1x/unlock.svg) 1x,
-      url(images/2x/unlock.svg) 2x );
-}
-
-.button-restricted {
-  border: 1px solid transparent;
-  color: white !important;
-  opacity: 0.4 !important;
-}
diff --git a/chrome/browser/resources/chromeos/login/header_bar.html b/chrome/browser/resources/chromeos/login/header_bar.html
deleted file mode 100644
index 61ecee3..0000000
--- a/chrome/browser/resources/chromeos/login/header_bar.html
+++ /dev/null
@@ -1,66 +0,0 @@
-<link rel="stylesheet" href="oobe_flex_layout.css">
-<div id="login-header-bar" hidden
-    class="login-header-bar-hidden layout horizontal">
-  <div id="shutdown-header-bar-item" class="header-bar-item">
-    <button id="shutdown-button" class="custom-appearance layout vertical">
-      <div id="shutdown-button-text" class="flex layout horizontal center"
-          i18n-content="shutDown">
-      </div>
-    </button>
-  </div>
-  <div id="restart-header-bar-item" class="header-bar-item">
-    <button id="restart-button" class="custom-appearance layout vertical">
-      <div id="restart-button-text" class="flex layout horizontal center"
-          i18n-content="restart">
-      </div>
-    </button>
-  </div>
-  <div id="apps-header-bar-item" class="header-bar-item" hidden>
-    <button id="show-apps-button" class="custom-appearance"
-        i18n-content="showApps">
-    </button>
-  </div>
-  <div id="guest-user-header-bar-item" class="header-bar-item" hidden>
-    <button id="guest-user-button" class="custom-appearance layout vertical">
-      <div id="guest-user-button-text" class="flex layout horizontal center"
-          i18n-content="browseAsGuest">
-      </div>
-    </button>
-  </div>
-  <div id="add-user-header-bar-item" class="header-bar-item" hidden>
-    <button id="add-user-button" class="custom-appearance layout vertical">
-      <div id="add-user-button-text" class="flex layout horizontal center"
-          i18n-content="addUser">
-      </div>
-    </button>
-  </div>
-  <div id="more-settings-header-bar-item" class="header-bar-item">
-    <button id="more-settings-button" class="custom-appearance layout vertical">
-      <div id="more-settings-button-text" class="flex layout horizontal center"
-          i18n-values="aria-label:moreOptions">
-      </div>
-    </button>
-  </div>
-  <div id="sign-out-user-item" class="header-bar-item" hidden>
-    <button id="sign-out-user-button" class="custom-appearance layout vertical">
-      <div id="sign-out-user-button-text" class="flex layout horizontal center"
-          i18n-content="signOutUser">
-      </div>
-    </button>
-  </div>
-  <div id="cancel-multiple-sign-in-item" class="header-bar-item" hidden>
-    <button id="cancel-multiple-sign-in-button"
-        class="custom-appearance layout vertical">
-      <div id="cancel-multiple-sign-in-button-text"
-          class="flex layout horizontal center" i18n-content="cancel">
-      </div>
-    </button>
-  </div>
-  <div id="unlock-user-header-bar-item" class="header-bar-item" hidden>
-    <button id="unlock-user-button" class="custom-appearance layout vertical">
-      <div id="unlock-user-button-text" class="flex layout horizontal center"
-          i18n-content="unlockUser">
-      </div>
-    </button>
-  </div>
-</div>
diff --git a/chrome/browser/resources/chromeos/login/header_bar.js b/chrome/browser/resources/chromeos/login/header_bar.js
deleted file mode 100644
index 0dae943..0000000
--- a/chrome/browser/resources/chromeos/login/header_bar.js
+++ /dev/null
@@ -1,395 +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.
-
-/**
- * @fileoverview Login UI header bar implementation.
- */
-
-cr.define('login', function() {
-  /**
-   * Creates a header bar element.
-   *
-   * @constructor
-   * @extends {HTMLDivElement}
-   */
-  var HeaderBar = cr.ui.define('div');
-
-  HeaderBar.prototype = {
-    __proto__: HTMLDivElement.prototype,
-
-    // Whether guest button should be shown when header bar is in normal mode.
-    showGuest_: false,
-
-    // Whether the reboot button should be shown the when header bar is in
-    // normal mode.
-    showReboot_: false,
-
-    // Whether the shutdown button should be shown when the header bar is in
-    // normal mode.
-    showShutdown_: true,
-
-    // Current UI state of the sign-in screen.
-    signinUIState_: SIGNIN_UI_STATE.HIDDEN,
-
-    // Current lock screen apps activity state. This value affects visibility of
-    // tray buttons visible in the header bar - when lock screeen apps state is
-    // FOREGROUND, only the unlock button should be shown (when clicked, the
-    // button issues a request to move lock screen apps to background, in the
-    // state where account picker is visible).
-    lockScreenAppsState_: LOCK_SCREEN_APPS_STATE.NONE,
-
-    // Whether to show kiosk apps menu.
-    hasApps_: false,
-
-    /** @override */
-    decorate: function() {
-      document.addEventListener('click', this.handleClick_.bind(this));
-      $('shutdown-header-bar-item')
-          .addEventListener('click', this.handleShutdownClick_);
-      $('shutdown-button').addEventListener('click', this.handleShutdownClick_);
-      $('restart-header-bar-item')
-          .addEventListener('click', this.handleShutdownClick_);
-      $('restart-button').addEventListener('click', this.handleShutdownClick_);
-      $('add-user-button').addEventListener('click', this.handleAddUserClick_);
-      $('more-settings-button')
-          .addEventListener('click', this.handleMoreSettingsClick_.bind(this));
-      $('guest-user-header-bar-item')
-          .addEventListener('click', this.handleGuestClick_);
-      $('guest-user-button').addEventListener('click', this.handleGuestClick_);
-      $('sign-out-user-button')
-          .addEventListener('click', this.handleSignoutClick_);
-      $('cancel-multiple-sign-in-button')
-          .addEventListener('click', this.handleCancelMultipleSignInClick_);
-      $('unlock-user-button')
-          .addEventListener('click', this.handleUnlockUserClick_);
-      if (Oobe.getInstance().displayType == DISPLAY_TYPE.LOGIN ||
-          Oobe.getInstance().displayType == DISPLAY_TYPE.OOBE) {
-        if (Oobe.getInstance().newKioskUI)
-          chrome.send('initializeKioskApps');
-        else
-          login.AppsMenuButton.decorate($('show-apps-button'));
-      }
-      this.updateUI_();
-    },
-
-    /**
-     * Tab index value for all button elements.
-     *
-     * @type {number}
-     */
-    set buttonsTabIndex(tabIndex) {
-      var buttons = this.getElementsByTagName('button');
-      for (var i = 0, button; button = buttons[i]; ++i) {
-        button.tabIndex = tabIndex;
-      }
-    },
-
-    /**
-     * Disables the header bar and all of its elements.
-     *
-     * @type {boolean}
-     */
-    set disabled(value) {
-      var buttons = this.getElementsByTagName('button');
-      for (var i = 0, button; button = buttons[i]; ++i)
-        if (!button.classList.contains('button-restricted'))
-          button.disabled = value;
-    },
-
-    get getMoreSettingsMenu() {
-      return $('more-settings-header-bar-item');
-    },
-
-    /**
-     * Whether action box button is in active state.
-     * @type {boolean}
-     */
-    get isMoreSettingsActive() {
-      return this.getMoreSettingsMenu.classList.contains('active');
-    },
-    set isMoreSettingsActive(active) {
-      if (active == this.isMoreSettingsActive)
-        return;
-      this.getMoreSettingsMenu.classList.toggle('active', active);
-      $('more-settings-button').tabIndex = active ? -1 : 0;
-    },
-
-    /**
-     * Add user button click handler.
-     *
-     * @private
-     */
-    handleAddUserClick_: function(e) {
-      Oobe.showSigninUI();
-      // Prevent further propagation of click event. Otherwise, the click event
-      // handler of document object will set wallpaper to user's wallpaper when
-      // there is only one existing user. See http://crbug.com/166477
-      e.stopPropagation();
-    },
-
-    handleMoreSettingsClick_: function(e) {
-      this.isMoreSettingsActive = !this.isMoreSettingsActive;
-      e.stopPropagation();
-    },
-
-    handleClick_: function(e) {
-      this.isMoreSettingsActive = false;
-    },
-
-    /**
-     * Cancel add user button click handler.
-     *
-     * @private
-     */
-    handleCancelAddUserClick_: function(e) {
-      // Let screen handle cancel itself if that is capable of doing so.
-      if (Oobe.getInstance().currentScreen &&
-          Oobe.getInstance().currentScreen.cancel) {
-        Oobe.getInstance().currentScreen.cancel();
-        return;
-      }
-
-      Oobe.showUserPods();
-    },
-
-    /**
-     * Guest button click handler.
-     *
-     * @private
-     */
-    handleGuestClick_: function(e) {
-      Oobe.disableSigninUI();
-      chrome.send('launchIncognito');
-      e.stopPropagation();
-    },
-
-    /**
-     * Sign out button click handler.
-     *
-     * @private
-     */
-    handleSignoutClick_: function(e) {
-      this.disabled = true;
-      chrome.send('signOutUser');
-      e.stopPropagation();
-    },
-
-    /**
-     * Shutdown button click handler.
-     *
-     * @private
-     */
-    handleShutdownClick_: function(e) {
-      chrome.send('shutdownSystem');
-      e.stopPropagation();
-    },
-
-    /**
-     * Cancel user adding button handler.
-     *
-     * @private
-     */
-    handleCancelMultipleSignInClick_: function(e) {
-      chrome.send('cancelUserAdding');
-      e.stopPropagation();
-    },
-
-    /**
-     * Unlock user button handler. Sends a request to Chrome to show user pods
-     * in foreground.
-     *
-     * @private
-     */
-    handleUnlockUserClick_: function(e) {
-      chrome.send('closeLockScreenApp');
-      e.preventDefault();
-    },
-
-    /**
-     * If true then "Browse as Guest" button is shown.
-     *
-     * @type {boolean}
-     */
-    set showGuestButton(value) {
-      this.showGuest_ = value;
-      this.updateUI_();
-    },
-
-    /**
-     * If true the "Restart" button is shown.
-     *
-     * @type {boolean}
-     */
-    set showRebootButton(value) {
-      this.showReboot_ = value;
-      this.updateUI_();
-    },
-
-    /**
-     * If true the "Shutdown" button is shown.
-     *
-     * @type {boolean}
-     */
-    set showShutdownButton(value) {
-      this.showShutdown_ = value;
-      this.updateUI_();
-    },
-
-    /**
-     * Current header bar UI / sign in state.
-     *
-     * @type {number} state Current state of the sign-in screen (see
-     *       SIGNIN_UI_STATE).
-     */
-    get signinUIState() {
-      return this.signinUIState_;
-    },
-    set signinUIState(state) {
-      this.signinUIState_ = state;
-      this.updateUI_();
-    },
-
-    /**
-     * Current activity state of lock screen app windows.
-     *
-     * @type {LOCK_SCREEN_APPS_STATE}
-     */
-    set lockScreenAppsState(state) {
-      this.lockScreenAppsState_ = state;
-      this.updateUI_();
-    },
-
-    /**
-     * Update whether there are kiosk apps.
-     *
-     * @type {boolean}
-     */
-    set hasApps(value) {
-      this.hasApps_ = value;
-      this.updateUI_();
-    },
-
-    /**
-     * Updates visibility state of action buttons.
-     *
-     * @private
-     */
-    updateUI_: function() {
-      var gaiaIsActive = (this.signinUIState_ == SIGNIN_UI_STATE.GAIA_SIGNIN);
-      var enrollmentIsActive =
-          (this.signinUIState_ == SIGNIN_UI_STATE.ENROLLMENT);
-      var accountPickerIsActive =
-          (this.signinUIState_ == SIGNIN_UI_STATE.ACCOUNT_PICKER);
-      var wrongHWIDWarningIsActive =
-          (this.signinUIState_ == SIGNIN_UI_STATE.WRONG_HWID_WARNING);
-      var isSamlPasswordConfirm =
-          (this.signinUIState_ == SIGNIN_UI_STATE.SAML_PASSWORD_CONFIRM);
-      var isPasswordChangedUI =
-          (this.signinUIState_ == SIGNIN_UI_STATE.PASSWORD_CHANGED);
-      var isMultiProfilesUI =
-          (Oobe.getInstance().displayType == DISPLAY_TYPE.USER_ADDING);
-      var isLockScreen = (Oobe.getInstance().displayType == DISPLAY_TYPE.LOCK);
-      var errorScreenIsActive = (this.signinUIState_ == SIGNIN_UI_STATE.ERROR);
-
-      $('add-user-button').hidden = !accountPickerIsActive ||
-          isMultiProfilesUI || isLockScreen || errorScreenIsActive;
-      $('more-settings-header-bar-item').hidden = true;
-      $('guest-user-header-bar-item').hidden = !this.showGuest_ ||
-          isLockScreen || wrongHWIDWarningIsActive || isSamlPasswordConfirm ||
-          isMultiProfilesUI || (gaiaIsActive && $('gaia-signin').closable) ||
-          (enrollmentIsActive && !$('oauth-enrollment').isAtTheBeginning()) ||
-          (gaiaIsActive && !$('gaia-signin').isAtTheBeginning());
-      $('restart-header-bar-item').hidden = !this.showReboot_ ||
-          this.lockScreenAppsState_ == LOCK_SCREEN_APPS_STATE.FOREGROUND;
-      $('shutdown-header-bar-item').hidden = !this.showShutdown_ ||
-          this.lockScreenAppsState_ == LOCK_SCREEN_APPS_STATE.FOREGROUND;
-      $('sign-out-user-item').hidden = !isLockScreen ||
-          this.lockScreenAppsState_ == LOCK_SCREEN_APPS_STATE.FOREGROUND;
-      $('unlock-user-header-bar-item').hidden = !isLockScreen ||
-          this.lockScreenAppsState_ != LOCK_SCREEN_APPS_STATE.FOREGROUND;
-
-      $('add-user-header-bar-item').hidden = $('add-user-button').hidden;
-      $('apps-header-bar-item').hidden =
-          !this.hasApps_ || (!gaiaIsActive && !accountPickerIsActive);
-      $('cancel-multiple-sign-in-item').hidden = !isMultiProfilesUI;
-
-      if (!Oobe.getInstance().newKioskUI) {
-        if (!$('apps-header-bar-item').hidden)
-          $('show-apps-button').didShow();
-      }
-
-      // Lock screen apps are generally shown maximized - update the header
-      // bar background opacity so the wallpaper is not visible behind it (
-      // since it won't be visible in the rest of UI).
-      this.classList.toggle(
-          'full-header-background',
-          this.lockScreenAppsState_ == LOCK_SCREEN_APPS_STATE.FOREGROUND ||
-              this.lockScreenAppsState_ == LOCK_SCREEN_APPS_STATE.BACKGROUND);
-    },
-
-    /**
-     * Animates Header bar to hide from the screen.
-     *
-     * @param {function()} callback will be called once animation is finished.
-     */
-    animateOut: function(callback) {
-      var launcher = this;
-      launcher.addEventListener('transitionend', function f(e) {
-        launcher.removeEventListener('transitionend', f);
-        callback();
-      });
-      // Guard timer for 2 seconds + 200 ms + epsilon.
-      ensureTransitionEndEvent(launcher, 2250);
-
-      this.classList.remove('login-header-bar-animate-slow');
-      this.classList.add('login-header-bar-animate-fast');
-      this.classList.add('login-header-bar-hidden');
-    },
-
-    /**
-     * Animates Header bar to appear on the screen.
-     *
-     * @param {boolean} fast Whether the animation should complete quickly or
-     *     slowly.
-     * @param {function()} callback will be called once animation is finished.
-     */
-    animateIn: function(fast, callback) {
-      if (callback) {
-        var launcher = this;
-        launcher.addEventListener('transitionend', function f(e) {
-          launcher.removeEventListener('transitionend', f);
-          callback();
-        });
-        // Guard timer for 2 seconds + 200 ms + epsilon.
-        ensureTransitionEndEvent(launcher, 2250);
-      }
-
-      if (fast) {
-        this.classList.remove('login-header-bar-animate-slow');
-        this.classList.add('login-header-bar-animate-fast');
-      } else {
-        this.classList.remove('login-header-bar-animate-fast');
-        this.classList.add('login-header-bar-animate-slow');
-      }
-
-      this.classList.remove('login-header-bar-hidden');
-    },
-  };
-
-  /**
-   * Convenience wrapper of animateOut.
-   */
-  HeaderBar.animateOut = function(callback) {
-    $('login-header-bar').animateOut(callback);
-  };
-
-  /**
-   * Convenience wrapper of animateIn.
-   */
-  HeaderBar.animateIn = function(fast, callback) {
-    $('login-header-bar').animateIn(fast, callback);
-  };
-
-  return {HeaderBar: HeaderBar};
-});
diff --git a/chrome/browser/resources/chromeos/login/login.html b/chrome/browser/resources/chromeos/login/login.html
deleted file mode 100644
index d07cb03..0000000
--- a/chrome/browser/resources/chromeos/login/login.html
+++ /dev/null
@@ -1,19 +0,0 @@
-<!doctype html>
-<html i18n-values="dir:textdirection;
-                   build:buildType;
-                   highlight:highlightStrength;
-                   lang:language">
-<head>
-<meta charset="utf-8">
-<meta name="google" value="notranslate">
-<title i18n-content="title"></title>
-<include src="login_shared.html">
-<include src="login_non_lock_shared.html">
-<include src="notification_card.html">
-<script src="chrome://oobe/login.js"></script>
-</head>
-<body i18n-values=".style.fontFamily:fontfamily;" class="chromeos">
-  <include src="screen_container.html">
-  <script src="chrome://resources/js/i18n_template.js"></script>
-</body>
-</html>
diff --git a/chrome/browser/resources/chromeos/login/login_shared.html b/chrome/browser/resources/chromeos/login/login_shared.html
deleted file mode 100644
index 1f9eefc..0000000
--- a/chrome/browser/resources/chromeos/login/login_shared.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!--
-This file is included for OOBE, login, and the lock screen. Only files which are
-needed by all three scenarios should be included here. If a file should only be
-shared between OOBE and login, add it to login_non_lock_shared.html.
-
-Any additional include increases the lock screen initialization time.
--->
-
-<!-- This must be the first import in all login pages. -->
-<link rel="import" href="chrome://resources/html/polymer.html">
-<link rel="stylesheet" href="chrome://resources/css/butter_bar.css">
-<link rel="stylesheet" href="chrome://resources/css/dialogs.css">
-<link rel="stylesheet" href="chrome://resources/css/list.css">
-<link rel="stylesheet" href="chrome://resources/css/menu_button.css">
-<link rel="stylesheet" href="chrome://resources/css/menu.css">
-<link rel="stylesheet" href="chrome://resources/css/spinner.css">
-<link rel="stylesheet" href="chrome://resources/css/throbber.css">
-<link rel="stylesheet" href="chrome://resources/css/widgets.css">
-
-<link rel="stylesheet" href="apps_menu.css">
-<link rel="stylesheet" href="../../../../../ui/login/bubble.css">
-<link rel="stylesheet" href="header_bar.css">
-<link rel="stylesheet" href="../../../../../ui/login/oobe.css">
-<link rel="stylesheet" href="oobe_popup_overlay.css">
-<link rel="stylesheet" href="oobe_screen.css">
-
-<link rel="stylesheet" href="../../../../../ui/login/screen_container.css">
-<link rel="stylesheet" href="../../../../../ui/login/account_picker/screen_account_picker.css">
-<link rel="stylesheet" href="../../../../../ui/login/account_picker/user_pod_row.css">
-
-<script src="chrome://resources/js/cr.js"></script>
-<script src="chrome://resources/js/event_tracker.js"></script>
-<script src="chrome://resources/js/cr/event_target.js"></script>
-<script src="chrome://resources/js/cr/ui.js"></script>
-<script src="chrome://resources/js/cr/ui/touch_handler.js"></script>
-<script src="chrome://resources/js/cr/ui/array_data_model.js"></script>
-<script src="chrome://resources/js/cr/ui/dialogs.js"></script>
-<script src="chrome://resources/js/cr/ui/list_selection_controller.js"></script>
-<script src="chrome://resources/js/cr/ui/list_selection_model.js"></script>
-<script src="chrome://resources/js/cr/ui/list_single_selection_model.js"></script>
-<script src="chrome://resources/js/cr/ui/list_item.js"></script>
-<script src="chrome://resources/js/cr/ui/list.js"></script>
-<script src="chrome://resources/js/cr/ui/grid.js"></script>
-<script src="chrome://resources/js/cr/ui/position_util.js"></script>
-<script src="chrome://resources/js/cr/ui/menu_item.js"></script>
-<script src="chrome://resources/js/cr/ui/menu.js"></script>
-<script src="chrome://resources/js/cr/ui/menu_button.js"></script>
-<script src="chrome://resources/js/load_time_data.js"></script>
-<script src="chrome://resources/js/util.js"></script>
-<script src="chrome://oobe/strings.js"></script>
diff --git a/chrome/browser/resources/chromeos/login/login_shared.js b/chrome/browser/resources/chromeos/login/login_shared.js
deleted file mode 100644
index b60ae27..0000000
--- a/chrome/browser/resources/chromeos/login/login_shared.js
+++ /dev/null
@@ -1,459 +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.
-
-/**
- * @fileoverview Common OOBE controller methods. This method is shared between
- * OOBE, login, and the lock screen. Add only methods that need to be shared
- * between all *three* screens here, as each additional method increases the
- * time it takes to show the lock screen.
- *
- * If a method needs to be shared between the oobe and login screens, add it to
- * login_non_lock_shared.js.
- */
-
-// <include src="test_util.js">
-// <include src="../../../../../ui/login/screen.js">
-// <include src="screen_context.js">
-// <include src="../user_images_grid.js">
-// <include src="apps_menu.js">
-// <include src="../../../../../ui/login/bubble.js">
-// <include src="../../../../../ui/login/display_manager.js">
-// <include src="header_bar.js">
-
-// <include
-// src="../../../../../ui/login/account_picker/screen_account_picker.js">
-
-// <include src="../../../../../ui/login/login_ui_tools.js">
-// <include src="../../../../../ui/login/account_picker/user_pod_row.js">
-// <include src="../../../../../ui/login/resource_loader.js">
-
-cr.define('cr.ui', function() {
-  var DisplayManager = cr.ui.login.DisplayManager;
-
-  /**
-   * Constructs an Out of box controller. It manages initialization of screens,
-   * transitions, error messages display.
-   * @extends {DisplayManager}
-   * @constructor
-   */
-  function Oobe() {}
-
-  /**
-   * Delay in milliseconds between start of OOBE animation and start of
-   * header bar animation.
-   */
-  var HEADER_BAR_DELAY_MS = 300;
-
-  cr.addSingletonGetter(Oobe);
-
-  Oobe.prototype = {
-    __proto__: DisplayManager.prototype,
-  };
-
-  /**
-   * Called when focus is returned from ash::SystemTray.
-   */
-  Oobe.focusReturned = function() {
-    if (Oobe.getInstance().currentScreen &&
-        Oobe.getInstance().currentScreen.onFocusReturned) {
-      Oobe.getInstance().currentScreen.onFocusReturned();
-    }
-  };
-
-  /**
-   * Handle accelerators. These are passed from native code instead of a JS
-   * event handler in order to make sure that embedded iframes cannot swallow
-   * them.
-   * @param {string} name Accelerator name.
-   */
-  Oobe.handleAccelerator = function(name) {
-    Oobe.getInstance().handleAccelerator(name);
-  };
-
-  /**
-   * Shows the given screen.
-   * @param {Object} screen Screen params dict, e.g. {id: screenId, data: data}
-   */
-  Oobe.showScreen = function(screen) {
-    Oobe.getInstance().showScreen(screen);
-  };
-
-  /**
-   * Updates missin API keys message visibility.
-   * @param {boolean} show True if the message should be visible.
-   */
-  Oobe.showAPIKeysNotice = function(show) {
-    $('api-keys-notice-container').hidden = !show;
-  };
-
-  /**
-   * Updates version label visibility.
-   * @param {boolean} show True if version label should be visible.
-   */
-  Oobe.showVersion = function(show) {
-    Oobe.getInstance().showVersion(show);
-  };
-
-  /**
-   * Update body class to switch between OOBE UI and Login UI.
-   */
-  Oobe.showOobeUI = function(showOobe) {
-    if (showOobe) {
-      document.body.classList.add('oobe-display');
-
-      // Callback to animate the header bar in.
-      var showHeaderBar = function() {
-        login.HeaderBar.animateIn(false, function() {
-          chrome.send('headerBarVisible');
-        });
-      };
-      // Start asynchronously so the OOBE welcome screen comes in first.
-      window.setTimeout(showHeaderBar, HEADER_BAR_DELAY_MS);
-    } else {
-      document.body.classList.remove('oobe-display');
-      Oobe.getInstance().prepareForLoginDisplay_();
-      // Ensure header bar is visible when switching to Login UI from oobe.
-      if (Oobe.getInstance().displayType == DISPLAY_TYPE.OOBE)
-        login.HeaderBar.animateIn(true);
-    }
-
-    Oobe.getInstance().headerHidden = false;
-  };
-
-  /**
-   * When |showShutdown| is set to "true", the shutdown button is shown and the
-   * reboot button hidden. If set to "false", the reboot button is visible and
-   * the shutdown button hidden.
-   */
-  Oobe.showShutdown = function(showShutdown) {
-    $('login-header-bar').showShutdownButton = showShutdown;
-    $('login-header-bar').showRebootButton = !showShutdown;
-  };
-
-  /**
-   * Enables keyboard driven flow.
-   */
-  Oobe.enableKeyboardFlow = function(value) {
-    // Don't show header bar for OOBE.
-    Oobe.getInstance().forceKeyboardFlow = value;
-  };
-
-  /**
-   * Disables signin UI.
-   */
-  Oobe.disableSigninUI = function() {
-    DisplayManager.disableSigninUI();
-  };
-
-  /**
-   * Shows signin UI.
-   * @param {string} opt_email An optional email for signin UI.
-   */
-  Oobe.showSigninUI = function(opt_email) {
-    DisplayManager.showSigninUI(opt_email);
-  };
-
-  /**
-   * Resets sign-in input fields.
-   * @param {boolean} forceOnline Whether online sign-in should be forced.
-   * If |forceOnline| is false previously used sign-in type will be used.
-   */
-  Oobe.resetSigninUI = function(forceOnline) {
-    DisplayManager.resetSigninUI(forceOnline);
-  };
-
-  /**
-   * Shows sign-in error bubble.
-   * @param {number} loginAttempts Number of login attemps tried.
-   * @param {string} message Error message to show.
-   * @param {string} link Text to use for help link.
-   * @param {number} helpId Help topic Id associated with help link.
-   */
-  Oobe.showSignInError = function(loginAttempts, message, link, helpId) {
-    DisplayManager.showSignInError(loginAttempts, message, link, helpId);
-  };
-
-  /**
-   * Shows password changed screen that offers migration.
-   * @param {boolean} showError Whether to show the incorrect password error.
-   */
-  Oobe.showPasswordChangedScreen = function(showError, email) {
-    DisplayManager.showPasswordChangedScreen(showError, email);
-  };
-
-  /**
-   * Shows TPM error screen.
-   */
-  Oobe.showTpmError = function() {
-    DisplayManager.showTpmError();
-  };
-
-  /**
-   * Shows Active Directory password change screen.
-   * @param {string} username Name of the user that should change the password.
-   */
-  Oobe.showActiveDirectoryPasswordChangeScreen = function(username) {
-    DisplayManager.showActiveDirectoryPasswordChangeScreen(username);
-  };
-
-  /**
-   * Show user-pods.
-   */
-  Oobe.showUserPods = function() {
-    $('pod-row').maybePreselectPod();
-    Oobe.showScreen({id: SCREEN_ACCOUNT_PICKER});
-    Oobe.resetSigninUI(true);
-  };
-
-  /**
-   * Clears error bubble as well as optional menus that could be open.
-   */
-  Oobe.clearErrors = function() {
-    var accessibilityMenu = $('accessibility-menu');
-    if (accessibilityMenu)
-      accessibilityMenu.hide();
-    DisplayManager.clearErrors();
-  };
-
-  /**
-   * Sets text content for a div with |labelId|.
-   * @param {string} labelId Id of the label div.
-   * @param {string} labelText Text for the label.
-   */
-  Oobe.setLabelText = function(labelId, labelText) {
-    DisplayManager.setLabelText(labelId, labelText);
-  };
-
-  /**
-   * Sets the text content of the enterprise info message.
-   * If the text is empty, the entire notification will be hidden.
-   * @param {string} messageText The message text.
-   */
-  Oobe.setEnterpriseInfo = function(messageText, assetId) {
-    DisplayManager.setEnterpriseInfo(messageText, assetId);
-  };
-
-  /**
-   * Sets the text content of the Bluetooth device info message.
-   * @param {string} bluetoothName The Bluetooth device name text.
-   */
-  Oobe.setBluetoothDeviceInfo = function(bluetoothName) {
-    DisplayManager.setBluetoothDeviceInfo(bluetoothName);
-  };
-
-  /**
-   * Updates the device requisition string shown in the requisition prompt.
-   * @param {string} requisition The device requisition.
-   */
-  Oobe.updateDeviceRequisition = function(requisition) {
-    Oobe.getInstance().updateDeviceRequisition(requisition);
-  };
-
-  /**
-   * Clears password field in user-pod.
-   */
-  Oobe.clearUserPodPassword = function() {
-    DisplayManager.clearUserPodPassword();
-  };
-
-  /**
-   * Restores input focus to currently selected pod.
-   */
-  Oobe.refocusCurrentPod = function() {
-    DisplayManager.refocusCurrentPod();
-  };
-
-  /**
-   * Some ForTesting APIs directly access to DOM. Because this script is loaded
-   * in header, DOM tree may not be available at beginning.
-   * In DOMContentLoaded, after Oobe.initialize() is done, this is marked to
-   * true, indicating ForTesting methods can be called.
-   * External script using ForTesting APIs should wait for this condition.
-   * @type {boolean}
-   */
-  Oobe.readyForTesting = false;
-
-  /**
-   * Skip to login screen for telemetry.
-   */
-  Oobe.skipToLoginForTesting = function() {
-    Oobe.disableSigninUI();
-    chrome.send('skipToLoginForTesting');
-  };
-
-  /**
-   * Skip to update screen for telemetry.
-   */
-  Oobe.skipToUpdateForTesting = function() {
-    Oobe.disableSigninUI();
-    chrome.send('skipToUpdateForTesting');
-  };
-
-  /**
-   * Login for telemetry.
-   * @param {string} username Login username.
-   * @param {string} password Login password.
-   * @param {boolean} enterpriseEnroll Login as an enterprise enrollment?
-   */
-  Oobe.loginForTesting = function(
-      username, password, gaia_id, enterpriseEnroll = false) {
-    // Helper method that runs |fn| after |screenName| is visible.
-    function waitForOobeScreen(screenName, fn) {
-      if (Oobe.getInstance().currentScreen &&
-          Oobe.getInstance().currentScreen.id === screenName) {
-        fn();
-      } else {
-        $('oobe').addEventListener('screenchanged', function handler(e) {
-          if (e.detail == screenName) {
-            $('oobe').removeEventListener('screenchanged', handler);
-            fn();
-          }
-        });
-      }
-    }
-
-    Oobe.disableSigninUI();
-    chrome.send('skipToLoginForTesting', [username]);
-
-    if (!enterpriseEnroll) {
-      chrome.send('completeLogin', [gaia_id, username, password, false]);
-    } else {
-      waitForOobeScreen('gaia-signin', function() {
-        chrome.send('toggleEnrollmentScreen');
-        chrome.send('toggleFakeEnrollment');
-      });
-
-      waitForOobeScreen('oauth-enrollment', function() {
-        chrome.send('oauthEnrollCompleteLogin', [username]);
-      });
-    }
-  };
-
-  /**
-   * Guest login for telemetry.
-   */
-  Oobe.guestLoginForTesting = function() {
-    Oobe.skipToLoginForTesting();
-    chrome.send('launchIncognito');
-  };
-
-  /**
-   * Authenticate for telemetry - used for screenlocker.
-   * @param {string} username Login username.
-   * @param {string} password Login password.
-   */
-  Oobe.authenticateForTesting = function(username, password) {
-    Oobe.disableSigninUI();
-    chrome.send('authenticateUser', [username, password, false]);
-  };
-
-  /**
-   * Gaia login screen for telemetry.
-   */
-  Oobe.addUserForTesting = function() {
-    Oobe.skipToLoginForTesting();
-    chrome.send('addUser');
-  };
-
-  /**
-   * Shows the add user dialog. Used in browser tests.
-   */
-  Oobe.showAddUserForTesting = function() {
-    chrome.send('showAddUser');
-  };
-
-  /**
-   * Hotrod requisition for telemetry.
-   */
-  Oobe.remoraRequisitionForTesting = function() {
-    chrome.send('setDeviceRequisition', ['remora']);
-  };
-
-  /**
-   * Begin enterprise enrollment for telemetry.
-   */
-  Oobe.switchToEnterpriseEnrollmentForTesting = function() {
-    chrome.send('toggleEnrollmentScreen');
-  };
-
-  /**
-   * Finish enterprise enrollment for telemetry.
-   */
-  Oobe.enterpriseEnrollmentDone = function() {
-    chrome.send('oauthEnrollClose', ['done']);
-  };
-
-  /**
-   * Returns true if enrollment was successful. Dismisses the enrollment
-   * attribute screen if it's present.
-   */
-  Oobe.isEnrollmentSuccessfulForTest = function() {
-    if (document.querySelector('.oauth-enroll-state-attribute-prompt'))
-      chrome.send('oauthEnrollAttributes', ['', '']);
-
-    return $('oauth-enrollment')
-        .classList.contains('oauth-enroll-state-success');
-  };
-
-  /**
-   * Shows/hides login UI control bar with buttons like [Shut down].
-   */
-  Oobe.showControlBar = function(show) {
-    Oobe.getInstance().headerHidden = !show;
-  };
-
-  /**
-   * Changes some UI which depends on the virtual keyboard being shown/hidden.
-   */
-  Oobe.setVirtualKeyboardShown = function(shown) {
-    Oobe.getInstance().virtualKeyboardShown = shown;
-    $('pod-row').setFocusedPodPinVisibility(!shown);
-  };
-
-  /**
-   * Sets the current size of the client area (display size).
-   * @param {number} width client area width
-   * @param {number} height client area height
-   */
-  Oobe.setClientAreaSize = function(width, height) {
-    Oobe.getInstance().setClientAreaSize(width, height);
-  };
-
-  // Export
-  return {Oobe: Oobe};
-});
-
-var Oobe = cr.ui.Oobe;
-
-// Allow selection events on components with editable text (password field)
-// bug (http://code.google.com/p/chromium/issues/detail?id=125863)
-disableTextSelectAndDrag(function(e) {
-  var src = e.target;
-  return src instanceof HTMLTextAreaElement ||
-      src instanceof HTMLInputElement && /text|password|search/.test(src.type);
-});
-
-
-(function() {
-'use strict';
-
-document.addEventListener('DOMContentLoaded', function() {
-  try {
-    Oobe.initialize();
-  } finally {
-    // TODO(crbug.com/712078): Do not set readyForTesting in case of that
-    // initialize() is failed. Currently, in some situation, initialize()
-    // raises an exception unexpectedly. It means testing APIs should not
-    // be called then. However, checking it here now causes bots failures
-    // unfortunately. So, as a short term workaround, here set
-    // readyForTesting even on failures, just to make test bots happy.
-    Oobe.readyForTesting = true;
-  }
-});
-
-// Install a global error handler so stack traces are included in logs.
-window.onerror = function(message, file, line, column, error) {
-  console.error(error.stack);
-};
-})();
diff --git a/chrome/browser/resources/chromeos/login/screen_container.html b/chrome/browser/resources/chromeos/login/screen_container.html
deleted file mode 100644
index b8c8b62..0000000
--- a/chrome/browser/resources/chromeos/login/screen_container.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<div id="background" class="background-initial"></div>
-<include src="api_keys_notice.html">
-<div id="scroll-container">
-  <div id="outer-container" class="down">
-    <div id="oobe" class="faded">
-      <div id="inner-container" class="down">
-        <div id="step-logo" hidden>
-          <img src="chrome://theme/IDR_PRODUCT_LOGO" alt>
-          <div id="header-sections">
-          </div>
-        </div>
-        <include src="[OOBE]_screens.html">
-      </div>
-    </div>
-  </div>
-</div>
-<div id="bubble" class="bubble faded" hidden></div>
-<include src="version.html">
-<include src="header_bar.html">
-<include src="../../../../../ui/login/account_picker/user_pod_template.html">
-<include src="oobe_screen_reset_confirmation_overlay.html">
diff --git a/chrome/browser/resources/device_log_ui/device_log_ui.html b/chrome/browser/resources/device_log_ui/device_log_ui.html
index c4dc792..4a6af562 100644
--- a/chrome/browser/resources/device_log_ui/device_log_ui.html
+++ b/chrome/browser/resources/device_log_ui/device_log_ui.html
@@ -3,13 +3,14 @@
 <head>
   <meta charset="utf-8">
   <title id="device-log-title">$i18n{titleText}</title>
-  <link rel="stylesheet" href="chrome://device-log/device_log_ui.css">
+  <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
+  <link rel="stylesheet" href="device_log_ui.css">
   <script src="chrome://resources/js/load_time_data.js"></script>
   <script src="chrome://resources/js/util.js"></script>
-  <script src="chrome://device-log/strings.js"></script>
-  <script src="chrome://device-log/device_log_ui.js"></script>
+  <script src="strings.js"></script>
+  <script src="device_log_ui.js"></script>
 </head>
-<body style="font-family: $i18n{fontfamily}; font-size: $i18n{fontsize};">
+<body>
   <div id="header">
     <p>$i18n{autoRefreshText}</p>
   </div>
diff --git a/chrome/browser/resources/downloads/browser_proxy.js b/chrome/browser/resources/downloads/browser_proxy.js
index 7c09bdd..b212a16 100644
--- a/chrome/browser/resources/downloads/browser_proxy.js
+++ b/chrome/browser/resources/downloads/browser_proxy.js
@@ -5,13 +5,13 @@
 cr.define('downloads', function() {
   class BrowserProxy {
     constructor() {
-      /** @type {mdDownloads.mojom.PageCallbackRouter} */
-      this.callbackRouter = new mdDownloads.mojom.PageCallbackRouter();
+      /** @type {downloads.mojom.PageCallbackRouter} */
+      this.callbackRouter = new downloads.mojom.PageCallbackRouter();
 
-      /** @type {mdDownloads.mojom.PageHandlerProxy} */
-      this.handler = new mdDownloads.mojom.PageHandlerProxy();
+      /** @type {downloads.mojom.PageHandlerProxy} */
+      this.handler = new downloads.mojom.PageHandlerProxy();
 
-      const factory = mdDownloads.mojom.PageHandlerFactory.getProxy();
+      const factory = downloads.mojom.PageHandlerFactory.getProxy();
       factory.createPageHandler(
           this.callbackRouter.createProxy(), this.handler.createRequest());
     }
diff --git a/chrome/browser/resources/downloads/externs.js b/chrome/browser/resources/downloads/externs.js
index 82757ab..5100068 100644
--- a/chrome/browser/resources/downloads/externs.js
+++ b/chrome/browser/resources/downloads/externs.js
@@ -13,6 +13,6 @@
 /**
  * The type of the download object. The definition is based on the Data struct
  * in downloads.mojom.
- * @typedef {mdDownloads.mojom.Data | {hideDate: boolean}}
+ * @typedef {downloads.mojom.Data | {hideDate: boolean}}
  */
 downloads.Data;
diff --git a/chrome/browser/resources/downloads/item.js b/chrome/browser/resources/downloads/item.js
index 3c87033..b28e306 100644
--- a/chrome/browser/resources/downloads/item.js
+++ b/chrome/browser/resources/downloads/item.js
@@ -98,7 +98,7 @@
       'observeIsDangerous_(isDangerous_, data)',
     ],
 
-    /** @private {mdDownloads.mojom.PageHandlerInterface} */
+    /** @private {downloads.mojom.PageHandlerInterface} */
     mojoHandler_: null,
 
     /** @override */
diff --git a/chrome/browser/resources/downloads/manager.js b/chrome/browser/resources/downloads/manager.js
index 7983162..ec6e547 100644
--- a/chrome/browser/resources/downloads/manager.js
+++ b/chrome/browser/resources/downloads/manager.js
@@ -60,10 +60,10 @@
       'itemsChanged_(items_.*)',
     ],
 
-    /** @private {mdDownloads.mojom.PageCallbackRouter} */
+    /** @private {downloads.mojom.PageCallbackRouter} */
     mojoEventTarget_: null,
 
-    /** @private {mdDownloads.mojom.PageHandlerInterface} */
+    /** @private {downloads.mojom.PageHandlerInterface} */
     mojoHandler_: null,
 
     /** @private {?downloads.SearchService} */
diff --git a/chrome/browser/resources/downloads/search_service.js b/chrome/browser/resources/downloads/search_service.js
index 26f7ac9c..b5d3be9 100644
--- a/chrome/browser/resources/downloads/search_service.js
+++ b/chrome/browser/resources/downloads/search_service.js
@@ -8,7 +8,7 @@
       /** @private {!Array<string>} */
       this.searchTerms_ = [];
 
-      /** @private {mdDownloads.mojom.PageHandlerInterface} */
+      /** @private {downloads.mojom.PageHandlerInterface} */
       this.mojoHandler_ = downloads.BrowserProxy.getInstance().handler;
     }
 
diff --git a/chrome/browser/resources/downloads/search_service_unittest.gtestjs b/chrome/browser/resources/downloads/search_service_unittest.gtestjs
index bd2bff2..f0d207d0 100644
--- a/chrome/browser/resources/downloads/search_service_unittest.gtestjs
+++ b/chrome/browser/resources/downloads/search_service_unittest.gtestjs
@@ -34,7 +34,7 @@
 });
 
 TEST_F('SearchServiceUnitTest', 'searchWithSimilarTerms', function() {
-  // TODO(calamity): Replace this with an mdDownloads.mojom.PageHandlerProxy()
+  // TODO(calamity): Replace this with an downloads.mojom.PageHandlerProxy()
   // once we can include generated files from inside a gtestjs.
   downloads.BrowserProxy.instance_ = {handler: {}};
   class TestSearchService extends downloads.SearchService {
diff --git a/chrome/browser/resources/downloads/toolbar.js b/chrome/browser/resources/downloads/toolbar.js
index db9f9b8..d90ed2d 100644
--- a/chrome/browser/resources/downloads/toolbar.js
+++ b/chrome/browser/resources/downloads/toolbar.js
@@ -20,7 +20,7 @@
       },
     },
 
-    /** @private {?mdDownloads.mojom.PageHandlerInterface} */
+    /** @private {?downloads.mojom.PageHandlerInterface} */
     mojoHandler_: null,
 
     /** @override */
diff --git a/chrome/browser/resources/md_user_manager/create_profile.html b/chrome/browser/resources/md_user_manager/create_profile.html
index 54fd9b50..8676ea7f 100644
--- a/chrome/browser/resources/md_user_manager/create_profile.html
+++ b/chrome/browser/resources/md_user_manager/create_profile.html
@@ -36,7 +36,7 @@
         align-items: center;
         background-color: var(--paper-red-50);
         box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .12);
-        color: var(--google-red-700);
+        color: var(--error-color);
         display: flex;
         height: 40px;
         left: 0;
@@ -48,6 +48,12 @@
         visibility: hidden;
       }
 
+      :host-context([dark]) #message-container {
+        background-color: unset;
+        border-bottom: var(--cr-separator-line);
+        box-shadow: none;
+      }
+
       #message-container[visible] {
         -webkit-transition: top 400ms cubic-bezier(.4, 0, .2, 1),
                             visibility 0s linear 0s;
@@ -76,7 +82,7 @@
 
       #nameInput {
         --cr-input-input: {
-          border-bottom: 1px solid var(--paper-grey-800);
+          border-bottom: 1px solid var(--cr-secondary-text-color);
         }
         margin-bottom: 24px;
         margin-top: 32px;
diff --git a/chrome/browser/resources/md_user_manager/create_profile.js b/chrome/browser/resources/md_user_manager/create_profile.js
index 8e4cbbf5..3ddd8cec 100644
--- a/chrome/browser/resources/md_user_manager/create_profile.js
+++ b/chrome/browser/resources/md_user_manager/create_profile.js
@@ -7,7 +7,6 @@
  * a profile, including choosing a name, and an avatar.
  */
 
-(function() {
 Polymer({
   is: 'create-profile',
 
@@ -248,4 +247,3 @@
     return createInProgress || !profileName || !nameInput.validate();
   },
 });
-}());
diff --git a/chrome/browser/resources/md_user_manager/shared_styles.html b/chrome/browser/resources/md_user_manager/shared_styles.html
index 49d7889..48aa3ac 100644
--- a/chrome/browser/resources/md_user_manager/shared_styles.html
+++ b/chrome/browser/resources/md_user_manager/shared_styles.html
@@ -5,18 +5,23 @@
 <dom-module id="shared-styles">
   <template>
     <style include="paper-button-style">
-      :root {
+      :host > * {
         --error-color: var(--google-red-700);
         --page-width: 624px;
         --title-icon-color: var(--paper-grey-500);
         --user-manager-separator-line: 1px solid rgba(0, 0, 0, .12);
       }
 
+      :host-context([dark]) > * {
+        --error-color: var(--google-red-refresh-300);
+        --user-manager-separator-line: var(--cr-separator-line);
+      }
+
       a,
       a:active,
       a:hover,
       a:visited {
-        color: var(--google-blue-700);
+        color: var(--cr-link-color);
         cursor: pointer;
         text-decoration: none;
       }
diff --git a/chrome/browser/resources/md_user_manager/user_manager.html b/chrome/browser/resources/md_user_manager/user_manager.html
index 3401148..43c8ecd4 100644
--- a/chrome/browser/resources/md_user_manager/user_manager.html
+++ b/chrome/browser/resources/md_user_manager/user_manager.html
@@ -35,6 +35,10 @@
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
   <custom-style>
     <style is="custom-style" include="shared-styles">
+      html[dark] body {
+        background-color: var(--google-grey-900);
+      }
+
       user-manager-pages,
       #login-header-bar {
         bottom: 0;
@@ -84,6 +88,7 @@
 
       .pod {
         @apply --cr-card-elevation;
+        background-color: var(--cr-card-background-color);
         border-radius: var(--cr-card-border-radius);
         color: var(--cr-primary-text-color);
         cursor: default;
@@ -142,6 +147,14 @@
         width: 92px;
       }
 
+      html[dark] .pod .main-pane .name-container {
+        background-color: transparent;
+      }
+
+      html[dark] .name {
+        color: inherit;
+      }
+
       .pod .indicator-container {
         bottom: 10px;
         display: flex;
@@ -167,6 +180,10 @@
         width: 20px;
       }
 
+      html[dark] .pod .indicator-container > .indicator {
+        background: var(--google-grey-refresh-500);
+      }
+
       .pod.locked .locked-indicator {
         -webkit-mask-image: url(../../../../ui/webui/resources/images/lock.svg);
         display: block;
@@ -269,11 +286,28 @@
         right: auto;
       }
 
+      html[dark] .action-box-area.active ~ .action-box-menu {
+        background-color: var(--cr-menu-background-color);
+        /* Adds 4% white on top of GG900. To/from are intentionally the same. */
+        background-image: linear-gradient(rgba(255, 255, 255, .04),
+                                          rgba(255, 255, 255, .04));
+        box-shadow: var(--cr-menu-shadow);
+      }
+
+      html[dark] .action-box-menu-remove:focus,
+      html[dark] .action-box-menu-remove:hover {
+        background-color: var(--cr-menu-background-focus-color);
+      }
+
       .action-box-menu-title {
         color: inherit;
         padding: 16px 12px;
       }
 
+      html[dark] .action-box-menu-title {
+        border-bottom: var(--cr-separator-line);
+      }
+
       .action-box-menu-title-name,
       .action-box-menu-title-email {
         height: auto;
@@ -288,10 +322,19 @@
       }
 
       .action-box-menu-remove {
+        --side-gap: 12px;
+        --vertical-gap: 8px;
         border-radius: var(--cr-card-border-radius);
         border-top: var(--user-manager-separator-line);
         line-height: 32px;
-        padding: 8px 12px;
+        padding: var(--vertical-gap) var(--side-gap);
+      }
+
+      html[dark] .action-box-menu-remove {
+        border-radius: 0;
+        border-top: none;
+        margin: var(--vertical-gap) 0;
+        padding: 0 var(--side-gap);
       }
 
       .action-box-remove-user-warning {
@@ -318,21 +361,24 @@
       }
 
       #user-manager-prompt-message {
-        background-image:
-            url(../../../../ui/webui/resources/images/business.svg);
-        background-position: 0 center;
-        background-repeat: no-repeat;
-        background-size: 19px;
+        align-items: center;
+        color: var(--cr-primary-text-color);
+        display: flex;
         font-size: 19px;
         margin-bottom: 45px;
-        padding-left: 30px;
         text-align: center;
       }
 
-      html[dir=rtl] #user-manager-prompt-message {
-        background-position: right center;
-        padding-left: 0;
-        padding-right: 30px;
+      #user-manager-prompt-message::before {
+        -webkit-mask-image:
+            url(../../../../ui/webui/resources/images/business.svg);
+        -webkit-mask-size: 19px;
+        background: currentcolor;
+        content: '';
+        display: inline-block;
+        height: 19px;
+        margin-inline-end: 11px;
+        width: 19px;
       }
 
       #user-manager-prompt-message:empty {
diff --git a/chrome/browser/resources/md_user_manager/user_manager_tutorial.html b/chrome/browser/resources/md_user_manager/user_manager_tutorial.html
index 1a14dab..f714e514b 100644
--- a/chrome/browser/resources/md_user_manager/user_manager_tutorial.html
+++ b/chrome/browser/resources/md_user_manager/user_manager_tutorial.html
@@ -11,7 +11,7 @@
       .tutorial-slide {
         @apply --cr-card-elevation;
         -webkit-transition: opacity 200ms ease-in-out;
-        background-color: white;
+        background-color: var(--cr-card-background-color);
         border-radius: var(--cr-card-border-radius);
         bottom: 0;
         height: 408px;
@@ -99,12 +99,11 @@
 
       .arrow-down {
         border-bottom: 10px solid white;
-        border-left: 10px solid white;
-        border-right: 10px solid transparent;
+        border-left: 10px solid white;  /* csschecker-disable-line left-right */
+        border-right: 10px solid transparent;  /* csschecker-disable-line left-right */
         border-top: 10px solid transparent;
         bottom: -10px;
-        box-shadow: rgba(60, 64, 67, .4) -1px 1px 1px 0,
-            rgba(60, 64, 67, .15) -2px 2px 2px 0;
+        box-shadow: var(--cr-card-shadow);
         height: 0;
         position: absolute;
         right: 40px;
@@ -117,6 +116,12 @@
         right: auto;
       }
 
+      :host-context([dark]) .arrow-down {
+        --gg900-blended-with-4percent-white: rgb(40, 41, 44);
+        border-bottom-color: var(--gg900-blended-with-4percent-white);
+        border-left-color: var(--gg900-blended-with-4percent-white);  /* csschecker-disable-line left-right */
+      }
+
       #guests .arrow-down {
         right: 110px;
       }
@@ -155,6 +160,10 @@
             url(chrome://theme/IDR_ICON_USER_MANAGER_TUTORIAL_COMPLETE);
       }
 
+      :host-context([dark]) #notYou {
+        color: var(--cr-primary-text-color);
+      }
+
       #notYou #dismiss {
         cursor: pointer;
         position: absolute;
@@ -162,6 +171,10 @@
         top: 5px;
       }
 
+      :host-context([dark]) #notYou #dismiss {
+        color: var(--google-grey-500);
+      }
+
       #notYou #slide-add-user {
         margin-top: 10px;
       }
diff --git a/chrome/browser/resources/print_preview/new/destination_list.js b/chrome/browser/resources/print_preview/new/destination_list.js
index 7b0c3af..59ee859 100644
--- a/chrome/browser/resources/print_preview/new/destination_list.js
+++ b/chrome/browser/resources/print_preview/new/destination_list.js
@@ -117,7 +117,9 @@
       this.throbberHidden_ =
           maxDisplayedItems <= this.matchingDestinations_.length;
     }
-    this.forceIronResize();
+    Polymer.RenderStatus.afterNextRender(this, () => {
+      this.forceIronResize();
+    });
   }
 });
 })();
diff --git a/chrome/browser/resources/settings/android_apps_page/android_apps_page.html b/chrome/browser/resources/settings/android_apps_page/android_apps_page.html
index a4d27d0..a7d73ca 100644
--- a/chrome/browser/resources/settings/android_apps_page/android_apps_page.html
+++ b/chrome/browser/resources/settings/android_apps_page/android_apps_page.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/polymer.html">
 
+<link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
 <link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_pref_indicator.html">
 <link rel="import" href="chrome://resources/html/i18n_behavior.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
@@ -52,8 +53,10 @@
           </div>
         </template>
         <template is="dom-if" if="[[!havePlayStoreApp]]" restamp>
-          <settings-android-settings-element>
-          </settings-android-settings-element>
+          <cr-link-row id="manageApps" icon-class="icon-external"
+              label="$i18n{androidAppsManageApps}"
+              on-click="onManageAndroidAppsTap_">
+          </cr-link-row>
         </template>
       </div>
 
diff --git a/chrome/browser/resources/settings/android_apps_page/android_apps_page.js b/chrome/browser/resources/settings/android_apps_page/android_apps_page.js
index bb419d6..f84e027 100644
--- a/chrome/browser/resources/settings/android_apps_page/android_apps_page.js
+++ b/chrome/browser/resources/settings/android_apps_page/android_apps_page.js
@@ -62,4 +62,15 @@
       settings.navigateTo(settings.routes.ANDROID_APPS_DETAILS);
     }
   },
+
+  /**
+   * @param {!MouseEvent} event
+   * @private
+   */
+  onManageAndroidAppsTap_: function(event) {
+    // |event.detail| is the click count. Keyboard events will have 0 clicks.
+    const isKeyboardAction = event.detail == 0;
+    settings.AndroidAppsBrowserProxyImpl.getInstance().showAndroidAppsSettings(
+        isKeyboardAction);
+  },
 });
diff --git a/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html b/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html
index 8041f79..7f5fe396 100644
--- a/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html
+++ b/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.html
@@ -1,6 +1,7 @@
 <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/cr_link_row/cr_link_row.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">
@@ -10,15 +11,16 @@
 <link rel="import" href="../i18n_setup.html">
 <link rel="import" href="../prefs/prefs_behavior.html">
 <link rel="import" href="../settings_shared_css.html">
-<link rel="import" href="android_settings_element.html">
 
 <dom-module id="settings-android-apps-subpage">
   <template>
     <style include="settings-shared"></style>
 
     <template is="dom-if" if="[[androidAppsInfo.settingsAppAvailable]]" restamp>
-      <settings-android-settings-element>
-      </settings-android-settings-element>
+      <cr-link-row id="manageApps" icon-class="icon-external"
+          label="$i18n{androidAppsManageApps}"
+          on-click="onManageAndroidAppsTap_">
+      </cr-link-row>
     </template>
 
     <template is="dom-if" if="[[allowRemove_(prefs.arc.enabled.*)]]">
diff --git a/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.js b/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.js
index c8baa19e..ddd09f37 100644
--- a/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.js
+++ b/chrome/browser/resources/settings/android_apps_page/android_apps_subpage.js
@@ -39,14 +39,6 @@
     }
   },
 
-  /** @private {?settings.AndroidAppsBrowserProxy} */
-  browserProxy_: null,
-
-  /** @override */
-  created: function() {
-    this.browserProxy_ = settings.AndroidAppsBrowserProxyImpl.getInstance();
-  },
-
   /** @private */
   onPlayStoreEnabledChanged_: function(enabled) {
     if (!enabled &&
@@ -104,4 +96,15 @@
   onConfirmDisableDialogClose_: function() {
     cr.ui.focusWithoutInk(assert(this.$$('#remove button')));
   },
+
+  /**
+   * @param {!MouseEvent} event
+   * @private
+   */
+  onManageAndroidAppsTap_: function(event) {
+    // |event.detail| is the click count. Keyboard events will have 0 clicks.
+    const isKeyboardAction = event.detail == 0;
+    settings.AndroidAppsBrowserProxyImpl.getInstance().showAndroidAppsSettings(
+        isKeyboardAction);
+  },
 });
diff --git a/chrome/browser/resources/settings/android_apps_page/android_settings_element.html b/chrome/browser/resources/settings/android_apps_page/android_settings_element.html
deleted file mode 100644
index 0fc45fb..0000000
--- a/chrome/browser/resources/settings/android_apps_page/android_settings_element.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
-<link rel="import" href="../i18n_setup.html">
-<link rel="import" href="../settings_shared_css.html">
-<link rel="import" href="android_apps_browser_proxy.html">
-
-<dom-module id="settings-android-settings-element">
-  <template>
-    <style include="settings-shared"></style>
-    <div id="manageApps" class="settings-box first"
-        on-keydown="onManageAndroidAppsKeydown_"
-        on-click="onManageAndroidAppsTap_" actionable>
-      <div class="start">
-        <div>$i18n{androidAppsManageApps}</div>
-      </div>
-      <paper-icon-button-light class="icon-external">
-        <button></button>
-      </paper-icon-button-light>
-    </div>
-  </template>
-  <script src="android_settings_element.js"></script>
-</dom-module>
diff --git a/chrome/browser/resources/settings/android_apps_page/android_settings_element.js b/chrome/browser/resources/settings/android_apps_page/android_settings_element.js
deleted file mode 100644
index 1764e8ec2..0000000
--- a/chrome/browser/resources/settings/android_apps_page/android_settings_element.js
+++ /dev/null
@@ -1,37 +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.
-
-/**
- * @fileoverview
- * 'android-settings-element' is the element to open Android settings.
- */
-
-Polymer({
-  is: 'settings-android-settings-element',
-
-  /** @private {?settings.AndroidAppsBrowserProxy} */
-  browserProxy_: null,
-
-  /** @override */
-  created: function() {
-    this.browserProxy_ = settings.AndroidAppsBrowserProxyImpl.getInstance();
-  },
-
-  /**
-   * @param {!Event} event
-   * @private
-   */
-  onManageAndroidAppsKeydown_: function(event) {
-    if (event.key != 'Enter' && event.key != ' ') {
-      return;
-    }
-    this.browserProxy_.showAndroidAppsSettings(true /** keyboardAction */);
-    event.stopPropagation();
-  },
-
-  /** @private */
-  onManageAndroidAppsTap_: function(event) {
-    this.browserProxy_.showAndroidAppsSettings(false /** keyboardAction */);
-  },
-});
\ No newline at end of file
diff --git a/chrome/browser/resources/settings/crostini_page/crostini_shared_paths.html b/chrome/browser/resources/settings/crostini_page/crostini_shared_paths.html
index 747c47b..2acab1f9 100644
--- a/chrome/browser/resources/settings/crostini_page/crostini_shared_paths.html
+++ b/chrome/browser/resources/settings/crostini_page/crostini_shared_paths.html
@@ -12,6 +12,7 @@
     <style include="settings-shared"></style>
     <div class="settings-box first">
       <div>
+        $i18n{crostiniSharedPathsInstructionsLocate}
         $i18n{crostiniSharedPathsInstructionsAdd}
         <span id="crostiniInstructionsRemove"
             hidden="[[!sharedPaths_.length]]">
@@ -19,14 +20,21 @@
         </span>
       </div>
     </div>
-    <template is="dom-repeat" items="[[sharedPaths_]]">
-      <div class="settings-box">
-        <div class="start">[[item.pathDisplayText]]</div>
-        <paper-icon-button-light class="icon-clear">
-          <button on-click="onRemoveSharedPathTap_"></button>
-        </paper-icon-button-light>
-      </div>
-    </template>
+    <div class="settings-box continuation">
+      <h2 class="start">$i18n{crostiniSharedPathsListHeading}</h2>
+    </div>
+    <div class="list-frame vertical-list">
+      <template is="dom-repeat" items="[[sharedPaths_]]">
+        <div class="list-item">
+          <div class="start">[[item.pathDisplayText]]</div>
+          <paper-icon-button-light class="icon-clear">
+            <button on-click="onRemoveSharedPathTap_"
+                    title="$i18n{crostiniSharedPathsRemoveSharing}">
+            </button>
+          </paper-icon-button-light>
+        </div>
+      </template>
+    </div>
   </template>
   <script src="crostini_shared_paths.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 8110baa..ea6d6f7 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -1231,12 +1231,6 @@
                    type="chrome_html"
                    preprocess="true"
                    allowexternalscript="true" />
-        <structure name="IDR_SETTINGS_ANDROID_SETTINGS_ELEMENT_HTML"
-                   file="android_apps_page/android_settings_element.html"
-                   type="chrome_html" />
-        <structure name="IDR_SETTINGS_ANDROID_SETTINGS_ELEMENT_JS"
-                   file="android_apps_page/android_settings_element.js"
-                   type="chrome_html" />
         <structure name="IDR_SETTINGS_CROSTINI_PAGE_HTML"
                    file="crostini_page/crostini_page.html"
                    type="chrome_html" />
diff --git a/chrome/browser/resources/settings/site_settings/cookie_info.js b/chrome/browser/resources/settings/site_settings/cookie_info.js
index bf563a3..742ddb48 100644
--- a/chrome/browser/resources/settings/site_settings/cookie_info.js
+++ b/chrome/browser/resources/settings/site_settings/cookie_info.js
@@ -38,7 +38,7 @@
     ['created', 'cookieCreated'], ['accessed', 'cookieLastAccessed']
   ],
   'database': [
-    ['name', 'cookieName'], ['desc', 'webdbDesc'], ['size', 'localStorageSize'],
+    ['origin', 'databaseOrigin'], ['size', 'localStorageSize'],
     ['modified', 'localStorageLastModified']
   ],
   'local_storage': [
@@ -57,9 +57,8 @@
     ['serverId', 'channelIdServerId'], ['certType', 'channelIdType'],
     ['created', 'channelIdCreated']
   ],
-  'service_worker': [
-    ['origin', 'serviceWorkerOrigin'], ['size', 'serviceWorkerSize']
-  ],
+  'service_worker':
+      [['origin', 'serviceWorkerOrigin'], ['size', 'serviceWorkerSize']],
   'shared_worker':
       [['worker', 'sharedWorkerWorker'], ['name', 'sharedWorkerName']],
   'cache_storage': [
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.cc
index f547948..59e295a 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win.cc
@@ -103,7 +103,7 @@
   cleaner_command_line_.AppendSwitchASCII(chrome_cleaner::kChromeVersionSwitch,
                                           version_info::GetVersionNumber());
   cleaner_command_line_.AppendSwitchASCII(chrome_cleaner::kChromeChannelSwitch,
-                                          base::IntToString(ChannelAsInt()));
+                                          base::NumberToString(ChannelAsInt()));
   base::FilePath chrome_exe_path;
   base::PathService::Get(base::FILE_EXE, &chrome_exe_path);
   cleaner_command_line_.AppendSwitchPath(chrome_cleaner::kChromeExePathSwitch,
@@ -115,7 +115,7 @@
   // Start the cleaner process in scanning mode.
   cleaner_command_line_.AppendSwitchASCII(
       chrome_cleaner::kExecutionModeSwitch,
-      base::IntToString(
+      base::NumberToString(
           static_cast<int>(chrome_cleaner::ExecutionMode::kScanning)));
 
   // If set, forward the engine flag from the reporter. Otherwise, set the
@@ -134,7 +134,8 @@
 
   cleaner_command_line_.AppendSwitchASCII(
       chrome_cleaner::kChromePromptSwitch,
-      base::IntToString(static_cast<int>(reporter_invocation.chrome_prompt())));
+      base::NumberToString(
+          static_cast<int>(reporter_invocation.chrome_prompt())));
 
   // If metrics is enabled, we can enable crash reporting in the Chrome Cleaner
   // process.
@@ -150,7 +151,7 @@
         chrome_cleaner::kSRTPromptFieldTrialGroupNameSwitch, group_name);
   }
 
-  std::string reboot_prompt_type = base::IntToString(GetRebootPromptType());
+  std::string reboot_prompt_type = base::NumberToString(GetRebootPromptType());
   cleaner_command_line_.AppendSwitchASCII(
       chrome_cleaner::kRebootPromptMethodSwitch, reboot_prompt_type);
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
index a5430249..a7ab2ec 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/chrome_cleaner_runner_win_unittest.cc
@@ -189,7 +189,7 @@
 
   EXPECT_EQ(
       command_line_.GetSwitchValueASCII(chrome_cleaner::kExecutionModeSwitch),
-      base::IntToString(
+      base::NumberToString(
           static_cast<int>(chrome_cleaner::ExecutionMode::kScanning)));
 
   // Ensure that the engine flag is always set and that it correctly reflects
@@ -215,7 +215,7 @@
       command_line_.HasSwitch(chrome_cleaner::kWithScanningModeLogsSwitch));
   EXPECT_EQ(
       command_line_.GetSwitchValueASCII(chrome_cleaner::kChromePromptSwitch),
-      base::IntToString(static_cast<int>(chrome_prompt_)));
+      base::NumberToString(static_cast<int>(chrome_prompt_)));
 
   const std::string reboot_prompt_method = command_line_.GetSwitchValueASCII(
       chrome_cleaner::kRebootPromptMethodSwitch);
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
index 95eb0e9e..5d194f4 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/mock_chrome_cleaner_process_win.cc
@@ -202,20 +202,21 @@
 
   if (crash_point() != CrashPoint::kNone) {
     command_line->AppendSwitchASCII(
-        kCrashPointSwitch, base::IntToString(static_cast<int>(crash_point())));
+        kCrashPointSwitch,
+        base::NumberToString(static_cast<int>(crash_point())));
   }
 
   command_line->AppendSwitchASCII(
       kRegistryKeysReportingSwitch,
-      base::IntToString(static_cast<int>(registry_keys_reporting())));
+      base::NumberToString(static_cast<int>(registry_keys_reporting())));
   command_line->AppendSwitchASCII(
       kExtensionsReportingSwitch,
-      base::IntToString(static_cast<int>(extensions_reporting())));
+      base::NumberToString(static_cast<int>(extensions_reporting())));
 
   if (expected_user_response() != PromptAcceptance::UNSPECIFIED) {
     command_line->AppendSwitchASCII(
         kExpectedUserResponseSwitch,
-        base::IntToString(static_cast<int>(expected_user_response())));
+        base::NumberToString(static_cast<int>(expected_user_response())));
   }
 }
 
diff --git a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.cc b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.cc
index d81a5e8..4847e79 100644
--- a/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.cc
+++ b/chrome/browser/safe_browsing/chrome_cleaner/reporter_runner_win.cc
@@ -958,7 +958,7 @@
         chrome_cleaner::kChromeVersionSwitch, version_info::GetVersionNumber());
     invocation->mutable_command_line().AppendSwitchNative(
         chrome_cleaner::kChromeChannelSwitch,
-        base::IntToString16(ChannelAsInt()));
+        base::NumberToString16(ChannelAsInt()));
   }
 
   void SendResultAndDeleteSelf(SwReporterInvocationResult result) {
diff --git a/chrome/browser/safe_browsing/chrome_password_protection_service.cc b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
index 103f74f8..0e174a6 100644
--- a/chrome/browser/safe_browsing/chrome_password_protection_service.cc
+++ b/chrome/browser/safe_browsing/chrome_password_protection_service.cc
@@ -431,7 +431,7 @@
     update->SetKey(
         Origin::Create(web_contents->GetLastCommittedURL()).Serialize(),
         base::Value(
-            base::Int64ToString(GetLastCommittedNavigationID(web_contents))));
+            base::NumberToString(GetLastCommittedNavigationID(web_contents))));
   }
 
   UpdateSecurityState(SB_THREAT_TYPE_SIGN_IN_PASSWORD_REUSE,
diff --git a/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
index 1319e4fb..982e83e 100644
--- a/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
+++ b/chrome/browser/safe_browsing/client_side_model_loader_unittest.cc
@@ -83,7 +83,7 @@
 
     std::map<std::string, std::string> params;
     params[ModelLoader::kClientModelFinchParam] =
-        base::IntToString(model_number);
+        base::NumberToString(model_number);
 
     ASSERT_TRUE(variations::AssociateVariationParams(
         ModelLoader::kClientModelFinchExperiment, group_name, params));
diff --git a/chrome/browser/safe_browsing/download_protection/download_feedback_service_unittest.cc b/chrome/browser/safe_browsing/download_protection/download_feedback_service_unittest.cc
index 0e30cfc..b5addb3 100644
--- a/chrome/browser/safe_browsing/download_protection/download_feedback_service_unittest.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_feedback_service_unittest.cc
@@ -133,8 +133,8 @@
   void TearDown() override { DownloadFeedback::RegisterFactory(nullptr); }
 
   base::FilePath CreateTestFile(int n) const {
-    base::FilePath upload_file_path(
-        temp_dir_.GetPath().AppendASCII("test file " + base::IntToString(n)));
+    base::FilePath upload_file_path(temp_dir_.GetPath().AppendASCII(
+        "test file " + base::NumberToString(n)));
     const std::string upload_file_data = "data";
     int wrote = base::WriteFile(upload_file_path, upload_file_data.data(),
                                 upload_file_data.size());
diff --git a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
index 36d68cc0..fc87da9 100644
--- a/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
+++ b/chrome/browser/safe_browsing/download_protection/download_protection_service.cc
@@ -289,7 +289,7 @@
       learn_more_url, g_browser_process->GetApplicationLocale());
   learn_more_url = net::AppendQueryParameter(
       learn_more_url, "ctx",
-      base::IntToString(static_cast<int>(item.GetDangerType())));
+      base::NumberToString(static_cast<int>(item.GetDangerType())));
   navigator->OpenURL(
       content::OpenURLParams(learn_more_url, content::Referrer(),
                              WindowOpenDisposition::NEW_FOREGROUND_TAB,
diff --git a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
index 276f8e1..2fae08c6 100644
--- a/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/extension_data_collection_unittest.cc
@@ -100,7 +100,7 @@
   extension_prefs_->UpdateExtensionPref(
       extension_id, "install_time",
       std::make_unique<base::Value>(
-          base::Int64ToString(install_time.ToInternalValue())));
+          base::NumberToString(install_time.ToInternalValue())));
   extension_prefs_->UpdateExtensionPref(
       extension_id, "state", std::make_unique<base::Value>(state_value));
 }
@@ -153,7 +153,7 @@
   std::unique_ptr<ExtensionTestingProfile> CreateProfile(
       SafeBrowsingDisposition safe_browsing_opt_in) {
     std::string profile_name("profile");
-    profile_name.append(base::IntToString(++profile_number_));
+    profile_name.append(base::NumberToString(++profile_number_));
 
     // Create prefs for the profile and safe browsing preferences accordingly.
     std::unique_ptr<sync_preferences::TestingPrefServiceSyncable> prefs(
diff --git a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
index 424cf3f..c5a7a2c1 100644
--- a/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/incident_reporting_service_unittest.cc
@@ -1299,10 +1299,10 @@
 // Test that a profile's prune state is properly cleaned upon load.
 TEST_F(IncidentReportingServiceTest, CleanLegacyPruneState) {
   CreateIncidentReportingService();
-  const std::string blacklist_load_type(base::IntToString(static_cast<int32_t>(
-      safe_browsing::IncidentType::OBSOLETE_BLACKLIST_LOAD)));
-  const std::string preference_type(base::IntToString(
-      static_cast<int32_t>(safe_browsing::IncidentType::TRACKED_PREFERENCE)));
+  const std::string blacklist_load_type(base::NumberToString(
+      static_cast<int>(safe_browsing::IncidentType::OBSOLETE_BLACKLIST_LOAD)));
+  const std::string preference_type(base::NumberToString(
+      static_cast<int>(safe_browsing::IncidentType::TRACKED_PREFERENCE)));
 
   // Set up a prune state dict with data to be cleared (and not).
   std::unique_ptr<base::DictionaryValue> incidents_sent(
diff --git a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
index 1361ac6..5cce36da 100644
--- a/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/last_download_finder_unittest.cc
@@ -166,7 +166,7 @@
 
   TestingProfile* CreateProfile(SafeBrowsingDisposition safe_browsing_opt_in) {
     std::string profile_name("profile");
-    profile_name.append(base::IntToString(++profile_number_));
+    profile_name.append(base::NumberToString(++profile_number_));
 
     // Set up keyed service factories.
     TestingProfile::TestingFactories factories;
diff --git a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
index a5469cf..538a473 100644
--- a/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/platform_state_store.cc
@@ -96,7 +96,7 @@
     if (!key_digest.has_key() || !key_digest.has_digest())
       continue;
     type_dict->SetKey(key_digest.key(),
-                      base::Value(base::UintToString(key_digest.digest())));
+                      base::Value(base::NumberToString(key_digest.digest())));
   }
 }
 
@@ -110,7 +110,7 @@
         type_incidents.incidents().key_to_digest_size() == 0) {
       continue;
     }
-    std::string type_string(base::IntToString(type_incidents.type()));
+    std::string type_string(base::NumberToString(type_incidents.type()));
     base::Value* type_dict =
         value_dict->FindKeyOfType(type_string, base::Value::Type::DICTIONARY);
     if (!type_dict) {
diff --git a/chrome/browser/safe_browsing/incident_reporting/state_store.cc b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
index c86469a..32c0a5f6 100644
--- a/chrome/browser/safe_browsing/incident_reporting/state_store.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/state_store.cc
@@ -37,7 +37,7 @@
 void StateStore::Transaction::MarkAsReported(IncidentType type,
                                              const std::string& key,
                                              IncidentDigest digest) {
-  std::string type_string(base::IntToString(static_cast<int32_t>(type)));
+  std::string type_string(base::NumberToString(static_cast<int>(type)));
   base::DictionaryValue* incidents_sent = GetPrefDict();
   base::Value* type_dict =
       incidents_sent->FindKeyOfType(type_string, base::Value::Type::DICTIONARY);
@@ -45,7 +45,7 @@
     type_dict = incidents_sent->SetKey(
         type_string, base::Value(base::Value::Type::DICTIONARY));
   }
-  type_dict->SetKey(key, base::Value(base::UintToString(digest)));
+  type_dict->SetKey(key, base::Value(base::NumberToString(digest)));
 }
 
 void StateStore::Transaction::Clear(IncidentType type, const std::string& key) {
@@ -57,7 +57,7 @@
   // to remove before committing to making a change since any use of GetPrefDict
   // will result in a full serialize-and-write operation on the preferences
   // store.
-  std::string type_string(base::IntToString(static_cast<int32_t>(type)));
+  std::string type_string(base::NumberToString(static_cast<int>(type)));
   const base::DictionaryValue* const_type_dict = nullptr;
   if (store_->incidents_sent_->GetDictionaryWithoutPathExpansion(
           type_string, &const_type_dict) &&
@@ -77,7 +77,7 @@
   // to remove before committing to making a change since any use of GetPrefDict
   // will result in a full serialize-and-write operation on the preferences
   // store.
-  std::string type_string(base::IntToString(static_cast<int32_t>(type)));
+  std::string type_string(base::NumberToString(static_cast<int>(type)));
   const base::DictionaryValue* type_dict = nullptr;
   if (store_->incidents_sent_->GetDictionaryWithoutPathExpansion(type_string,
                                                                  &type_dict)) {
@@ -160,9 +160,9 @@
   std::string digest_string;
   return (incidents_sent_ &&
           incidents_sent_->GetDictionaryWithoutPathExpansion(
-              base::IntToString(static_cast<int32_t>(type)), &type_dict) &&
+              base::NumberToString(static_cast<int>(type)), &type_dict) &&
           type_dict->GetStringWithoutPathExpansion(key, &digest_string) &&
-          digest_string == base::UintToString(digest));
+          digest_string == base::NumberToString(digest));
 }
 
 void StateStore::CleanLegacyValues(Transaction* transaction) {
diff --git a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
index 798e575..db94cfb 100644
--- a/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
+++ b/chrome/browser/safe_browsing/safe_browsing_blocking_page_test.cc
@@ -492,7 +492,7 @@
     }
     EXPECT_EQ(SSLBlockingPage::kTypeForTesting,
               ssl_blocking_page->GetTypeForTesting());
-    ssl_blocking_page->CommandReceived(base::IntToString(
+    ssl_blocking_page->CommandReceived(base::NumberToString(
         security_interstitials::SecurityInterstitialCommand::CMD_PROCEED));
     if (base::FeatureList::IsEnabled(features::kSSLCommittedInterstitials))
       EXPECT_TRUE(WaitForRenderFrameReady(contents->GetMainFrame()));
@@ -558,7 +558,7 @@
     ASSERT_TRUE(interstitial_page);
     ASSERT_EQ(SafeBrowsingBlockingPage::kTypeForTesting,
               interstitial_page->GetTypeForTesting());
-    interstitial_page->CommandReceived(base::IntToString(command));
+    interstitial_page->CommandReceived(base::NumberToString(command));
   }
 
   void AssertNoInterstitial(bool wait_for_delete) {
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
index cb00066..f880d939 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
+++ b/chrome/browser/search/one_google_bar/one_google_bar_loader_impl.cc
@@ -147,7 +147,7 @@
   void Start();
 
  private:
-  net::HttpRequestHeaders GetRequestHeaders() const;
+  void SetRequestHeaders(network::ResourceRequest* request) const;
 
   void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
 
@@ -176,11 +176,10 @@
       callback_(std::move(callback)) {
 }
 
-net::HttpRequestHeaders
-OneGoogleBarLoaderImpl::AuthenticatedURLLoader::GetRequestHeaders() const {
-  net::HttpRequestHeaders headers;
-  variations::AppendVariationHeadersUnknownSignedIn(
-      api_url_, variations::InIncognito::kNo, &headers);
+void OneGoogleBarLoaderImpl::AuthenticatedURLLoader::SetRequestHeaders(
+    network::ResourceRequest* request) const {
+  variations::AppendVariationsHeaderUnknownSignedIn(
+      api_url_, variations::InIncognito::kNo, request);
 #if defined(OS_CHROMEOS)
   signin::ChromeConnectedHeaderHelper chrome_connected_header_helper(
       account_consistency_mirror_required_
@@ -200,11 +199,10 @@
           // Account ID is only needed for (drive|docs).google.com.
           /*account_id=*/std::string(), profile_mode);
   if (!chrome_connected_header_value.empty()) {
-    headers.SetHeader(signin::kChromeConnectedHeader,
-                      chrome_connected_header_value);
+    request->headers.SetHeader(signin::kChromeConnectedHeader,
+                               chrome_connected_header_value);
   }
 #endif
-  return headers;
 }
 
 void OneGoogleBarLoaderImpl::AuthenticatedURLLoader::Start() {
@@ -236,7 +234,7 @@
   auto resource_request = std::make_unique<network::ResourceRequest>();
   resource_request->url = api_url_;
   resource_request->load_flags = net::LOAD_DO_NOT_SEND_AUTH_DATA;
-  resource_request->headers = GetRequestHeaders();
+  SetRequestHeaders(resource_request.get());
   resource_request->request_initiator =
       url::Origin::Create(GURL(chrome::kChromeUINewTabURL));
 
diff --git a/chrome/browser/search/one_google_bar/one_google_bar_service_unittest.cc b/chrome/browser/search/one_google_bar/one_google_bar_service_unittest.cc
index f3bcc39f..10628c9 100644
--- a/chrome/browser/search/one_google_bar/one_google_bar_service_unittest.cc
+++ b/chrome/browser/search/one_google_bar/one_google_bar_service_unittest.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/search/one_google_bar/one_google_bar_data.h"
 #include "chrome/browser/search/one_google_bar/one_google_bar_loader.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -66,9 +67,7 @@
     identity_env_.SetCookieAccounts({{account_info.email, account_info.gaia}});
   }
 
-  void SignOut() {
-    identity_env_.SetCookieAccounts({});
-  }
+  void SignOut() { identity_env_.SetCookieAccounts({}); }
 
  private:
   base::test::ScopedTaskEnvironment task_environment_;
diff --git a/chrome/browser/search/search_suggest/search_suggest_loader_impl.cc b/chrome/browser/search/search_suggest/search_suggest_loader_impl.cc
index 8067939..f2edf04 100644
--- a/chrome/browser/search/search_suggest/search_suggest_loader_impl.cc
+++ b/chrome/browser/search/search_suggest/search_suggest_loader_impl.cc
@@ -122,8 +122,6 @@
   void Start();
 
  private:
-  net::HttpRequestHeaders GetRequestHeaders() const;
-
   void OnURLLoaderComplete(std::unique_ptr<std::string> response_body);
 
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
@@ -143,14 +141,6 @@
       api_url_(std::move(api_url)),
       callback_(std::move(callback)) {}
 
-net::HttpRequestHeaders
-SearchSuggestLoaderImpl::AuthenticatedURLLoader::GetRequestHeaders() const {
-  net::HttpRequestHeaders headers;
-  variations::AppendVariationHeadersUnknownSignedIn(
-      api_url_, variations::InIncognito::kNo, &headers);
-  return headers;
-}
-
 void SearchSuggestLoaderImpl::AuthenticatedURLLoader::Start() {
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("search_suggest_service", R"(
@@ -182,7 +172,8 @@
 
   auto resource_request = std::make_unique<network::ResourceRequest>();
   resource_request->url = api_url_;
-  resource_request->headers = GetRequestHeaders();
+  variations::AppendVariationsHeaderUnknownSignedIn(
+      api_url_, variations::InIncognito::kNo, resource_request.get());
   resource_request->request_initiator =
       url::Origin::Create(GURL(chrome::kChromeUINewTabURL));
 
diff --git a/chrome/browser/search_engines/template_url_scraper_browsertest.cc b/chrome/browser/search_engines/template_url_scraper_browsertest.cc
index f84d217..34816e350 100644
--- a/chrome/browser/search_engines/template_url_scraper_browsertest.cc
+++ b/chrome/browser/search_engines/template_url_scraper_browsertest.cc
@@ -92,7 +92,7 @@
 
   EXPECT_EQ(prepopulate_urls.size(), all_urls.size());
 
-  std::string port(base::IntToString(embedded_test_server()->port()));
+  std::string port(base::NumberToString(embedded_test_server()->port()));
   ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
       browser(), GURL("http://www.foo.com:" + port + "/"), 1);
 
diff --git a/chrome/browser/sessions/restore_on_startup_policy_handler.cc b/chrome/browser/sessions/restore_on_startup_policy_handler.cc
index 56a2fe3..28b03d7 100644
--- a/chrome/browser/sessions/restore_on_startup_policy_handler.cc
+++ b/chrome/browser/sessions/restore_on_startup_policy_handler.cc
@@ -71,9 +71,8 @@
         // No error
         break;
       default:
-        errors->AddError(policy_name(),
-                         IDS_POLICY_OUT_OF_RANGE_ERROR,
-                         base::IntToString(restore_value));
+        errors->AddError(policy_name(), IDS_POLICY_OUT_OF_RANGE_ERROR,
+                         base::NumberToString(restore_value));
     }
   }
   return true;
diff --git a/chrome/browser/sessions/restore_on_startup_policy_handler_unittest.cc b/chrome/browser/sessions/restore_on_startup_policy_handler_unittest.cc
index 884a5e66..e7a7f7a 100644
--- a/chrome/browser/sessions/restore_on_startup_policy_handler_unittest.cc
+++ b/chrome/browser/sessions/restore_on_startup_policy_handler_unittest.cc
@@ -82,7 +82,7 @@
   ASSERT_EQ(1U, errors().size());
   EXPECT_EQ(l10n_util::GetStringFUTF16(
                 IDS_POLICY_OUT_OF_RANGE_ERROR,
-                base::ASCIIToUTF16(base::IntToString(impossible_value))),
+                base::ASCIIToUTF16(base::NumberToString(impossible_value))),
             errors().begin()->second);
 }
 
diff --git a/chrome/browser/sessions/session_service_unittest.cc b/chrome/browser/sessions/session_service_unittest.cc
index ac40565..17b823b2 100644
--- a/chrome/browser/sessions/session_service_unittest.cc
+++ b/chrome/browser/sessions/session_service_unittest.cc
@@ -53,7 +53,7 @@
   void SetUp() override {
     BrowserWithTestWindowTest::SetUp();
 
-    std::string b = base::Int64ToString(base::Time::Now().ToInternalValue());
+    std::string b = base::NumberToString(base::Time::Now().ToInternalValue());
     TestingProfile* profile = profile_manager()->CreateTestingProfile(b);
     SessionService* session_service = new SessionService(profile);
     path_ = profile->GetPath();
@@ -619,7 +619,7 @@
   for (int i = 0; i < 5; ++i) {
     SerializedNavigationEntry nav =
         SerializedNavigationEntryTestHelper::CreateNavigation(
-            base_url + base::IntToString(i), "a");
+            base_url + base::NumberToString(i), "a");
     nav.set_index(i);
     UpdateNavigation(window_id, tab_id, nav, (i == 3));
   }
@@ -643,11 +643,11 @@
   sessions::SessionTab* tab = windows[0]->tabs[0].get();
   ASSERT_EQ(1, tab->current_navigation_index);
   EXPECT_EQ(3U, tab->navigations.size());
-  EXPECT_TRUE(GURL(base_url + base::IntToString(2)) ==
+  EXPECT_TRUE(GURL(base_url + base::NumberToString(2)) ==
               tab->navigations[0].virtual_url());
-  EXPECT_TRUE(GURL(base_url + base::IntToString(3)) ==
+  EXPECT_TRUE(GURL(base_url + base::NumberToString(3)) ==
               tab->navigations[1].virtual_url());
-  EXPECT_TRUE(GURL(base_url + base::IntToString(4)) ==
+  EXPECT_TRUE(GURL(base_url + base::NumberToString(4)) ==
               tab->navigations[2].virtual_url());
 }
 
@@ -662,7 +662,7 @@
   for (int i = 0; i < 5; ++i) {
     SerializedNavigationEntry nav =
         SerializedNavigationEntryTestHelper::CreateNavigation(
-            base_url + base::IntToString(i), "a");
+            base_url + base::NumberToString(i), "a");
     nav.set_index(i);
     UpdateNavigation(window_id, tab_id, nav, (i == 3));
   }
@@ -859,7 +859,7 @@
   for (int i = 0; i < 5; ++i) {
     SerializedNavigationEntry nav =
         SerializedNavigationEntryTestHelper::CreateNavigation(
-            base_url + base::IntToString(i), "a");
+            base_url + base::NumberToString(i), "a");
     nav.set_index(i / 2);
     UpdateNavigation(window_id, tab_id, nav, true);
   }
@@ -872,11 +872,11 @@
   ASSERT_EQ(1U, windows.size());
   ASSERT_EQ(1U, windows[0]->tabs.size());
   EXPECT_EQ(3U, windows[0]->tabs[0]->navigations.size());
-  EXPECT_EQ(GURL(base_url + base::IntToString(1)),
+  EXPECT_EQ(GURL(base_url + base::NumberToString(1)),
             windows[0]->tabs[0]->navigations[0].virtual_url());
-  EXPECT_EQ(GURL(base_url + base::IntToString(3)),
+  EXPECT_EQ(GURL(base_url + base::NumberToString(3)),
             windows[0]->tabs[0]->navigations[1].virtual_url());
-  EXPECT_EQ(GURL(base_url + base::IntToString(4)),
+  EXPECT_EQ(GURL(base_url + base::NumberToString(4)),
             windows[0]->tabs[0]->navigations[2].virtual_url());
 }
 
@@ -889,7 +889,7 @@
   for (int i = 0; i < 5; ++i) {
     SerializedNavigationEntry nav =
         SerializedNavigationEntryTestHelper::CreateNavigation(
-            base_url + base::IntToString(i), "a");
+            base_url + base::NumberToString(i), "a");
     nav.set_index(i);
     UpdateNavigation(window_id, tab_id, nav, true);
   }
@@ -900,7 +900,7 @@
   // Add another navigation to replace the last one.
   SerializedNavigationEntry nav =
       SerializedNavigationEntryTestHelper::CreateNavigation(
-        base_url + base::IntToString(5), "a");
+          base_url + base::NumberToString(5), "a");
   nav.set_index(4);
   UpdateNavigation(window_id, tab_id, nav, true);
 
@@ -913,7 +913,7 @@
   ASSERT_EQ(1U, windows.size());
   ASSERT_EQ(1U, windows[0]->tabs.size());
   ASSERT_EQ(1U, windows[0]->tabs[0]->navigations.size());
-  EXPECT_EQ(GURL(base_url + base::IntToString(5)),
+  EXPECT_EQ(GURL(base_url + base::NumberToString(5)),
             windows[0]->tabs[0]->navigations[0].virtual_url());
 }
 
diff --git a/chrome/browser/shell_integration_win_unittest.cc b/chrome/browser/shell_integration_win_unittest.cc
index d19f328..c157b168 100644
--- a/chrome/browser/shell_integration_win_unittest.cc
+++ b/chrome/browser/shell_integration_win_unittest.cc
@@ -84,7 +84,7 @@
       base::win::ShortcutProperties* shortcut_properties) {
     ShortcutTestObject shortcut_test_object;
     base::FilePath shortcut_path = temp_dir_.GetPath().Append(
-        L"Shortcut " + base::IntToString16(shortcuts_.size()) +
+        L"Shortcut " + base::NumberToString16(shortcuts_.size()) +
         installer::kLnkExt);
     shortcut_test_object.path = shortcut_path;
     shortcut_test_object.properties = *shortcut_properties;
diff --git a/chrome/browser/signin/dice_response_handler_unittest.cc b/chrome/browser/signin/dice_response_handler_unittest.cc
index 970555f..8db0eb67 100644
--- a/chrome/browser/signin/dice_response_handler_unittest.cc
+++ b/chrome/browser/signin/dice_response_handler_unittest.cc
@@ -22,7 +22,6 @@
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/dice_account_reconcilor_delegate.h"
 #include "components/signin/core/browser/fake_account_fetcher_service.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/fake_signin_manager.h"
 #include "components/signin/core/browser/mutable_profile_oauth2_token_service_delegate.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
diff --git a/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc b/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc
deleted file mode 100644
index bad98c8a..0000000
--- a/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc
+++ /dev/null
@@ -1,39 +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/signin/fake_gaia_cookie_manager_service_builder.h"
-
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/chrome_signin_client_factory.h"
-#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-
-std::unique_ptr<KeyedService> BuildGaiaCookieManagerServiceWithURLLoader(
-    network::TestURLLoaderFactory* test_url_loader_factory,
-    content::BrowserContext* context) {
-  Profile* profile = Profile::FromBrowserContext(context);
-  return std::make_unique<GaiaCookieManagerService>(
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
-      ChromeSigninClientFactory::GetForProfile(profile),
-      base::BindRepeating(
-          [](network::TestURLLoaderFactory* test_url_loader_factory)
-              -> scoped_refptr<network::SharedURLLoaderFactory> {
-            return test_url_loader_factory->GetSafeWeakWrapper();
-          },
-          test_url_loader_factory));
-}
-
-std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerServiceWithURLLoader(
-    network::TestURLLoaderFactory* test_url_loader_factory,
-    content::BrowserContext* context) {
-  Profile* profile = Profile::FromBrowserContext(context);
-  return std::make_unique<FakeGaiaCookieManagerService>(
-      ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
-      ChromeSigninClientFactory::GetForProfile(profile),
-      test_url_loader_factory);
-}
diff --git a/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h b/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h
deleted file mode 100644
index a855490..0000000
--- a/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h
+++ /dev/null
@@ -1,39 +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_SIGNIN_FAKE_GAIA_COOKIE_MANAGER_SERVICE_BUILDER_H_
-#define CHROME_BROWSER_SIGNIN_FAKE_GAIA_COOKIE_MANAGER_SERVICE_BUILDER_H_
-
-#include <memory>
-
-class KeyedService;
-
-namespace content {
-class BrowserContext;
-}
-
-namespace network {
-class TestURLLoaderFactory;
-}
-
-// Creates a GaiaCookieManagerService using the supplied
-// |test_url_loader_factory| and |context|.
-std::unique_ptr<KeyedService> BuildGaiaCookieManagerServiceWithURLLoader(
-    network::TestURLLoaderFactory* test_url_loader_factory,
-    content::BrowserContext* context);
-
-// Builds a FakeGaiaCookieManagerService which uses the provided
-// |test_url_loader_factory| for cookie-related requests.
-//
-// TODO(https://crbug.com/907782): Convert all test code to use
-// GaiaCookieManagerService directly, passing a TestURLLoaderFactory when
-// fakes are needed.
-//
-// Once that's done, the method below can be deleted, and this file can be
-// renamed to something like gaia_cookie_manager_service_test_util.cc
-std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerServiceWithURLLoader(
-    network::TestURLLoaderFactory* test_url_loader_factory,
-    content::BrowserContext* context);
-
-#endif  // CHROME_BROWSER_SIGNIN_FAKE_GAIA_COOKIE_MANAGER_SERVICE_BUILDER_H_
diff --git a/chrome/browser/signin/fake_signin_manager_builder.cc b/chrome/browser/signin/fake_signin_manager_builder.cc
index bfd95f6..0de48a55 100644
--- a/chrome/browser/signin/fake_signin_manager_builder.cc
+++ b/chrome/browser/signin/fake_signin_manager_builder.cc
@@ -20,8 +20,6 @@
   Profile* profile = static_cast<Profile*>(context);
   manager.reset(new FakeSigninManagerForTesting(profile));
   manager->Initialize(nullptr);
-  SigninManagerFactory::GetInstance()
-      ->NotifyObserversOfSigninManagerCreationForTesting(manager.get());
   return std::move(manager);
 }
 
diff --git a/chrome/browser/signin/gaia_cookie_manager_service_test_util.cc b/chrome/browser/signin/gaia_cookie_manager_service_test_util.cc
new file mode 100644
index 0000000..c36d118b
--- /dev/null
+++ b/chrome/browser/signin/gaia_cookie_manager_service_test_util.cc
@@ -0,0 +1,29 @@
+// 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/signin/gaia_cookie_manager_service_test_util.h"
+
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/chrome_signin_client_factory.h"
+#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/signin/core/browser/gaia_cookie_manager_service.h"
+#include "components/signin/core/browser/profile_oauth2_token_service.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "services/network/test/test_url_loader_factory.h"
+
+std::unique_ptr<KeyedService> BuildGaiaCookieManagerServiceWithURLLoader(
+    network::TestURLLoaderFactory* test_url_loader_factory,
+    content::BrowserContext* context) {
+  Profile* profile = Profile::FromBrowserContext(context);
+  return std::make_unique<GaiaCookieManagerService>(
+      ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
+      ChromeSigninClientFactory::GetForProfile(profile),
+      base::BindRepeating(
+          [](network::TestURLLoaderFactory* test_url_loader_factory)
+              -> scoped_refptr<network::SharedURLLoaderFactory> {
+            return test_url_loader_factory->GetSafeWeakWrapper();
+          },
+          test_url_loader_factory));
+}
diff --git a/chrome/browser/signin/gaia_cookie_manager_service_test_util.h b/chrome/browser/signin/gaia_cookie_manager_service_test_util.h
new file mode 100644
index 0000000..1bbdac5
--- /dev/null
+++ b/chrome/browser/signin/gaia_cookie_manager_service_test_util.h
@@ -0,0 +1,39 @@
+// 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_SIGNIN_GAIA_COOKIE_MANAGER_SERVICE_TEST_UTIL_H_
+#define CHROME_BROWSER_SIGNIN_GAIA_COOKIE_MANAGER_SERVICE_TEST_UTIL_H_
+
+#include <memory>
+
+class KeyedService;
+
+namespace content {
+class BrowserContext;
+}
+
+namespace network {
+class TestURLLoaderFactory;
+}
+
+// Creates a GaiaCookieManagerService using the supplied
+// |test_url_loader_factory| and |context|.
+std::unique_ptr<KeyedService> BuildGaiaCookieManagerServiceWithURLLoader(
+    network::TestURLLoaderFactory* test_url_loader_factory,
+    content::BrowserContext* context);
+
+// Builds a FakeGaiaCookieManagerService which uses the provided
+// |test_url_loader_factory| for cookie-related requests.
+//
+// TODO(https://crbug.com/907782): Convert all test code to use
+// GaiaCookieManagerService directly, passing a TestURLLoaderFactory when
+// fakes are needed.
+//
+// Once that's done, the method below can be deleted, and this file can be
+// renamed to something like gaia_cookie_manager_service_test_util.cc
+std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerServiceWithURLLoader(
+    network::TestURLLoaderFactory* test_url_loader_factory,
+    content::BrowserContext* context);
+
+#endif  // CHROME_BROWSER_SIGNIN_FAKE_GAIA_COOKIE_MANAGER_SERVICE_BUILDER_H_
diff --git a/chrome/browser/signin/identity_manager_factory.h b/chrome/browser/signin/identity_manager_factory.h
index f674000..871024f 100644
--- a/chrome/browser/signin/identity_manager_factory.h
+++ b/chrome/browser/signin/identity_manager_factory.h
@@ -22,7 +22,7 @@
 // Profiles.
 class IdentityManagerFactory : public BrowserContextKeyedServiceFactory {
  public:
-  class Observer {
+  class Observer : public base::CheckedObserver {
    public:
     // Called when a IdentityManager instance is created.
     virtual void IdentityManagerCreated(
@@ -34,7 +34,7 @@
         identity::IdentityManager* identity_manager) {}
 
    protected:
-    virtual ~Observer() {}
+    ~Observer() override {}
   };
 
   static identity::IdentityManager* GetForProfile(Profile* profile);
@@ -70,7 +70,8 @@
   void BrowserContextShutdown(content::BrowserContext* profile) override;
 
   // List of observers. Checks that list is empty on destruction.
-  mutable base::ObserverList<Observer, true>::Unchecked observer_list_;
+  base::ObserverList<Observer, /*check_empty=*/true, /*allow_reentrancy=*/false>
+      observer_list_;
 
   DISALLOW_COPY_AND_ASSIGN(IdentityManagerFactory);
 };
diff --git a/chrome/browser/signin/identity_test_environment_profile_adaptor.cc b/chrome/browser/signin/identity_test_environment_profile_adaptor.cc
index fed033d..0c3782d 100644
--- a/chrome/browser/signin/identity_test_environment_profile_adaptor.cc
+++ b/chrome/browser/signin/identity_test_environment_profile_adaptor.cc
@@ -8,10 +8,10 @@
 #include "chrome/browser/signin/account_fetcher_service_factory.h"
 #include "chrome/browser/signin/account_tracker_service_factory.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
-#include "chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h"
 #include "chrome/browser/signin/fake_profile_oauth2_token_service_builder.h"
 #include "chrome/browser/signin/fake_signin_manager_builder.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/gaia_cookie_manager_service_test_util.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "chrome/browser/signin/signin_manager_factory.h"
diff --git a/chrome/browser/signin/signin_manager_factory.cc b/chrome/browser/signin/signin_manager_factory.cc
index 2f0ab7d0..f1712b2 100644
--- a/chrome/browser/signin/signin_manager_factory.cc
+++ b/chrome/browser/signin/signin_manager_factory.cc
@@ -91,20 +91,6 @@
   SigninManagerBase::RegisterPrefs(registry);
 }
 
-void SigninManagerFactory::AddObserver(Observer* observer) {
-  observer_list_.AddObserver(observer);
-}
-
-void SigninManagerFactory::RemoveObserver(Observer* observer) {
-  observer_list_.RemoveObserver(observer);
-}
-
-void SigninManagerFactory::NotifyObserversOfSigninManagerCreationForTesting(
-    SigninManagerBase* manager) {
-  for (Observer& observer : observer_list_)
-    observer.SigninManagerCreated(manager);
-}
-
 KeyedService* SigninManagerFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   SigninManagerBase* service = NULL;
@@ -124,18 +110,5 @@
 #endif
   AccountFetcherServiceFactory::GetForProfile(profile);
   service->Initialize(g_browser_process->local_state());
-  for (Observer& observer : observer_list_)
-    observer.SigninManagerCreated(service);
   return service;
 }
-
-void SigninManagerFactory::BrowserContextShutdown(
-    content::BrowserContext* context) {
-  SigninManagerBase* manager = static_cast<SigninManagerBase*>(
-      GetServiceForBrowserContext(context, false));
-  if (manager) {
-    for (Observer& observer : observer_list_)
-      observer.SigninManagerShutdown(manager);
-  }
-  BrowserContextKeyedServiceFactory::BrowserContextShutdown(context);
-}
diff --git a/chrome/browser/signin/signin_manager_factory.h b/chrome/browser/signin/signin_manager_factory.h
index 255e7c1..69a5e9b 100644
--- a/chrome/browser/signin/signin_manager_factory.h
+++ b/chrome/browser/signin/signin_manager_factory.h
@@ -6,7 +6,6 @@
 #define CHROME_BROWSER_SIGNIN_SIGNIN_MANAGER_FACTORY_H_
 
 #include "base/memory/singleton.h"
-#include "base/observer_list.h"
 #include "build/build_config.h"
 #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
 
@@ -20,19 +19,6 @@
 // the associated SigninManager.
 class SigninManagerFactory : public BrowserContextKeyedServiceFactory {
  public:
-  class Observer {
-   public:
-    // Called when a SigninManager(Base) instance is created.
-    virtual void SigninManagerCreated(SigninManagerBase* manager) {}
-
-    // Called when a SigninManager(Base) instance is being shut down. Observers
-    // of |manager| should remove themselves at this point.
-    virtual void SigninManagerShutdown(SigninManagerBase* manager) {}
-
-   protected:
-    virtual ~Observer() {}
-  };
-
 #if defined(OS_CHROMEOS)
   // Returns the instance of SigninManager associated with this profile
   // (creating one if none exists). Returns NULL if this profile cannot have a
@@ -63,29 +49,15 @@
   // Registers the browser-global prefs used by SigninManager.
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
-  // Methods to register or remove observers of SigninManager creation/shutdown.
-  void AddObserver(Observer* observer);
-  void RemoveObserver(Observer* observer);
-
-  // Notifies observers of |manager|'s creation. Should be called only by test
-  // SigninManager subclasses whose construction does not occur in
-  // |BuildServiceInstanceFor()|.
-  void NotifyObserversOfSigninManagerCreationForTesting(
-      SigninManagerBase* manager);
-
  private:
   friend struct base::DefaultSingletonTraits<SigninManagerFactory>;
 
   SigninManagerFactory();
   ~SigninManagerFactory() override;
 
-  // List of observers. Checks that list is empty on destruction.
-  mutable base::ObserverList<Observer, true>::Unchecked observer_list_;
-
   // BrowserContextKeyedServiceFactory:
   KeyedService* BuildServiceInstanceFor(
       content::BrowserContext* profile) const override;
-  void BrowserContextShutdown(content::BrowserContext* context) override;
 };
 
 #endif  // CHROME_BROWSER_SIGNIN_SIGNIN_MANAGER_FACTORY_H_
diff --git a/chrome/browser/signin/signin_promo.cc b/chrome/browser/signin/signin_promo.cc
index 793859909..2fd3022e1 100644
--- a/chrome/browser/signin/signin_promo.cc
+++ b/chrome/browser/signin/signin_promo.cc
@@ -150,7 +150,7 @@
 
   url = net::AppendQueryParameter(
       url, kSignInPromoQueryKeyAccessPoint,
-      base::IntToString(static_cast<int>(access_point)));
+      base::NumberToString(static_cast<int>(access_point)));
 
   // TODO(gogerald): right now, gaia server needs to distinguish the source from
   // signin_metrics::SOURCE_START_PAGE, signin_metrics::SOURCE_SETTINGS and
@@ -164,7 +164,7 @@
     source = signin_metrics::SOURCE_SETTINGS;
   }
   url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeySource,
-                                  base::IntToString(static_cast<int>(source)));
+                                  base::NumberToString(source));
   return GURL(url);
 }
 
@@ -191,9 +191,10 @@
   GURL url(chrome::kChromeUIChromeSigninURL);
   url = net::AppendQueryParameter(
       url, signin::kSignInPromoQueryKeyAccessPoint,
-      base::IntToString(static_cast<int>(access_point)));
-  url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyReason,
-                                  base::IntToString(static_cast<int>(reason)));
+      base::NumberToString(static_cast<int>(access_point)));
+  url =
+      net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyReason,
+                                base::NumberToString(static_cast<int>(reason)));
   if (auto_close) {
     url = net::AppendQueryParameter(url, signin::kSignInPromoQueryKeyAutoClose,
                                     "1");
diff --git a/chrome/browser/ssl/certificate_error_report.cc b/chrome/browser/ssl/certificate_error_report.cc
index 2d8e6d6f..fe7358cd 100644
--- a/chrome/browser/ssl/certificate_error_report.cc
+++ b/chrome/browser/ssl/certificate_error_report.cc
@@ -75,21 +75,24 @@
 }
 
 void AddVerifyFlagsToReport(
-    const net::CertVerifier::Config& config,
+    bool enable_rev_checking,
+    bool require_rev_checking_local_anchors,
+    bool enable_sha1_local_anchors,
+    bool disable_symantec_enforcement,
     ::google::protobuf::RepeatedField<int>* report_flags) {
-  if (config.enable_rev_checking) {
+  if (enable_rev_checking) {
     report_flags->Add(
         chrome_browser_ssl::TrialVerificationInfo::VERIFY_REV_CHECKING_ENABLED);
   }
-  if (config.require_rev_checking_local_anchors) {
+  if (require_rev_checking_local_anchors) {
     report_flags->Add(chrome_browser_ssl::TrialVerificationInfo::
                           VERIFY_REV_CHECKING_REQUIRED_LOCAL_ANCHORS);
   }
-  if (config.enable_sha1_local_anchors) {
+  if (enable_sha1_local_anchors) {
     report_flags->Add(chrome_browser_ssl::TrialVerificationInfo::
                           VERIFY_ENABLE_SHA1_LOCAL_ANCHORS);
   }
-  if (config.disable_symantec_enforcement) {
+  if (disable_symantec_enforcement) {
     report_flags->Add(chrome_browser_ssl::TrialVerificationInfo::
                           VERIFY_DISABLE_SYMANTEC_ENFORCEMENT);
   }
@@ -123,7 +126,10 @@
 CertificateErrorReport::CertificateErrorReport(
     const std::string& hostname,
     const net::X509Certificate& unverified_cert,
-    const net::CertVerifier::Config& verifier_config,
+    bool enable_rev_checking,
+    bool require_rev_checking_local_anchors,
+    bool enable_sha1_local_anchors,
+    bool disable_symantec_enforcement,
     const net::CertVerifyResult& primary_result,
     const net::CertVerifyResult& trial_result)
     : CertificateErrorReport(hostname,
@@ -145,7 +151,10 @@
                               trial_report->mutable_cert_error());
   AddCertStatusToReportStatus(trial_result.cert_status,
                               trial_report->mutable_cert_status());
-  AddVerifyFlagsToReport(verifier_config, trial_report->mutable_verify_flags());
+  AddVerifyFlagsToReport(
+      enable_rev_checking, require_rev_checking_local_anchors,
+      enable_sha1_local_anchors, disable_symantec_enforcement,
+      trial_report->mutable_verify_flags());
 }
 
 CertificateErrorReport::~CertificateErrorReport() {}
diff --git a/chrome/browser/ssl/certificate_error_report.h b/chrome/browser/ssl/certificate_error_report.h
index 2f92905..fa93efd 100644
--- a/chrome/browser/ssl/certificate_error_report.h
+++ b/chrome/browser/ssl/certificate_error_report.h
@@ -65,7 +65,10 @@
   // TODO(mattm): remove this when the trial is done. (https://crbug.com/649026)
   CertificateErrorReport(const std::string& hostname,
                          const net::X509Certificate& unverified_cert,
-                         const net::CertVerifier::Config& config,
+                         bool enable_rev_checking,
+                         bool require_rev_checking_local_anchors,
+                         bool enable_sha1_local_anchors,
+                         bool disable_symantec_enforcement,
                          const net::CertVerifyResult& primary_result,
                          const net::CertVerifyResult& trial_result);
 
diff --git a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
index b8b3b3b5..49efc646 100644
--- a/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
+++ b/chrome/browser/ssl/chrome_ssl_host_state_delegate.cc
@@ -195,7 +195,7 @@
       base::StringPiece(reinterpret_cast<const char*>(fingerprint.data),
                         sizeof(fingerprint.data)),
       &base64_fingerprint);
-  return base::UintToString(error) + base64_fingerprint;
+  return base::NumberToString(error) + base64_fingerprint;
 }
 
 void MigrateOldSettings(HostContentSettingsMap* map) {
@@ -619,7 +619,7 @@
     // values, only doubles. Since this mildly depends on precision, it is
     // better to store the value as a string.
     dict->SetString(kSSLCertDecisionExpirationTimeKey,
-                    base::Int64ToString(expiration_time.ToInternalValue()));
+                    base::NumberToString(expiration_time.ToInternalValue()));
   }
 
   // Extract the map of certificate fingerprints to errors from the setting.
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index ebce29b..cab4239 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -418,7 +418,7 @@
             tab);
     helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting()
         ->CommandReceived(
-            base::IntToString(security_interstitials::CMD_PROCEED));
+            base::NumberToString(security_interstitials::CMD_PROCEED));
     nav_observer.Wait();
   } else {
     content::InterstitialPage* interstitial_page = tab->GetInterstitialPage();
diff --git a/chrome/browser/ssl/ssl_blocking_page_unittest.cc b/chrome/browser/ssl/ssl_blocking_page_unittest.cc
index 1afb9b2c..51fc9904 100644
--- a/chrome/browser/ssl/ssl_blocking_page_unittest.cc
+++ b/chrome/browser/ssl/ssl_blocking_page_unittest.cc
@@ -77,7 +77,7 @@
 
   // Simulates a proceed action.
   blocking_page->CommandReceived(
-      base::IntToString(security_interstitials::CMD_PROCEED));
+      base::NumberToString(security_interstitials::CMD_PROCEED));
 
   // Verifies that security interstitial proceeded event is observed.
   observer.VerifyLatestSecurityInterstitialEvent(
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index 193b4d1..fd98866 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -100,6 +100,7 @@
 #include "components/ssl_errors/error_classification.h"
 #include "components/strings/grit/components_strings.h"
 #include "components/variations/variations_associated_data.h"
+#include "components/variations/variations_params_manager.h"
 #include "components/variations/variations_switches.h"
 #include "components/web_modal/web_contents_modal_dialog_manager.h"
 #include "content/public/browser/browser_context.h"
@@ -641,7 +642,7 @@
       WebContents* tab,
       security_interstitials::SecurityInterstitialCommand command) {
     tab->GetInterstitialPage()->GetDelegateForTesting()->CommandReceived(
-        base::IntToString(command));
+        base::NumberToString(command));
   }
 
   network::mojom::NetworkContextParamsPtr CreateDefaultNetworkContextParams() {
@@ -1253,6 +1254,16 @@
     // been called.
     CertReportHelper::SetFakeOfficialBuildForTesting();
   }
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SSLUITest::SetUpCommandLine(command_line);
+
+    // CertReportHelper::ShouldReportCertificateError checks the value of this
+    // variation. Ensure reporting is enabled.
+    variations::testing::VariationParamsManager::AppendVariationParams(
+        "ReportCertificateErrors", "ShowAndPossiblySend",
+        {{"sendingThreshold", "1.0"}}, command_line);
+  }
 };
 
 INSTANTIATE_TEST_SUITE_P(,
@@ -2769,18 +2780,10 @@
                      AuthState::DISPLAYED_FORM_WITH_INSECURE_ACTION);
 }
 
-// TODO(crbug.com/795820): Fails in Windows, Linux and Mac official builds.
-#if defined(OFFICIAL_BUILD)
-#define MAYBE_TestBrokenHTTPSReportingCloseTab \
-  DISABLED_TestBrokenHTTPSReportingCloseTab
-#else
-#define MAYBE_TestBrokenHTTPSReportingCloseTab TestBrokenHTTPSReportingCloseTab
-#endif
-
 // Test that a report is sent if the user closes the tab on an interstitial
 // before making a decision to proceed or go back.
 IN_PROC_BROWSER_TEST_P(SSLUITestWithExtendedReporting,
-                       MAYBE_TestBrokenHTTPSReportingCloseTab) {
+                       TestBrokenHTTPSReportingCloseTab) {
   ASSERT_TRUE(https_server_expired_.Start());
 
   base::RunLoop run_loop;
@@ -3945,7 +3948,7 @@
             tab);
     helper->GetBlockingPageForCurrentlyCommittedNavigationForTesting()
         ->CommandReceived(
-            base::IntToString(security_interstitials::CMD_PROCEED));
+            base::NumberToString(security_interstitials::CMD_PROCEED));
     nav_observer.Wait();
   } else {
     ProceedThroughInterstitial(tab);
diff --git a/chrome/browser/status_icons/desktop_notification_balloon.cc b/chrome/browser/status_icons/desktop_notification_balloon.cc
index 236061a0..306bfb3 100644
--- a/chrome/browser/status_icons/desktop_notification_balloon.cc
+++ b/chrome/browser/status_icons/desktop_notification_balloon.cc
@@ -44,7 +44,7 @@
   }
 
   const std::string notification_id =
-      kDesktopNotificationPrefix + base::IntToString(id_count_++);
+      kDesktopNotificationPrefix + base::NumberToString(id_count_++);
   message_center::Notification notification(
       message_center::NOTIFICATION_TYPE_SIMPLE, notification_id, title,
       contents, gfx::Image(icon), base::string16(), GURL(), notifier_id, {},
diff --git a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
index e346d50a..8ec97c6 100644
--- a/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
+++ b/chrome/browser/subresource_filter/ad_tagging_browsertest.cc
@@ -9,7 +9,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "chrome/browser/metrics/subprocess_metrics_provider.h"
-#include "chrome/browser/page_load_metrics/observers/ads_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer.h"
+#include "chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h"
 #include "chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h"
 #include "chrome/browser/subresource_filter/subresource_filter_browser_test_harness.h"
 #include "chrome/browser/ui/browser.h"
@@ -226,9 +227,8 @@
 
   // Navigate away and ensure we report same origin.
   ui_test_utils::NavigateToURL(browser(), GetURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      kSubresourceFilterOriginStatusHistogram,
-      AdsPageLoadMetricsObserver::AdOriginStatus::kSame, 1);
+  histogram_tester.ExpectUniqueSample(kSubresourceFilterOriginStatusHistogram,
+                                      FrameData::OriginStatus::kSame, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(AdTaggingBrowserTest, VerifyCrossOriginWithoutNavigate) {
@@ -270,9 +270,8 @@
 
   // Navigate away and ensure we report cross origin.
   ui_test_utils::NavigateToURL(browser(), GetURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      kSubresourceFilterOriginStatusHistogram,
-      AdsPageLoadMetricsObserver::AdOriginStatus::kCross, 1);
+  histogram_tester.ExpectUniqueSample(kSubresourceFilterOriginStatusHistogram,
+                                      FrameData::OriginStatus::kCross, 1);
 }
 
 // Ad script creates a frame and navigates it same origin.
@@ -293,9 +292,8 @@
 
   // Navigate away and ensure we report same origin.
   ui_test_utils::NavigateToURL(browser(), GetURL(url::kAboutBlankURL));
-  histogram_tester.ExpectUniqueSample(
-      kSubresourceFilterOriginStatusHistogram,
-      AdsPageLoadMetricsObserver::AdOriginStatus::kSame, 1);
+  histogram_tester.ExpectUniqueSample(kSubresourceFilterOriginStatusHistogram,
+                                      FrameData::OriginStatus::kSame, 1);
 }
 
 // Test that a subframe with a non-ad url but loaded by ad script is an ad.
diff --git a/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc b/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc
index 9b4d8c1f..2f5b233c 100644
--- a/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc
+++ b/chrome/browser/supervised_user/child_accounts/child_account_service_unittest.cc
@@ -6,8 +6,8 @@
 
 #include "base/bind.h"
 #include "chrome/browser/signin/chrome_signin_client_factory.h"
-#include "chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/gaia_cookie_manager_service_test_util.h"
 #include "chrome/browser/supervised_user/child_accounts/child_account_service_factory.h"
 #include "chrome/test/base/testing_profile.h"
 #include "components/signin/core/browser/list_accounts_test_utils.h"
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
index e00a7cb..cc8db80 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.cc
@@ -17,7 +17,7 @@
 #include "net/base/load_flags.h"
 #include "net/http/http_status_code.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
-#include "services/identity/public/cpp/primary_account_access_token_fetcher.h"
+#include "services/identity/public/cpp/identity_manager.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
@@ -90,7 +90,11 @@
       url_loader_factory_(std::move(url_loader_factory)),
       access_token_expired_(false) {}
 
-FamilyInfoFetcher::~FamilyInfoFetcher() {}
+FamilyInfoFetcher::~FamilyInfoFetcher() {
+  // Ensures IdentityManager observation is cleared when FamilyInfoFetcher is
+  // destructed before refresh token is available.
+  identity_manager_->RemoveObserver(this);
+}
 
 // static
 std::string FamilyInfoFetcher::RoleToString(FamilyMemberRole role) {
@@ -112,25 +116,56 @@
 
 void FamilyInfoFetcher::StartGetFamilyProfile() {
   request_path_ = kGetFamilyProfileApiPath;
-  StartFetchingAccessToken();
+  StartFetching();
 }
 
 void FamilyInfoFetcher::StartGetFamilyMembers() {
   request_path_ = kGetFamilyMembersApiPath;
-  StartFetchingAccessToken();
+  StartFetching();
+}
+
+void FamilyInfoFetcher::StartFetching() {
+  if (identity_manager_->HasAccountWithRefreshToken(primary_account_id_)) {
+    StartFetchingAccessToken();
+  } else {
+    // Wait until we get a refresh token.
+    identity_manager_->AddObserver(this);
+  }
 }
 
 void FamilyInfoFetcher::StartFetchingAccessToken() {
-  OAuth2TokenService::ScopeSet scopes{kScope};
-  access_token_fetcher_ = std::make_unique<
-      identity::PrimaryAccountAccessTokenFetcher>(
-      "family_info_fetcher", identity_manager_, scopes,
-      base::BindOnce(&FamilyInfoFetcher::OnAccessTokenFetchComplete,
-                     base::Unretained(this)),
-      identity::PrimaryAccountAccessTokenFetcher::Mode::kWaitUntilAvailable);
+  OAuth2TokenService::ScopeSet scopes;
+  scopes.insert(kScope);
+  access_token_fetcher_ = identity_manager_->CreateAccessTokenFetcherForAccount(
+      primary_account_id_, "family_info_fetcher", scopes,
+      base::BindOnce(&FamilyInfoFetcher::OnAccessTokenFetchCompleteForAccount,
+                     base::Unretained(this), primary_account_id_),
+      identity::AccessTokenFetcher::Mode::kImmediate);
 }
 
-void FamilyInfoFetcher::OnAccessTokenFetchComplete(
+void FamilyInfoFetcher::OnRefreshTokenUpdatedForAccount(
+    const AccountInfo& account_info) {
+  // Wait until we get a refresh token for the requested account.
+  if (account_info.account_id != primary_account_id_)
+    return;
+
+  identity_manager_->RemoveObserver(this);
+
+  StartFetchingAccessToken();
+}
+
+void FamilyInfoFetcher::OnRefreshTokensLoaded() {
+  identity_manager_->RemoveObserver(this);
+
+  // The PO2TS has loaded all tokens, but we didn't get one for the account we
+  // want. We probably won't get one any time soon, so report an error.
+  DLOG(WARNING) << "Did not get a refresh token for account "
+                << primary_account_id_;
+  consumer_->OnFailure(TOKEN_ERROR);
+}
+
+void FamilyInfoFetcher::OnAccessTokenFetchCompleteForAccount(
+    std::string account_id,
     GoogleServiceAuthError error,
     identity::AccessTokenInfo access_token_info) {
   access_token_fetcher_.reset();
@@ -218,7 +253,7 @@
     scopes.insert(kScope);
     identity_manager_->RemoveAccessTokenFromCache(primary_account_id_, scopes,
                                                   access_token_);
-    StartFetchingAccessToken();
+    StartFetching();
     return;
   }
 
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
index d79fd2a..5b96fc2b 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher.h
@@ -20,7 +20,7 @@
 }
 
 namespace identity {
-class PrimaryAccountAccessTokenFetcher;
+class AccessTokenFetcher;
 }
 
 namespace network {
@@ -31,7 +31,7 @@
 // Fetches information about the family of the signed-in user. It can get
 // information about the family itself (e.g. a name), as well as a list of
 // family members and their properties.
-class FamilyInfoFetcher {
+class FamilyInfoFetcher : public identity::IdentityManager::Observer {
  public:
   enum ErrorCode {
     TOKEN_ERROR,    // Failed to get OAuth2 token.
@@ -86,7 +86,7 @@
       Consumer* consumer,
       identity::IdentityManager* identity_manager,
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
-  ~FamilyInfoFetcher();
+  ~FamilyInfoFetcher() override;
 
   // Public so tests can use them.
   static std::string RoleToString(FamilyMemberRole role);
@@ -103,8 +103,15 @@
                                       const std::string& response_body);
 
  private:
-  void OnAccessTokenFetchComplete(GoogleServiceAuthError error,
-                                  identity::AccessTokenInfo access_token_info);
+  // IdentityManager::Observer implementation:
+  void OnRefreshTokenUpdatedForAccount(
+      const AccountInfo& account_info) override;
+  void OnRefreshTokensLoaded() override;
+
+  void OnAccessTokenFetchCompleteForAccount(
+      std::string account_id,
+      GoogleServiceAuthError error,
+      identity::AccessTokenInfo access_token_info);
 
   void OnSimpleLoaderComplete(std::unique_ptr<std::string> response_body);
 
@@ -115,6 +122,7 @@
   static void ParseProfile(const base::DictionaryValue* dict,
                            FamilyMember* member);
 
+  void StartFetching();
   void StartFetchingAccessToken();
   void FamilyProfileFetched(const std::string& response);
   void FamilyMembersFetched(const std::string& response);
@@ -125,8 +133,7 @@
   scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
 
   std::string request_path_;
-  std::unique_ptr<identity::PrimaryAccountAccessTokenFetcher>
-      access_token_fetcher_;
+  std::unique_ptr<identity::AccessTokenFetcher> access_token_fetcher_;
   std::string access_token_;
   bool access_token_expired_;
   std::unique_ptr<network::SimpleURLLoader> simple_url_loader_;
diff --git a/chrome/browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc b/chrome/browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc
index 154bc48..6b53167 100644
--- a/chrome/browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc
+++ b/chrome/browser/supervised_user/child_accounts/family_info_fetcher_unittest.cc
@@ -288,6 +288,11 @@
   EXPECT_CALL(access_token_requested, Run()).Times(0);
   identity_test_env_.SetCallbackForNextAccessTokenRequest(
       access_token_requested.Get());
+
+  // After all refresh tokens have been loaded, there is still no token for our
+  // user, so we expect a token error.
+  EXPECT_CALL(*this, OnFailure(FamilyInfoFetcher::TOKEN_ERROR));
+  identity_test_env_.ReloadAccountsFromDisk();
 }
 
 TEST_F(FamilyInfoFetcherTest, GetTokenFailure) {
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc
index 4ab0fda..9b0af47 100644
--- a/chrome/browser/sync/sync_ui_util.cc
+++ b/chrome/browser/sync/sync_ui_util.cc
@@ -54,25 +54,25 @@
                       : IDS_SYNC_ACCOUNT_SYNCING_CUSTOM_DATA_TYPES);
 }
 
-void GetStatusForActionableError(const syncer::SyncProtocolError& error,
+void GetStatusForActionableError(syncer::ClientAction action,
                                  base::string16* status_label,
                                  base::string16* link_label,
                                  ActionType* action_type) {
   DCHECK(status_label);
   DCHECK(link_label);
-  switch (error.action) {
+  switch (action) {
     case syncer::UPGRADE_CLIENT:
-      status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT));
-      link_label->assign(
-          l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT_LINK_LABEL));
+      *status_label = l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT);
+      *link_label =
+          l10n_util::GetStringUTF16(IDS_SYNC_UPGRADE_CLIENT_LINK_LABEL);
       *action_type = UPGRADE_CLIENT;
       break;
     case syncer::ENABLE_SYNC_ON_ACCOUNT:
-      status_label->assign(
-          l10n_util::GetStringUTF16(IDS_SYNC_STATUS_ENABLE_SYNC_ON_ACCOUNT));
+      *status_label =
+          l10n_util::GetStringUTF16(IDS_SYNC_STATUS_ENABLE_SYNC_ON_ACCOUNT);
       break;
     default:
-      status_label->clear();
+      *status_label = base::string16();
       break;
   }
 }
@@ -87,47 +87,44 @@
   // unrecoverable error message.
   syncer::SyncStatus status;
   service->QueryDetailedSyncStatus(&status);
-  GetStatusForActionableError(status.sync_protocol_error, status_label,
+  GetStatusForActionableError(status.sync_protocol_error.action, status_label,
                               link_label, action_type);
   if (status_label->empty()) {
     *action_type = REAUTHENTICATE;
-    link_label->assign(
-        l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL));
+    *link_label = l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL);
 
 #if !defined(OS_CHROMEOS)
-    status_label->assign(l10n_util::GetStringUTF16(
-        IDS_SYNC_STATUS_UNRECOVERABLE_ERROR));
+    *status_label =
+        l10n_util::GetStringUTF16(IDS_SYNC_STATUS_UNRECOVERABLE_ERROR);
     // The message for managed accounts is the same as that of the cros.
     if (!signin_util::IsUserSignoutAllowedForProfile(profile)) {
-      status_label->assign(l10n_util::GetStringUTF16(
-          IDS_SYNC_STATUS_UNRECOVERABLE_ERROR_NEEDS_SIGNOUT));
+      *status_label = l10n_util::GetStringUTF16(
+          IDS_SYNC_STATUS_UNRECOVERABLE_ERROR_NEEDS_SIGNOUT);
     }
 #else
-    status_label->assign(l10n_util::GetStringUTF16(
-        IDS_SYNC_STATUS_UNRECOVERABLE_ERROR_NEEDS_SIGNOUT));
+    *status_label = l10n_util::GetStringUTF16(
+        IDS_SYNC_STATUS_UNRECOVERABLE_ERROR_NEEDS_SIGNOUT);
 #endif
   }
 }
 
 // Depending on the authentication state, returns labels to be used to display
 // information about the sync status.
-void GetStatusForAuthError(Profile* profile,
+void GetStatusForAuthError(const GoogleServiceAuthError& auth_error,
                            base::string16* status_label,
                            base::string16* link_label,
                            ActionType* action_type) {
   DCHECK(status_label);
   DCHECK(link_label);
-  const GoogleServiceAuthError::State state =
-      SigninErrorControllerFactory::GetForProfile(profile)->
-          auth_error().state();
-  switch (state) {
+  switch (auth_error.state()) {
+    case GoogleServiceAuthError::NONE:
+      NOTREACHED();
+      break;
     case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
-      status_label->assign(
-          l10n_util::GetStringUTF16(IDS_SYNC_SERVICE_UNAVAILABLE));
+      *status_label = l10n_util::GetStringUTF16(IDS_SYNC_SERVICE_UNAVAILABLE);
       break;
     case GoogleServiceAuthError::CONNECTION_FAILED:
-      status_label->assign(
-          l10n_util::GetStringUTF16(IDS_SYNC_SERVER_IS_UNREACHABLE));
+      *status_label = l10n_util::GetStringUTF16(IDS_SYNC_SERVER_IS_UNREACHABLE);
       // Note that there is little the user can do if the server is not
       // reachable. Since attempting to re-connect is done automatically by
       // the Syncer, we do not show the (re)login link.
@@ -137,16 +134,13 @@
     case GoogleServiceAuthError::ACCOUNT_DELETED:
     case GoogleServiceAuthError::ACCOUNT_DISABLED:
     default:
-      status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_ERROR));
-      link_label->assign(
-          l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL));
+      *status_label = l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_ERROR);
+      *link_label = l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL);
       *action_type = REAUTHENTICATE;
       break;
   }
 }
 
-// TODO(akalin): Write unit tests for these three functions below.
-
 // status_label and link_label must either be both null or both non-null.
 MessageType GetStatusInfo(Profile* profile,
                           const syncer::SyncService* service,
@@ -154,17 +148,18 @@
                           base::string16* status_label,
                           base::string16* link_label,
                           ActionType* action_type) {
+  DCHECK(service);
   DCHECK_EQ(status_label == nullptr, link_label == nullptr);
 
-  MessageType result_type(SYNCED);
-
-  if (!identity_manager->HasPrimaryAccount())
+  if (!identity_manager->HasPrimaryAccount()) {
     return PRE_SYNCED;
+  }
 
   // Needed to check the state of the authentication process below.
-  auto* primary_account_mutator = identity_manager->GetPrimaryAccountMutator();
+  const auto* primary_account_mutator =
+      identity_manager->GetPrimaryAccountMutator();
 
-  if (!service || service->GetUserSettings()->IsFirstSetupComplete() ||
+  if (service->GetUserSettings()->IsFirstSetupComplete() ||
       service->HasDisableReason(
           syncer::SyncService::DISABLE_REASON_ENTERPRISE_POLICY) ||
       service->HasDisableReason(
@@ -172,7 +167,7 @@
     // The order or priority is going to be: 1. Unrecoverable errors.
     // 2. Auth errors. 3. Protocol errors. 4. Passphrase errors.
 
-    if (service && service->HasUnrecoverableError()) {
+    if (service->HasUnrecoverableError()) {
       if (status_label && link_label) {
         GetStatusForUnrecoverableError(profile, service, status_label,
                                        link_label, action_type);
@@ -184,118 +179,114 @@
     if (primary_account_mutator &&
         primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress()) {
       if (status_label) {
-        status_label->assign(
-          l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
+        *status_label =
+            l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL);
       }
       return PRE_SYNCED;
     }
 
-    bool sync_everything =
+    // Since there is no auth in progress, check for an auth error first.
+    GoogleServiceAuthError auth_error =
+        SigninErrorControllerFactory::GetForProfile(profile)->auth_error();
+    if (auth_error.state() != GoogleServiceAuthError::NONE) {
+      if (status_label && link_label) {
+        GetStatusForAuthError(auth_error, status_label, link_label,
+                              action_type);
+      }
+      return SYNC_ERROR;
+    }
+
+    // We don't have an auth error. Check for an actionable error.
+    syncer::SyncStatus status;
+    service->QueryDetailedSyncStatus(&status);
+    if (status_label && link_label) {
+      GetStatusForActionableError(status.sync_protocol_error.action,
+                                  status_label, link_label, action_type);
+      if (!status_label->empty()) {
+        return SYNC_ERROR;
+      }
+    }
+
+    // Check for a passphrase error.
+    if (service->GetUserSettings()->IsPassphraseRequiredForDecryption()) {
+      if (status_label && link_label) {
+        *status_label =
+            l10n_util::GetStringUTF16(IDS_SYNC_STATUS_NEEDS_PASSWORD);
+        *link_label = l10n_util::GetStringUTF16(
+            IDS_SYNC_STATUS_NEEDS_PASSWORD_LINK_LABEL);
+        *action_type = ENTER_PASSPHRASE;
+      }
+      return SYNC_ERROR;
+    }
+
+    const bool sync_everything =
         service->GetUserSettings()->IsSyncEverythingEnabled();
 
-    // Check for sync errors if the sync service is enabled.
-    if (service) {
-      // Since there is no auth in progress, check for an auth error first.
-      GoogleServiceAuthError auth_error =
-          SigninErrorControllerFactory::GetForProfile(profile)->auth_error();
-      if (auth_error.state() != GoogleServiceAuthError::NONE) {
-        if (status_label && link_label) {
-          GetStatusForAuthError(profile, status_label, link_label, action_type);
-        }
-        return SYNC_ERROR;
+    // Check to see if sync has been disabled via the dasboard and needs to be
+    // set up once again.
+    if (service->HasDisableReason(
+            syncer::SyncService::DISABLE_REASON_USER_CHOICE) &&
+        status.sync_protocol_error.error_type == syncer::NOT_MY_BIRTHDAY) {
+      if (status_label) {
+        *status_label = GetSyncedStateStatusLabel(service, sync_everything);
       }
-
-      // We don't have an auth error. Check for an actionable error.
-      syncer::SyncStatus status;
-      service->QueryDetailedSyncStatus(&status);
-      if (status_label && link_label) {
-        GetStatusForActionableError(status.sync_protocol_error, status_label,
-                                    link_label, action_type);
-        if (!status_label->empty())
-          return SYNC_ERROR;
-      }
-
-      // Check for a passphrase error.
-      if (service->GetUserSettings()->IsPassphraseRequired() &&
-          service->GetUserSettings()->IsPassphraseRequiredForDecryption()) {
-        if (status_label && link_label) {
-          status_label->assign(
-              l10n_util::GetStringUTF16(IDS_SYNC_STATUS_NEEDS_PASSWORD));
-          link_label->assign(
-              l10n_util::GetStringUTF16(
-                  IDS_SYNC_STATUS_NEEDS_PASSWORD_LINK_LABEL));
-          *action_type = ENTER_PASSPHRASE;
-        }
-        return SYNC_ERROR;
-      }
-
-      // Check to see if sync has been disabled via the dasboard and needs to be
-      // set up once again.
-      if (service->HasDisableReason(
-              syncer::SyncService::DISABLE_REASON_USER_CHOICE) &&
-          status.sync_protocol_error.error_type == syncer::NOT_MY_BIRTHDAY) {
-        if (status_label) {
-          status_label->assign(
-              GetSyncedStateStatusLabel(service, sync_everything));
-        }
-        return PRE_SYNCED;
-      }
+      return PRE_SYNCED;
     }
 
     // There is no error. Display "Last synced..." message.
     if (status_label) {
-      status_label->assign(GetSyncedStateStatusLabel(service, sync_everything));
+      *status_label = GetSyncedStateStatusLabel(service, sync_everything);
     }
     return SYNCED;
-  } else {
-    // Either show auth error information with a link to re-login, auth in prog,
-    // or provide a link to continue with setup.
-    if (service->IsFirstSetupInProgress()) {
-      result_type = PRE_SYNCED;
-      syncer::SyncStatus status;
-      service->QueryDetailedSyncStatus(&status);
-      GoogleServiceAuthError auth_error =
-          SigninErrorControllerFactory::GetForProfile(profile)->auth_error();
+  }
+
+  MessageType result_type = SYNCED;
+  // Either show auth error information with a link to re-login, auth in prog,
+  // or provide a link to continue with setup.
+  if (service->IsFirstSetupInProgress()) {
+    result_type = PRE_SYNCED;
+    if (status_label) {
+      *status_label = l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS);
+    }
+
+    GoogleServiceAuthError auth_error =
+        SigninErrorControllerFactory::GetForProfile(profile)->auth_error();
+    if (primary_account_mutator &&
+        primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress()) {
       if (status_label) {
-        status_label->assign(
-            l10n_util::GetStringUTF16(IDS_SYNC_NTP_SETUP_IN_PROGRESS));
+        *status_label =
+            l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL);
       }
-      if (primary_account_mutator &&
-          primary_account_mutator->LegacyIsPrimaryAccountAuthInProgress()) {
-        if (status_label) {
-          status_label->assign(
-              l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
-        }
-      } else if (auth_error.state() != GoogleServiceAuthError::NONE &&
-                 auth_error.state() != GoogleServiceAuthError::TWO_FACTOR) {
-        if (status_label && link_label) {
-          GetStatusForAuthError(profile, status_label, link_label, action_type);
-        }
-        result_type = SYNC_ERROR;
-      }
-    } else if (service->HasUnrecoverableError()) {
-      result_type = SYNC_ERROR;
+    } else if (auth_error.state() != GoogleServiceAuthError::NONE &&
+               auth_error.state() != GoogleServiceAuthError::TWO_FACTOR) {
       if (status_label && link_label) {
-        GetStatusForUnrecoverableError(profile, service, status_label,
-                                       link_label, action_type);
+        GetStatusForAuthError(auth_error, status_label, link_label,
+                              action_type);
       }
-    } else if (identity_manager->HasPrimaryAccount()) {
-      if (ShouldRequestSyncConfirmation(service)) {
-        if (status_label && link_label) {
-          status_label->assign(
-              l10n_util::GetStringUTF16(IDS_SYNC_SETTINGS_NOT_CONFIRMED));
-          link_label->assign(l10n_util::GetStringUTF16(
-              IDS_SYNC_ERROR_USER_MENU_CONFIRM_SYNC_SETTINGS_BUTTON));
-        }
-        *action_type = CONFIRM_SYNC_SETTINGS;
-        result_type = SYNC_ERROR;
-      } else {
-        // The user is signed in, but sync has been stopped.
-        result_type = PRE_SYNCED;
-        if (status_label) {
-          status_label->assign(
-              l10n_util::GetStringUTF16(IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED));
-        }
+      result_type = SYNC_ERROR;
+    }
+  } else if (service->HasUnrecoverableError()) {
+    result_type = SYNC_ERROR;
+    if (status_label && link_label) {
+      GetStatusForUnrecoverableError(profile, service, status_label, link_label,
+                                     action_type);
+    }
+  } else {
+    if (ShouldRequestSyncConfirmation(service)) {
+      if (status_label && link_label) {
+        *status_label =
+            l10n_util::GetStringUTF16(IDS_SYNC_SETTINGS_NOT_CONFIRMED);
+        *link_label = l10n_util::GetStringUTF16(
+            IDS_SYNC_ERROR_USER_MENU_CONFIRM_SYNC_SETTINGS_BUTTON);
+      }
+      *action_type = CONFIRM_SYNC_SETTINGS;
+      result_type = SYNC_ERROR;
+    } else {
+      // The user is signed in, but sync has been stopped.
+      result_type = PRE_SYNCED;
+      if (status_label) {
+        *status_label =
+            l10n_util::GetStringUTF16(IDS_SIGNED_IN_WITH_SYNC_SUPPRESSED);
       }
     }
   }
@@ -310,6 +301,7 @@
                             base::string16* status_label,
                             base::string16* link_label,
                             ActionType* action_type) {
+  DCHECK(service);
   DCHECK(status_label);
   DCHECK(link_label);
   return GetStatusInfo(profile, service, identity_manager, status_label,
@@ -319,7 +311,6 @@
 #if !defined(OS_CHROMEOS)
 AvatarSyncErrorType GetMessagesForAvatarSyncError(
     Profile* profile,
-    identity::IdentityManager* identity_manager,
     int* content_string_id,
     int* button_string_id) {
   const syncer::SyncService* service =
@@ -354,15 +345,7 @@
   SigninErrorController* signin_error_controller =
       SigninErrorControllerFactory::GetForProfile(profile);
   if (signin_error_controller && signin_error_controller->HasError()) {
-    if (profile->IsSupervised()) {
-      // For a supervised user, no direct action can be taken to resolve an
-      // auth token error.
-      *content_string_id = IDS_SYNC_ERROR_USER_MENU_SUPERVISED_SIGNIN_MESSAGE;
-      *button_string_id = 0;
-      return SUPERVISED_USER_AUTH_ERROR;
-    }
-    // For a non-supervised user, the user can reauth to resolve the signin
-    // error.
+    // The user can reauth to resolve the signin error.
     *content_string_id = IDS_SYNC_ERROR_USER_MENU_SIGNIN_MESSAGE;
     *button_string_id = IDS_SYNC_ERROR_USER_MENU_SIGNIN_BUTTON;
     return AUTH_ERROR;
@@ -387,8 +370,7 @@
     }
 
     // Check for a sync confirmation error.
-    if (identity_manager->HasPrimaryAccount() &&
-        ShouldRequestSyncConfirmation(service)) {
+    if (ShouldRequestSyncConfirmation(service)) {
       *content_string_id = IDS_SYNC_SETTINGS_NOT_CONFIRMED;
       *button_string_id = IDS_SYNC_ERROR_USER_MENU_CONFIRM_SYNC_SETTINGS_BUTTON;
       return SETTINGS_UNCONFIRMED_ERROR;
@@ -398,27 +380,27 @@
   // There is no error.
   return NO_SYNC_ERROR;
 }
-#endif
+#endif  // !defined(OS_CHROMEOS)
 
 MessageType GetStatus(Profile* profile,
                       const syncer::SyncService* service,
                       identity::IdentityManager* identity_manager) {
+  DCHECK(service);
   ActionType action_type = NO_ACTION;
   return GetStatusInfo(profile, service, identity_manager, nullptr, nullptr,
                        &action_type);
 }
 
 bool ShouldRequestSyncConfirmation(const syncer::SyncService* service) {
-  return !service->IsSetupInProgress() &&
-         !service->GetUserSettings()->IsFirstSetupComplete() &&
-         !service->HasDisableReason(
-             syncer::SyncService::DISABLE_REASON_USER_CHOICE) &&
-         service->IsAuthenticatedAccountPrimary();
+  return !service->IsLocalSyncEnabled() &&
+         service->GetUserSettings()->IsSyncRequested() &&
+         service->IsAuthenticatedAccountPrimary() &&
+         !service->IsSetupInProgress() &&
+         !service->GetUserSettings()->IsFirstSetupComplete();
 }
 
 bool ShouldShowPassphraseError(const syncer::SyncService* service) {
   return service->GetUserSettings()->IsFirstSetupComplete() &&
-         service->GetUserSettings()->IsPassphraseRequired() &&
          service->GetUserSettings()->IsPassphraseRequiredForDecryption();
 }
 
diff --git a/chrome/browser/sync/sync_ui_util.h b/chrome/browser/sync/sync_ui_util.h
index 12a492a4..f6e225d 100644
--- a/chrome/browser/sync/sync_ui_util.h
+++ b/chrome/browser/sync/sync_ui_util.h
@@ -64,7 +64,6 @@
 // exposed to the user through the titlebar avatar button.
 AvatarSyncErrorType GetMessagesForAvatarSyncError(
     Profile* profile,
-    identity::IdentityManager* identity_manager,
     int* content_string_id,
     int* button_string_id);
 #endif
diff --git a/chrome/browser/sync/test/integration/apps_helper.cc b/chrome/browser/sync/test/integration/apps_helper.cc
index 2a8e5bc..c88d6fd 100644
--- a/chrome/browser/sync/test/integration/apps_helper.cc
+++ b/chrome/browser/sync/test/integration/apps_helper.cc
@@ -23,7 +23,7 @@
 namespace {
 
 std::string CreateFakeAppName(int index) {
-  return "fakeapp" + base::IntToString(index);
+  return "fakeapp" + base::NumberToString(index);
 }
 
 }  // namespace
diff --git a/chrome/browser/sync/test/integration/dictionary_helper.cc b/chrome/browser/sync/test/integration/dictionary_helper.cc
index e6fd2914..13d08f6 100644
--- a/chrome/browser/sync/test/integration/dictionary_helper.cc
+++ b/chrome/browser/sync/test/integration/dictionary_helper.cc
@@ -120,7 +120,7 @@
 bool AddWords(int index, int n, const std::string& prefix) {
   bool result = true;
   for (int i = 0; i < n; ++i) {
-    result &= AddWord(index, prefix + base::IntToString(i));
+    result &= AddWord(index, prefix + base::NumberToString(i));
   }
   return result;
 }
diff --git a/chrome/browser/sync/test/integration/performance/sync_timing_helper.cc b/chrome/browser/sync/test/integration/performance/sync_timing_helper.cc
index 6c541e0..d7b0423 100644
--- a/chrome/browser/sync/test/integration/performance/sync_timing_helper.cc
+++ b/chrome/browser/sync/test/integration/performance/sync_timing_helper.cc
@@ -36,7 +36,7 @@
                  const std::string& trace,
                  const base::TimeDelta& dt) {
   printf("*RESULT %s: %s= %s ms\n", measurement.c_str(), trace.c_str(),
-         base::Int64ToString(dt.InMilliseconds()).c_str());
+         base::NumberToString(dt.InMilliseconds()).c_str());
 }
 
 }  // namespace sync_timing_helper
diff --git a/chrome/browser/sync/test/integration/secondary_account_helper.cc b/chrome/browser/sync/test/integration/secondary_account_helper.cc
index 61aefbb..89e998c 100644
--- a/chrome/browser/sync/test/integration/secondary_account_helper.cc
+++ b/chrome/browser/sync/test/integration/secondary_account_helper.cc
@@ -6,8 +6,8 @@
 
 #include "base/bind.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/gaia_cookie_manager_service_test_util.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "services/identity/public/cpp/identity_manager.h"
diff --git a/chrome/browser/sync/test/integration/single_client_directory_sync_test.cc b/chrome/browser/sync/test/integration/single_client_directory_sync_test.cc
index 89404d3..57d4aa3cb 100644
--- a/chrome/browser/sync/test/integration/single_client_directory_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_directory_sync_test.cc
@@ -129,7 +129,7 @@
       0, bookmarks_helper::GetOtherNode(0), 0, "top");
   for (int i = 0; i < 100; ++i) {
     ASSERT_TRUE(
-        bookmarks_helper::AddURL(0, top, 0, base::Int64ToString(i), url));
+        bookmarks_helper::AddURL(0, top, 0, base::NumberToString(i), url));
   }
   sync_service->FlushDirectory();
 
diff --git a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
index edf5b2b..375d1f2 100644
--- a/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_wallet_sync_test.cc
@@ -50,6 +50,7 @@
 using wallet_helper::GetDefaultCreditCard;
 using wallet_helper::GetPersonalDataManager;
 using wallet_helper::GetProfileWebDataService;
+using wallet_helper::GetWalletDataModelTypeState;
 using wallet_helper::kDefaultBillingAddressID;
 using wallet_helper::kDefaultCardID;
 using wallet_helper::kDefaultCustomerID;
@@ -608,6 +609,49 @@
   ExpectNoHistogramsForAddressesDiff();
 }
 
+class SingleClientWalletSyncTestWithDataUss
+    : public UssWalletSwitchToggler,
+      public SingleClientWalletSyncTest {
+ public:
+  SingleClientWalletSyncTestWithDataUss() {
+    InitWithFeatures(
+        /*enabled_features=*/{switches::kSyncUSSAutofillWalletData},
+        /*disabled_features=*/{});
+  }
+};
+
+// Check on top of
+// SingleClientWalletSyncTestWithDefaultFeatures.EmptyUpdatesAreIgnored that the
+// new progress marker is stored for empty updates. This is a regression test
+// for crbug.com/924447 (and restricts to USS because the bug cannot exist for
+// Directory and because the USS implementation is launching).
+IN_PROC_BROWSER_TEST_P(SingleClientWalletSyncTestWithDataUss,
+                       EmptyUpdatesUpdateProgressMarker) {
+  GetFakeServer()->SetWalletData(
+      {CreateSyncWalletCard(/*name=*/"card-1", /*last_four=*/"0001",
+                            kDefaultBillingAddressID),
+       CreateSyncWalletAddress(/*name=*/"address-1", /*company=*/"Company-1"),
+       CreateDefaultSyncPaymentsCustomerData()});
+  ASSERT_TRUE(SetupSync());
+
+  sync_pb::ModelTypeState state_before = GetWalletDataModelTypeState(0);
+
+  // Do not change anything on the server so that the update forced below is an
+  // empty one.
+
+  // Constructing the checker captures the current progress marker. Make sure to
+  // do that before triggering the fetch.
+  WaitForNextWalletUpdateChecker checker(GetSyncService(0));
+  // Trigger a sync and wait for the new data to arrive.
+  TriggerSyncForModelTypes(0,
+                           syncer::ModelTypeSet(syncer::AUTOFILL_WALLET_DATA));
+  ASSERT_TRUE(checker.Wait());
+
+  sync_pb::ModelTypeState state_after = GetWalletDataModelTypeState(0);
+  EXPECT_NE(state_before.progress_marker().token(),
+            state_after.progress_marker().token());
+}
+
 // If the server sends the same cards and addresses again, they should not
 // change on the client.
 IN_PROC_BROWSER_TEST_P(SingleClientWalletSyncTestWithDefaultFeatures,
@@ -1390,6 +1434,13 @@
                                            std::make_pair(true, false),
                                            std::make_pair(true, true)));
 
+// TODO(jkrcal): Merge this with SingleClientWalletSyncTestWithDefaultFeatures
+// once wallet data USS is launched. https://crbug.com/853688.
+INSTANTIATE_TEST_SUITE_P(USS,
+                         SingleClientWalletSyncTestWithDataUss,
+                         ::testing::Values(std::make_pair(true, false),
+                                           std::make_pair(true, true)));
+
 // Depends on SyncUSSWalletData, cannot set the first param to false.
 INSTANTIATE_TEST_SUITE_P(USS,
                          SingleClientWalletSecondaryAccountSyncTest,
diff --git a/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc b/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc
index 9b9178e..80ad392 100644
--- a/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc
+++ b/chrome/browser/sync/test/integration/sync_exponential_backoff_test.cc
@@ -12,6 +12,7 @@
 #include "chrome/browser/sync/test/integration/updated_progress_marker_checker.h"
 #include "components/browser_sync/profile_sync_service.h"
 #include "components/sync/test/fake_server/fake_server_http_post_provider.h"
+#include "content/public/test/network_connection_change_simulator.h"
 #include "net/base/network_change_notifier.h"
 
 namespace {
@@ -86,8 +87,9 @@
   // Trigger network change notification and remember time when it happened.
   // Ensure that scheduler runs canary job immediately.
   fake_server::FakeServerHttpPostProvider::EnableNetwork();
-  net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests(
-      net::NetworkChangeNotifier::CONNECTION_ETHERNET);
+  content::NetworkConnectionChangeSimulator connection_change_simulator;
+  connection_change_simulator.SetConnectionType(
+      network::mojom::ConnectionType::CONNECTION_ETHERNET);
 
   base::Time network_notification_time = base::Time::Now();
 
diff --git a/chrome/browser/sync/test/integration/sync_extension_helper.cc b/chrome/browser/sync/test/integration/sync_extension_helper.cc
index 57bbc83..24d62df1 100644
--- a/chrome/browser/sync/test/integration/sync_extension_helper.cc
+++ b/chrome/browser/sync/test/integration/sync_extension_helper.cc
@@ -296,7 +296,7 @@
 }
 
 std::string SyncExtensionHelper::CreateFakeExtensionName(int index) {
-  return extension_name_prefix_ + base::IntToString(index);
+  return extension_name_prefix_ + base::NumberToString(index);
 }
 
 bool SyncExtensionHelper::ExtensionNameToIndex(const std::string& name,
diff --git a/chrome/browser/sync/test/integration/sync_test.cc b/chrome/browser/sync/test/integration/sync_test.cc
index a4cf29a..2dffbc3 100644
--- a/chrome/browser/sync/test/integration/sync_test.cc
+++ b/chrome/browser/sync/test/integration/sync_test.cc
@@ -82,6 +82,7 @@
 #include "content/public/browser/navigation_entry.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/browser/notification_service.h"
+#include "content/public/browser/storage_partition.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/test_browser_thread.h"
 #include "google_apis/gaia/gaia_urls.h"
@@ -94,6 +95,7 @@
 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/test_url_fetcher_factory.h"
 #include "net/url_request/url_fetcher.h"
+#include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/cpp/simple_url_loader.h"
 #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
@@ -183,9 +185,17 @@
     content::BrowserContext* context,
     syncer::P2PNotificationTarget notification_target) {
   Profile* profile = static_cast<Profile*>(context);
-  auto config_helper =
-      std::make_unique<jingle_glue::NetworkServiceConfigTestUtil>(
-          profile->GetRequestContext());
+  std::unique_ptr<jingle_glue::NetworkServiceConfigTestUtil> config_helper;
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+    content::StoragePartition* storage_partition =
+        content::BrowserContext::GetDefaultStoragePartition(profile);
+    config_helper = std::make_unique<jingle_glue::NetworkServiceConfigTestUtil>(
+        base::BindRepeating(&content::StoragePartition::GetNetworkContext,
+                            base::Unretained(storage_partition)));
+  } else {
+    config_helper = std::make_unique<jingle_glue::NetworkServiceConfigTestUtil>(
+        profile->GetRequestContext());
+  }
   return std::make_unique<invalidation::ProfileInvalidationProvider>(
       std::make_unique<invalidation::P2PInvalidationService>(
           std::move(config_helper), content::GetNetworkConnectionTracker(),
diff --git a/chrome/browser/sync/test/integration/themes_helper.cc b/chrome/browser/sync/test/integration/themes_helper.cc
index d95a196..d86532920 100644
--- a/chrome/browser/sync/test/integration/themes_helper.cc
+++ b/chrome/browser/sync/test/integration/themes_helper.cc
@@ -23,7 +23,7 @@
 
 // Make a name to pass to an extension helper.
 std::string MakeName(int index) {
-  return "faketheme" + base::IntToString(index);
+  return "faketheme" + base::NumberToString(index);
 }
 
 ThemeService* GetThemeService(Profile* profile) {
diff --git a/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc b/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc
index e5756974..d1164e3 100644
--- a/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_dictionary_sync_test.cc
@@ -97,7 +97,7 @@
   ASSERT_TRUE(DictionaryMatchChecker().Wait());
 
   for (int i = 0; i < num_clients(); ++i)
-    dictionary_helper::AddWord(i, "foo" + base::IntToString(i));
+    dictionary_helper::AddWord(i, "foo" + base::NumberToString(i));
 
   ASSERT_TRUE(DictionaryMatchChecker().Wait());
   ASSERT_EQ(num_clients(),
diff --git a/chrome/browser/sync/test/integration/wallet_helper.cc b/chrome/browser/sync/test/integration/wallet_helper.cc
index 97d3171..123a212 100644
--- a/chrome/browser/sync/test/integration/wallet_helper.cc
+++ b/chrome/browser/sync/test/integration/wallet_helper.cc
@@ -25,6 +25,7 @@
 #include "components/autofill/core/browser/webdata/autofill_table.h"
 #include "components/keyed_service/core/service_access_type.h"
 #include "components/sync/driver/sync_driver_switches.h"
+#include "components/sync/protocol/model_type_state.pb.h"
 
 using autofill::AutofillMetadata;
 using autofill::AutofillProfile;
@@ -108,11 +109,11 @@
               const std::vector<Item*>& list_b) {
   int x = 0;
   for (Item* item : list_a) {
-    LOG(WARNING) << "A#" << x++ << " " << *item;
+    DVLOG(1) << "A#" << x++ << " " << *item;
   }
   x = 0;
   for (Item* item : list_b) {
-    LOG(WARNING) << "B#" << x++ << " " << *item;
+    DVLOG(1) << "B#" << x++ << " " << *item;
   }
 }
 
@@ -134,7 +135,7 @@
   // Check that all server profiles have converted to local ones.
   for (AutofillProfile* profile : server_profiles_a) {
     if (!profile->has_converted()) {
-      LOG(WARNING) << "Not all profiles are converted";
+      DVLOG(1) << "Not all profiles are converted";
       LogLists(server_profiles_a, server_profiles_b);
       return false;
     }
@@ -199,6 +200,16 @@
       ->GetServerAddressesMetadata(addresses_metadata);
 }
 
+void GetWalletDataModelTypeStateOnDBSequence(
+    AutofillWebDataService* wds,
+    sync_pb::ModelTypeState* model_type_state) {
+  DCHECK(wds->GetDBTaskRunner()->RunsTasksInCurrentSequence());
+  syncer::MetadataBatch metadata_batch;
+  AutofillTable::FromWebDatabase(wds->GetDatabase())
+      ->GetAllSyncMetadata(syncer::AUTOFILL_WALLET_DATA, &metadata_batch);
+  *model_type_state = metadata_batch.GetModelTypeState();
+}
+
 }  // namespace
 
 namespace wallet_helper {
@@ -286,6 +297,16 @@
   WaitForCurrentTasksToComplete(wds->GetDBTaskRunner());
 }
 
+sync_pb::ModelTypeState GetWalletDataModelTypeState(int profile) {
+  sync_pb::ModelTypeState result;
+  scoped_refptr<AutofillWebDataService> wds = GetProfileWebDataService(profile);
+  wds->GetDBTaskRunner()->PostTask(
+      FROM_HERE, base::BindOnce(&GetWalletDataModelTypeStateOnDBSequence,
+                                base::Unretained(wds.get()), &result));
+  WaitForCurrentTasksToComplete(wds->GetDBTaskRunner());
+  return result;
+}
+
 void UnmaskServerCard(int profile,
                       const CreditCard& credit_card,
                       const base::string16& full_number) {
diff --git a/chrome/browser/sync/test/integration/wallet_helper.h b/chrome/browser/sync/test/integration/wallet_helper.h
index f612dac..f99486e7 100644
--- a/chrome/browser/sync/test/integration/wallet_helper.h
+++ b/chrome/browser/sync/test/integration/wallet_helper.h
@@ -24,6 +24,7 @@
 
 namespace sync_pb {
 class SyncEntity;
+class ModelTypeState;
 }
 
 namespace wallet_helper {
@@ -72,6 +73,8 @@
     int profile,
     std::map<std::string, autofill::AutofillMetadata>* addresses_metadata);
 
+sync_pb::ModelTypeState GetWalletDataModelTypeState(int profile);
+
 void UnmaskServerCard(int profile,
                       const autofill::CreditCard& credit_card,
                       const base::string16& full_number);
diff --git a/chrome/browser/sync_file_system/drive_backend/drive_backend_util.cc b/chrome/browser/sync_file_system/drive_backend/drive_backend_util.cc
index e933b82..feefab4a 100644
--- a/chrome/browser/sync_file_system/drive_backend/drive_backend_util.cc
+++ b/chrome/browser/sync_file_system/drive_backend/drive_backend_util.cc
@@ -21,7 +21,7 @@
 
 void PutVersionToDB(int64_t version, LevelDBWrapper* db) {
   DCHECK(db);
-  db->Put(kDatabaseVersionKey, base::Int64ToString(version));
+  db->Put(kDatabaseVersionKey, base::NumberToString(version));
 }
 
 void PutServiceMetadataToDB(const ServiceMetadata& service_metadata,
@@ -49,7 +49,7 @@
   std::string value;
   bool success = tracker.SerializeToString(&value);
   DCHECK(success);
-  db->Put(kFileTrackerKeyPrefix + base::Int64ToString(tracker.tracker_id()),
+  db->Put(kFileTrackerKeyPrefix + base::NumberToString(tracker.tracker_id()),
           value);
 }
 
@@ -61,7 +61,7 @@
 
 void PutFileTrackerDeletionToDB(int64_t tracker_id, LevelDBWrapper* db) {
   DCHECK(db);
-  db->Delete(kFileTrackerKeyPrefix + base::Int64ToString(tracker_id));
+  db->Delete(kFileTrackerKeyPrefix + base::NumberToString(tracker_id));
 }
 
 bool HasFileAsParent(const FileDetails& details, const std::string& file_id) {
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
index d31d9cd..d6abcd4 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database.cc
@@ -1659,7 +1659,7 @@
 
     std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
     base::FilePath path = BuildDisplayPathForTracker(tracker);
-    dict->SetString("tracker_id", base::Int64ToString(tracker_id));
+    dict->SetString("tracker_id", base::NumberToString(tracker_id));
     dict->SetString("path", path.AsUTF8Unsafe());
     dict->SetString("file_id", tracker.file_id());
     TrackerKind tracker_kind = tracker.tracker_kind();
@@ -1684,7 +1684,7 @@
       dict->SetString("md5", details.md5());
       dict->SetString("etag", details.etag());
       dict->SetString("missing", details.missing() ? "true" : "false");
-      dict->SetString("change_id", base::Int64ToString(details.change_id()));
+      dict->SetString("change_id", base::NumberToString(details.change_id()));
     }
     trackers->Append(std::move(dict));
   }
@@ -1728,7 +1728,7 @@
       dict->SetString("md5", details.md5());
       dict->SetString("etag", details.etag());
       dict->SetString("missing", details.missing() ? "true" : "false");
-      dict->SetString("change_id", base::Int64ToString(details.change_id()));
+      dict->SetString("change_id", base::NumberToString(details.change_id()));
 
       std::vector<base::StringPiece> parents;
       for (int i = 0; i < details.parent_folder_ids_size(); ++i)
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_index.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database_index.cc
index ca536cf..01aaa7b 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database_index.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_index.cc
@@ -28,7 +28,7 @@
 //
 // NOTE
 // - Entries are sorted by keys.
-// - int64_t value is serialized as a string by base::Int64ToString().
+// - int64_t value is serialized as a string by base::NumberToString().
 // - ServiceMetadata, FileMetadata, and FileTracker values are serialized
 //   as a string by SerializeToString() of protocol buffers.
 //
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.cc
index d092a60..91f9d15 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk.cc
@@ -23,7 +23,7 @@
 //
 // NOTE
 // - Entries are sorted by keys.
-// - int64_t value is serialized as a string by base::Int64ToString().
+// - int64_t value is serialized as a string by base::NumberToString().
 // - ServiceMetadata, FileMetadata, and FileTracker values are serialized
 //   as a string by SerializeToString() of protocol buffers.
 //
@@ -143,11 +143,11 @@
 }
 
 std::string GenerateDirtyIDKey(int64_t tracker_id) {
-  return kDirtyIDKeyPrefix + base::Int64ToString(tracker_id);
+  return kDirtyIDKeyPrefix + base::NumberToString(tracker_id);
 }
 
 std::string GenerateDemotedDirtyIDKey(int64_t tracker_id) {
-  return kDemotedDirtyIDKeyPrefix + base::Int64ToString(tracker_id);
+  return kDemotedDirtyIDKeyPrefix + base::NumberToString(tracker_id);
 }
 
 void RemoveUnreachableItemsFromDB(LevelDBWrapper* db,
@@ -314,7 +314,7 @@
 bool MetadataDatabaseIndexOnDisk::GetFileTracker(int64_t tracker_id,
                                                  FileTracker* tracker) const {
   const std::string key =
-      kFileTrackerKeyPrefix + base::Int64ToString(tracker_id);
+      kFileTrackerKeyPrefix + base::NumberToString(tracker_id);
   std::string value;
   leveldb::Status status = db_->Get(key, &value);
 
@@ -765,7 +765,7 @@
     DeleteTrackerIndexes();
     BuildTrackerIndexes();
     db_->Put(kLastValidationTimeKey,
-             base::Int64ToString(base::Time::Now().ToInternalValue()));
+             base::NumberToString(base::Time::Now().ToInternalValue()));
   } else {
     num_dirty_trackers_ = CountDirtyTrackerInternal();
   }
@@ -782,7 +782,7 @@
   const std::string db_key = GenerateAppRootIDByAppIDKey(tracker.app_id());
   DCHECK(tracker.active());
   DCHECK(!DBHasKey(db_key));
-  db_->Put(db_key, base::Int64ToString(tracker.tracker_id()));
+  db_->Put(db_key, base::NumberToString(tracker.tracker_id()));
 }
 
 void MetadataDatabaseIndexOnDisk::UpdateInAppIDIndex(
@@ -805,7 +805,7 @@
     DCHECK(!DBHasKey(key));
 
     DVLOG(1) << "  Add to App root by App ID: " << new_tracker.app_id();
-    db_->Put(key, base::Int64ToString(new_tracker.tracker_id()));
+    db_->Put(key, base::NumberToString(new_tracker.tracker_id()));
   }
 }
 
@@ -849,7 +849,7 @@
 
   const std::string& file_id = new_tracker.file_id();
   const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id);
-  DCHECK(DBHasKey(prefix + base::Int64ToString(new_tracker.tracker_id())));
+  DCHECK(DBHasKey(prefix + base::NumberToString(new_tracker.tracker_id())));
 
   if (old_tracker.active() && !new_tracker.active()) {
     DeactivateInTrackerIDSetWithPrefix(
@@ -1106,7 +1106,7 @@
     const FileTracker& tracker) {
   DCHECK(tracker.tracker_id());
 
-  const std::string id_str = base::Int64ToString(tracker.tracker_id());
+  const std::string id_str = base::NumberToString(tracker.tracker_id());
   db_->Put(key_prefix + id_str, std::string());
   if (tracker.active())
     db_->Put(active_tracker_key, id_str);
@@ -1117,7 +1117,7 @@
     const std::string& key_prefix,
     int64_t tracker_id) {
   std::string value;
-  const std::string del_key = key_prefix + base::Int64ToString(tracker_id);
+  const std::string del_key = key_prefix + base::NumberToString(tracker_id);
   leveldb::Status status = db_->Get(del_key, &value);
   if (status.IsNotFound())
     return false;
@@ -1138,7 +1138,7 @@
     const std::string& active_tracker_key,
     const std::string& key_prefix,
     int64_t tracker_id) {
-  DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
+  DCHECK(DBHasKey(key_prefix + base::NumberToString(tracker_id)));
 
   std::string value;
   leveldb::Status status = db_->Get(active_tracker_key, &value);
@@ -1146,7 +1146,7 @@
   if (status.IsNotFound() ||
       (status.ok() && base::StringToInt64(value, &active_tracker_id))) {
     DCHECK(active_tracker_id != tracker_id);
-    db_->Put(active_tracker_key, base::Int64ToString(tracker_id));
+    db_->Put(active_tracker_key, base::NumberToString(tracker_id));
   }
 }
 
@@ -1154,7 +1154,7 @@
     const std::string& active_tracker_key,
     const std::string& key_prefix,
     int64_t tracker_id) {
-  DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
+  DCHECK(DBHasKey(key_prefix + base::NumberToString(tracker_id)));
 
   std::string value;
   leveldb::Status status = db_->Get(active_tracker_key, &value);
@@ -1188,7 +1188,7 @@
 MetadataDatabaseIndexOnDisk::NumEntries
 MetadataDatabaseIndexOnDisk::CountWithPrefix(const std::string& prefix,
                                              int64_t ignored_id) {
-  const std::string ignored = base::Int64ToString(ignored_id);
+  const std::string ignored = base::NumberToString(ignored_id);
 
   size_t count = 0;
   std::unique_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk_unittest.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk_unittest.cc
index e1dac42..dc606f2 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_index_on_disk_unittest.cc
@@ -206,7 +206,7 @@
   EXPECT_TRUE(db->Commit().ok());
 
   const std::string key =
-      kFileTrackerKeyPrefix + base::Int64ToString(kOrphanedFileTrackerID);
+      kFileTrackerKeyPrefix + base::NumberToString(kOrphanedFileTrackerID);
   std::string value;
   EXPECT_TRUE(db->Get(key, &value).ok());
 
diff --git a/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc b/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc
index 6517f12..a769afb8f 100644
--- a/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/metadata_database_unittest.cc
@@ -205,7 +205,7 @@
 
  protected:
   std::string GenerateFileID() {
-    return "file_id_" + base::Int64ToString(next_file_id_number_++);
+    return "file_id_" + base::NumberToString(next_file_id_number_++);
   }
 
   int64_t GetTrackerIDByFileID(const std::string& file_id) {
@@ -287,7 +287,7 @@
 
     std::unique_ptr<LevelDBWrapper> wrapper(new LevelDBWrapper(std::move(db)));
 
-    wrapper->Put(kDatabaseVersionKey, base::Int64ToString(3));
+    wrapper->Put(kDatabaseVersionKey, base::NumberToString(3));
     SetUpServiceMetadata(wrapper.get());
 
     return wrapper;
@@ -320,8 +320,8 @@
     details->add_parent_folder_ids(parent.file_id());
     details->set_title(title);
     details->set_file_kind(FILE_KIND_FILE);
-    details->set_md5(
-        "md5_value_" + base::Int64ToString(next_md5_sequence_number_++));
+    details->set_md5("md5_value_" +
+                     base::NumberToString(next_md5_sequence_number_++));
     details->set_change_id(current_change_id_);
     return file;
   }
@@ -458,8 +458,8 @@
 
   void ApplyContentChangeToMetadata(FileMetadata* file) {
     FileDetails* details = file->mutable_details();
-    details->set_md5(
-        "md5_value_" + base::Int64ToString(next_md5_sequence_number_++));
+    details->set_md5("md5_value_" +
+                     base::NumberToString(next_md5_sequence_number_++));
     details->set_change_id(++current_change_id_);
   }
 
@@ -570,7 +570,7 @@
         tracker.tracker_id(), &tracker_in_metadata_database));
 
     SCOPED_TRACE("Expect equivalent tracker[" +
-                 base::Int64ToString(tracker.tracker_id()) + "]");
+                 base::NumberToString(tracker.tracker_id()) + "]");
     ExpectEquivalent(&tracker, &tracker_in_metadata_database);
   }
 
diff --git a/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc b/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc
index b8eb17a0..a97973d 100644
--- a/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc
+++ b/chrome/browser/sync_file_system/drive_backend/register_app_task_unittest.cc
@@ -112,8 +112,7 @@
     *sync_root_tracker.mutable_synced_details() = sync_root_details;
     sync_root_tracker.set_active(true);
 
-    db->Put(kDatabaseVersionKey,
-            base::Int64ToString(kCurrentDatabaseVersion));
+    db->Put(kDatabaseVersionKey, base::NumberToString(kCurrentDatabaseVersion));
     PutServiceMetadataToDB(service_metadata, db);
     PutFileMetadataToDB(sync_root_metadata, db);
     PutFileTrackerToDB(sync_root_tracker, db);
diff --git a/chrome/browser/sync_file_system/drive_backend/sync_worker.cc b/chrome/browser/sync_file_system/drive_backend/sync_worker.cc
index a36b9b3..11c6feb 100644
--- a/chrome/browser/sync_file_system/drive_backend/sync_worker.cc
+++ b/chrome/browser/sync_file_system/drive_backend/sync_worker.cc
@@ -501,7 +501,7 @@
     if (syncer->sync_action() == SYNC_ACTION_DELETED &&
         syncer->url().is_valid() &&
         storage::VirtualPath::IsRootPath(syncer->url().path())) {
-      RegisterOrigin(syncer->url().origin(), base::DoNothing());
+      RegisterOrigin(syncer->url().origin().GetURL(), base::DoNothing());
     }
     should_check_conflict_ = true;
   }
@@ -518,7 +518,7 @@
       syncer->sync_action() != SYNC_ACTION_NONE) {
     storage::FileSystemURL updated_url = syncer->url();
     if (!syncer->target_path().empty()) {
-      updated_url = CreateSyncableFileSystemURL(syncer->url().origin(),
+      updated_url = CreateSyncableFileSystemURL(syncer->url().origin().GetURL(),
                                                 syncer->target_path());
     }
     for (auto& observer : observers_) {
@@ -529,7 +529,7 @@
   }
 
   if (status == SYNC_STATUS_UNKNOWN_ORIGIN && syncer->url().is_valid())
-    RegisterOrigin(syncer->url().origin(), base::DoNothing());
+    RegisterOrigin(syncer->url().origin().GetURL(), base::DoNothing());
 
   if (syncer->needs_remote_change_listing() &&
       !listing_remote_changes_) {
diff --git a/chrome/browser/sync_file_system/fake_remote_change_processor.cc b/chrome/browser/sync_file_system/fake_remote_change_processor.cc
index a892961..7da1a40e 100644
--- a/chrome/browser/sync_file_system/fake_remote_change_processor.cc
+++ b/chrome/browser/sync_file_system/fake_remote_change_processor.cc
@@ -73,7 +73,7 @@
   base::FilePath ancestor = storage::VirtualPath::DirName(url.path());
   while (true) {
     storage::FileSystemURL ancestor_url =
-        CreateSyncableFileSystemURL(url.origin(), ancestor);
+        CreateSyncableFileSystemURL(url.origin().GetURL(), ancestor);
     if (!ancestor_url.is_valid())
       break;
 
diff --git a/chrome/browser/sync_file_system/local/local_file_change_tracker.cc b/chrome/browser/sync_file_system/local/local_file_change_tracker.cc
index 14926c8..f1aaca3 100644
--- a/chrome/browser/sync_file_system/local/local_file_change_tracker.cc
+++ b/chrome/browser/sync_file_system/local/local_file_change_tracker.cc
@@ -288,7 +288,7 @@
     // Advance |iter| before calling ResetForURL to avoid the iterator
     // invalidation in it.
     ++iter;
-    if (url.origin() == origin && url.type() == type)
+    if (url.origin().GetURL() == origin && url.type() == type)
       ResetForURL(url, change_seq, batch.get());
   }
 
@@ -298,7 +298,7 @@
     // Advance |iter| before calling ResetForURL to avoid the iterator
     // invalidation in it.
     ++iter;
-    if (url.origin() == origin && url.type() == type)
+    if (url.origin().GetURL() == origin && url.type() == type)
       ResetForURL(url, change_seq, batch.get());
   }
 
@@ -389,8 +389,8 @@
             file_util->CreateFileEnumerator(context.get(), url, false));
         base::FilePath path_each;
         while (!(path_each = enumerator->Next()).empty()) {
-          dirty_files.push(CreateSyncableFileSystemURL(
-                  url.origin(), path_each));
+          dirty_files.push(
+              CreateSyncableFileSystemURL(url.origin().GetURL(), path_each));
         }
         break;
       }
diff --git a/chrome/browser/sync_file_system/local/local_file_sync_context.cc b/chrome/browser/sync_file_system/local/local_file_sync_context.cc
index 660d8554..3234d03 100644
--- a/chrome/browser/sync_file_system/local/local_file_sync_context.cc
+++ b/chrome/browser/sync_file_system/local/local_file_sync_context.cc
@@ -379,8 +379,7 @@
             local_path, url_for_sync, std::move(operation_callback));
       } else {
         FileSystemURL dir_url = file_system_context->CreateCrackedFileSystemURL(
-            url_for_sync.origin(),
-            url_for_sync.mount_type(),
+            url_for_sync.origin().GetURL(), url_for_sync.mount_type(),
             storage::VirtualPath::DirName(url_for_sync.virtual_path()));
         file_system_context->operation_runner()->CreateDirectory(
             dir_url, false /* exclusive */, true /* recursive */,
@@ -549,7 +548,7 @@
   DCHECK(io_task_runner_->RunsTasksInCurrentSequence());
   if (shutdown_on_io_)
     return;
-  UpdateChangesForOrigin(url.origin(), base::DoNothing());
+  UpdateChangesForOrigin(url.origin().GetURL(), base::DoNothing());
   if (url_syncable_callback_.is_null() ||
       sync_status()->IsWriting(url_waiting_sync_on_io_)) {
     return;
@@ -687,7 +686,7 @@
   (*tracker_ptr)->GetNextChangedURLs(&urls, 0);
   for (FileSystemURLQueue::iterator iter = urls.begin();
        iter != urls.end(); ++iter) {
-    origins_with_changes->insert(iter->origin());
+    origins_with_changes->insert(iter->origin().GetURL());
   }
 
   // Creates snapshot directory.
@@ -986,7 +985,7 @@
   }
 
   // Since a sync has finished the number of changes must have been updated.
-  UpdateChangesForOrigin(url.origin(), base::DoNothing());
+  UpdateChangesForOrigin(url.origin().GetURL(), base::DoNothing());
 }
 
 void LocalFileSyncContext::FinalizeSnapshotSyncOnIOThread(
@@ -997,7 +996,7 @@
   sync_status()->EndWriting(url);
 
   // Since a sync has finished the number of changes must have been updated.
-  UpdateChangesForOrigin(url.origin(), base::DoNothing());
+  UpdateChangesForOrigin(url.origin().GetURL(), base::DoNothing());
 }
 
 void LocalFileSyncContext::DidApplyRemoteChange(
diff --git a/chrome/browser/sync_file_system/local/local_file_sync_service.cc b/chrome/browser/sync_file_system/local/local_file_sync_service.cc
index e67e6bb..0e3184c 100644
--- a/chrome/browser/sync_file_system/local/local_file_sync_service.cc
+++ b/chrome/browser/sync_file_system/local/local_file_sync_service.cc
@@ -187,14 +187,14 @@
 void LocalFileSyncService::HasPendingLocalChanges(
     const FileSystemURL& url,
     const HasPendingLocalChangeCallback& callback) {
-  if (!base::ContainsKey(origin_to_contexts_, url.origin())) {
+  if (!base::ContainsKey(origin_to_contexts_, url.origin().GetURL())) {
     base::ThreadTaskRunnerHandle::Get()->PostTask(
         FROM_HERE,
         base::BindOnce(callback, SYNC_FILE_ERROR_INVALID_URL, false));
     return;
   }
   sync_context_->HasPendingLocalChanges(
-      origin_to_contexts_[url.origin()], url, callback);
+      origin_to_contexts_[url.origin().GetURL()], url, callback);
 }
 
 void LocalFileSyncService::PromoteDemotedChanges(
@@ -217,8 +217,8 @@
 
 void LocalFileSyncService::GetLocalFileMetadata(
     const FileSystemURL& url, const SyncFileMetadataCallback& callback) {
-  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin()));
-  sync_context_->GetFileMetadata(origin_to_contexts_[url.origin()],
+  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin().GetURL()));
+  sync_context_->GetFileMetadata(origin_to_contexts_[url.origin().GetURL()],
                                  url, callback);
 }
 
@@ -227,20 +227,20 @@
     const PrepareChangeCallback& callback) {
   DVLOG(1) << "PrepareForProcessRemoteChange: " << url.DebugString();
 
-  if (!base::ContainsKey(origin_to_contexts_, url.origin())) {
+  if (!base::ContainsKey(origin_to_contexts_, url.origin().GetURL())) {
     // This could happen if a remote sync is triggered for the app that hasn't
     // been initialized in this service.
     DCHECK(profile_);
     // The given url.origin() must be for valid installed app.
     const extensions::Extension* extension =
         extensions::ExtensionRegistry::Get(profile_)
-            ->enabled_extensions().GetAppByURL(url.origin());
+            ->enabled_extensions()
+            .GetAppByURL(url.origin().GetURL());
     if (!extension) {
       util::Log(
-          logging::LOG_WARNING,
-          FROM_HERE,
+          logging::LOG_WARNING, FROM_HERE,
           "PrepareForProcessRemoteChange called for non-existing origin: %s",
-          url.origin().spec().c_str());
+          url.origin().GetURL().spec().c_str());
 
       // The extension has been uninstalled and this method is called
       // before the remote changes for the origin are removed.
@@ -255,16 +255,16 @@
         content::BrowserContext::GetStoragePartitionForSite(profile_, site_url)
             ->GetFileSystemContext();
     MaybeInitializeFileSystemContext(
-        url.origin(), file_system_context.get(),
+        url.origin().GetURL(), file_system_context.get(),
         base::Bind(&LocalFileSyncService::DidInitializeForRemoteSync,
                    AsWeakPtr(), url, base::RetainedRef(file_system_context),
                    callback));
     return;
   }
 
-  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin()));
+  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin().GetURL()));
   sync_context_->PrepareForSync(
-      origin_to_contexts_[url.origin()], url,
+      origin_to_contexts_[url.origin().GetURL()], url,
       LocalFileSyncContext::SYNC_EXCLUSIVE,
       base::Bind(&PrepareForProcessRemoteChangeCallbackAdapter, callback));
 }
@@ -274,15 +274,14 @@
     const base::FilePath& local_path,
     const FileSystemURL& url,
     const SyncStatusCallback& callback) {
-  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin()));
+  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin().GetURL()));
   util::Log(logging::LOG_VERBOSE, FROM_HERE,
             "[Remote -> Local] ApplyRemoteChange: %s on %s",
             change.DebugString().c_str(),
             url.DebugString().c_str());
 
   sync_context_->ApplyRemoteChange(
-      origin_to_contexts_[url.origin()],
-      change, local_path, url,
+      origin_to_contexts_[url.origin().GetURL()], change, local_path, url,
       base::Bind(&LocalFileSyncService::DidApplyRemoteChange, AsWeakPtr(),
                  callback));
 }
@@ -291,19 +290,19 @@
     const FileSystemURL& url,
     bool clear_local_changes,
     const base::Closure& completion_callback) {
-  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin()));
+  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin().GetURL()));
   sync_context_->FinalizeExclusiveSync(
-      origin_to_contexts_[url.origin()],
-      url, clear_local_changes, completion_callback);
+      origin_to_contexts_[url.origin().GetURL()], url, clear_local_changes,
+      completion_callback);
 }
 
 void LocalFileSyncService::RecordFakeLocalChange(
     const FileSystemURL& url,
     const FileChange& change,
     const SyncStatusCallback& callback) {
-  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin()));
-  sync_context_->RecordFakeLocalChange(origin_to_contexts_[url.origin()],
-                                       url, change, callback);
+  DCHECK(base::ContainsKey(origin_to_contexts_, url.origin().GetURL()));
+  sync_context_->RecordFakeLocalChange(
+      origin_to_contexts_[url.origin().GetURL()], url, change, callback);
 }
 
 void LocalFileSyncService::OnChangesAvailableInOrigins(
@@ -397,7 +396,7 @@
     callback.Run(status, SyncFileMetadata(), FileChangeList());
     return;
   }
-  origin_to_contexts_[url.origin()] = file_system_context;
+  origin_to_contexts_[url.origin().GetURL()] = file_system_context;
   PrepareForProcessRemoteChange(url, callback);
 }
 
@@ -472,9 +471,9 @@
 
   const FileSystemURL& url = sync_file_info.url;
   if (status != SYNC_STATUS_OK || changes.empty()) {
-    DCHECK(base::ContainsKey(origin_to_contexts_, url.origin()));
+    DCHECK(base::ContainsKey(origin_to_contexts_, url.origin().GetURL()));
     sync_context_->FinalizeSnapshotSync(
-        origin_to_contexts_[url.origin()], url, status,
+        origin_to_contexts_[url.origin().GetURL()], url, status,
         base::Bind(callback, status, url));
     return;
   }
@@ -494,7 +493,7 @@
 LocalChangeProcessor* LocalFileSyncService::GetLocalChangeProcessor(
     const FileSystemURL& url) {
   if (!get_local_change_processor_.is_null())
-    return get_local_change_processor_.Run(url.origin());
+    return get_local_change_processor_.Run(url.origin().GetURL());
   DCHECK(local_change_processor_);
   return local_change_processor_;
 }
diff --git a/chrome/browser/sync_file_system/local/local_file_sync_status.cc b/chrome/browser/sync_file_system/local/local_file_sync_status.cc
index 994bb86..71cbba6d 100644
--- a/chrome/browser/sync_file_system/local/local_file_sync_status.cc
+++ b/chrome/browser/sync_file_system/local/local_file_sync_status.cc
@@ -19,7 +19,7 @@
 using OriginAndType = LocalFileSyncStatus::OriginAndType;
 
 OriginAndType GetOriginAndType(const storage::FileSystemURL& url) {
-  return std::make_pair(url.origin(), url.type());
+  return std::make_pair(url.origin().GetURL(), url.type());
 }
 
 base::FilePath NormalizePath(const base::FilePath& path) {
diff --git a/chrome/browser/sync_file_system/local/root_delete_helper.cc b/chrome/browser/sync_file_system/local/root_delete_helper.cc
index a71823a1..f2203b3 100644
--- a/chrome/browser/sync_file_system/local/root_delete_helper.cc
+++ b/chrome/browser/sync_file_system/local/root_delete_helper.cc
@@ -29,7 +29,8 @@
       SyncFileSystemBackend::GetBackend(file_system_context);
   DCHECK(backend);
   DCHECK(backend->change_tracker());
-  backend->change_tracker()->ResetForFileSystem(url.origin(), url.type());
+  backend->change_tracker()->ResetForFileSystem(url.origin().GetURL(),
+                                                url.type());
 }
 
 }  // namespace
@@ -61,7 +62,7 @@
             "%s", url_.DebugString().c_str());
 
   file_system_context_->DeleteFileSystem(
-      url_.origin(), url_.type(),
+      url_.origin().GetURL(), url_.type(),
       base::Bind(&RootDeleteHelper::DidDeleteFileSystem,
                  weak_factory_.GetWeakPtr()));
 }
@@ -90,8 +91,7 @@
 
   // Reopening the filesystem.
   file_system_context_->sandbox_delegate()->OpenFileSystem(
-      url_.origin(),
-      url_.type(),
+      url_.origin().GetURL(), url_.type(),
       storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
       base::Bind(&RootDeleteHelper::DidOpenFileSystem,
                  weak_factory_.GetWeakPtr()),
diff --git a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc
index a3a1736..07a8fc8 100644
--- a/chrome/browser/sync_file_system/local/sync_file_system_backend.cc
+++ b/chrome/browser/sync_file_system/local/sync_file_system_backend.cc
@@ -120,18 +120,18 @@
   DCHECK(CanHandleType(url.type()));
 
   if (skip_initialize_syncfs_service_for_testing_) {
-    GetDelegate()->OpenFileSystem(url.origin(), url.type(), mode,
-                                  std::move(callback),
-                                  GetSyncableFileSystemRootURI(url.origin()));
+    GetDelegate()->OpenFileSystem(
+        url.origin().GetURL(), url.type(), mode, std::move(callback),
+        GetSyncableFileSystemRootURI(url.origin().GetURL()));
     return;
   }
 
   // It is safe to pass Unretained(this) since |context_| owns it.
-  SyncStatusCallback initialize_callback =
-      base::Bind(&SyncFileSystemBackend::DidInitializeSyncFileSystemService,
-                 base::Unretained(this), base::RetainedRef(context_),
-                 url.origin(), url.type(), mode, base::Passed(&callback));
-  InitializeSyncFileSystemService(url.origin(), initialize_callback);
+  SyncStatusCallback initialize_callback = base::Bind(
+      &SyncFileSystemBackend::DidInitializeSyncFileSystemService,
+      base::Unretained(this), base::RetainedRef(context_),
+      url.origin().GetURL(), url.type(), mode, base::Passed(&callback));
+  InitializeSyncFileSystemService(url.origin().GetURL(), initialize_callback);
 }
 
 storage::AsyncFileUtil* SyncFileSystemBackend::GetAsyncFileUtil(
diff --git a/chrome/browser/sync_file_system/syncable_file_system_util.cc b/chrome/browser/sync_file_system/syncable_file_system_util.cc
index 59d2d6c..ff321b6 100644
--- a/chrome/browser/sync_file_system/syncable_file_system_util.cc
+++ b/chrome/browser/sync_file_system/syncable_file_system_util.cc
@@ -71,8 +71,7 @@
     storage::FileSystemContext* file_system_context,
     const FileSystemURL& syncable_url) {
   return ExternalMountPoints::GetSystemInstance()->CreateExternalFileSystemURL(
-      syncable_url.origin(),
-      kSyncableMountNameForInternalSync,
+      syncable_url.origin().GetURL(), kSyncableMountNameForInternalSync,
       syncable_url.path());
 }
 
@@ -80,9 +79,8 @@
                                     std::string* serialized_url) {
   if (!url.is_valid() || url.type() != storage::kFileSystemTypeSyncable)
     return false;
-  *serialized_url =
-      GetSyncableFileSystemRootURI(url.origin()).spec() +
-      url.path().AsUTF8Unsafe();
+  *serialized_url = GetSyncableFileSystemRootURI(url.origin().GetURL()).spec() +
+                    url.path().AsUTF8Unsafe();
   return true;
 }
 
diff --git a/chrome/browser/task_manager/sampling/task_group.cc b/chrome/browser/task_manager/sampling/task_group.cc
index 6a9adda5..43f4fed 100644
--- a/chrome/browser/task_manager/sampling/task_group.cc
+++ b/chrome/browser/task_manager/sampling/task_group.cc
@@ -96,7 +96,7 @@
 #endif  // defined(OS_CHROMEOS)
       expected_on_bg_done_flags_(kBackgroundRefreshTypesMask),
       current_on_bg_done_flags_(0),
-      platform_independent_cpu_usage_(0.0),
+      platform_independent_cpu_usage_(std::numeric_limits<double>::quiet_NaN()),
       swapped_mem_bytes_(-1),
       memory_footprint_(-1),
       gpu_memory_(-1),
diff --git a/chrome/browser/tracing/crash_service_uploader.cc b/chrome/browser/tracing/crash_service_uploader.cc
index b13e4fc2..e009130 100644
--- a/chrome/browser/tracing/crash_service_uploader.cc
+++ b/chrome/browser/tracing/crash_service_uploader.cc
@@ -98,8 +98,8 @@
       response_code =
           simple_url_loader_->ResponseInfo()->headers->response_code();
     }
-    feedback =
-        "Uploading failed, response code: " + base::IntToString(response_code);
+    feedback = "Uploading failed, response code: " +
+               base::NumberToString(response_code);
   }
 
   base::PostTaskWithTraits(
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 65ffae47..5f17f84 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1795,7 +1795,6 @@
       "//services/ws/public/cpp",
       "//services/ws/public/mojom",
       "//ui/base/ime",
-      "//ui/base/user_activity",
       "//ui/chromeos",
       "//ui/chromeos/events",
       "//ui/compositor_extra",
@@ -3069,8 +3068,6 @@
       "views/apps/shaped_app_window_targeter.h",
       "views/color_chooser_aura.cc",
       "views/color_chooser_aura.h",
-      "views/crypto_module_password_dialog_view.cc",
-      "views/crypto_module_password_dialog_view.h",
       "views/desktop_capture/desktop_media_list_view.cc",
       "views/desktop_capture/desktop_media_list_view.h",
       "views/desktop_capture/desktop_media_picker_views.cc",
@@ -3153,6 +3150,10 @@
       "app_list/search/arc/arc_app_data_search_provider.h",
       "app_list/search/arc/arc_app_data_search_result.cc",
       "app_list/search/arc/arc_app_data_search_result.h",
+      "app_list/search/arc/arc_app_reinstall_app_result.cc",
+      "app_list/search/arc/arc_app_reinstall_app_result.h",
+      "app_list/search/arc/arc_app_reinstall_search_provider.cc",
+      "app_list/search/arc/arc_app_reinstall_search_provider.h",
       "app_list/search/arc/arc_app_shortcut_search_result.cc",
       "app_list/search/arc/arc_app_shortcut_search_result.h",
       "app_list/search/arc/arc_app_shortcuts_search_provider.cc",
@@ -3605,6 +3606,13 @@
       "webui/certificates_handler.cc",
       "webui/certificates_handler.h",
     ]
+
+    if (use_aura) {
+      sources += [
+        "views/crypto_module_password_dialog_view.cc",
+        "views/crypto_module_password_dialog_view.h",
+      ]
+    }
   }
 
   if (use_udev) {
diff --git a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
index 335abae..40962b9 100644
--- a/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
+++ b/chrome/browser/ui/android/infobars/translate_compact_infobar.cc
@@ -22,20 +22,7 @@
 using base::android::JavaParamRef;
 using base::android::ScopedJavaLocalRef;
 
-// Default values for the finch parameters. (used when the corresponding finch
-// parameter does not exist.)
-const int kDefaultAutoAlwaysThreshold = 5;
-const int kDefaultAutoNeverThreshold = 10;
-const int kDefaultMaxNumberOfAutoAlways = 2;
-const int kDefaultMaxNumberOfAutoNever = 2;
-
 // Finch parameter names:
-const char kTranslateAutoAlwaysThreshold[] = "translate_auto_always_threshold";
-const char kTranslateAutoNeverThreshold[] = "translate_auto_never_threshold";
-const char kTranslateMaxNumberOfAutoAlways[] =
-    "translate_max_number_of_auto_always";
-const char kTranslateMaxNumberOfAutoNever[] =
-    "translate_max_number_of_auto_never";
 const char kTranslateTabDefaultTextColor[] = "translate_tab_default_text_color";
 
 // ChromeTranslateClient
@@ -95,17 +82,10 @@
   if (action == InfoBarAndroid::ACTION_TRANSLATE) {
     action_flags_ |= FLAG_TRANSLATE;
     delegate->Translate();
-    if (!delegate->ShouldAlwaysTranslate() && ShouldAutoAlwaysTranslate()) {
+    if (delegate->ShouldAutoAlwaysTranslate()) {
       JNIEnv* env = base::android::AttachCurrentThread();
       Java_TranslateCompactInfoBar_setAutoAlwaysTranslate(env,
                                                           GetJavaInfoBar());
-
-      // Auto-always is triggered by the line above.  Need to increment the
-      // auto-always counter.
-      delegate->IncrementTranslationAutoAlwaysCount();
-      // Reset translateAcceptedCount so that auto-always could be triggered
-      // again.
-      delegate->ResetTranslationAcceptedCount();
     }
   } else if (action == InfoBarAndroid::ACTION_TRANSLATE_SHOW_ORIGINAL) {
     action_flags_ |= FLAG_REVERT;
@@ -172,12 +152,6 @@
   }
 }
 
-bool TranslateCompactInfoBar::ShouldAutoAlwaysTranslate() {
-  translate::TranslateInfoBarDelegate* delegate = GetDelegate();
-  return (delegate->GetTranslationAcceptedCount() >= AutoAlwaysThreshold() &&
-          delegate->GetTranslationAutoAlwaysCount() < MaxNumberOfAutoAlways());
-}
-
 jboolean TranslateCompactInfoBar::ShouldAutoNeverTranslate(
     JNIEnv* env,
     const base::android::JavaParamRef<jobject>& obj,
@@ -189,30 +163,7 @@
   if (!IsDeclinedByUser())
     return false;
 
-  translate::TranslateInfoBarDelegate* delegate = GetDelegate();
-  // Don't trigger if it's off the record or already blocked.
-  if (delegate->is_off_the_record() ||
-      !delegate->IsTranslatableLanguageByPrefs())
-    return false;
-
-  int auto_never_count = delegate->GetTranslationAutoNeverCount();
-
-  // At the beginning (auto_never_count == 0), deniedCount starts at 0 and is
-  // off-by-one (because this checking is done before increment). However, after
-  // auto-never is triggered once (auto_never_count > 0), deniedCount starts at
-  // 1.  So there is no off-by-one by then.
-  int off_by_one = auto_never_count == 0 ? 1 : 0;
-
-  bool never_translate = (delegate->GetTranslationDeniedCount() + off_by_one >=
-                              AutoNeverThreshold() &&
-                          auto_never_count < MaxNumberOfAutoNever());
-  if (never_translate) {
-    // Auto-never will be triggered.  Need to increment the auto-never counter.
-    delegate->IncrementTranslationAutoNeverCount();
-    // Reset translateDeniedCount so that auto-never could be triggered again.
-    delegate->ResetTranslationDeniedCount();
-  }
-  return never_translate;
+  return GetDelegate()->ShouldAutoNeverTranslate();
 }
 
 // Returns true if the current tab is an incognito tab.
@@ -237,23 +188,6 @@
   return value <= 0 ? default_value : value;
 }
 
-int TranslateCompactInfoBar::AutoAlwaysThreshold() {
-  return GetParam(kTranslateAutoAlwaysThreshold, kDefaultAutoAlwaysThreshold);
-}
-
-int TranslateCompactInfoBar::AutoNeverThreshold() {
-  return GetParam(kTranslateAutoNeverThreshold, kDefaultAutoNeverThreshold);
-}
-
-int TranslateCompactInfoBar::MaxNumberOfAutoAlways() {
-  return GetParam(kTranslateMaxNumberOfAutoAlways,
-                  kDefaultMaxNumberOfAutoAlways);
-}
-
-int TranslateCompactInfoBar::MaxNumberOfAutoNever() {
-  return GetParam(kTranslateMaxNumberOfAutoNever, kDefaultMaxNumberOfAutoNever);
-}
-
 int TranslateCompactInfoBar::TabDefaultTextColor() {
   return GetParam(kTranslateTabDefaultTextColor, 0);
 }
diff --git a/chrome/browser/ui/android/infobars/translate_compact_infobar.h b/chrome/browser/ui/android/infobars/translate_compact_infobar.h
index 37a5cbc..7daae673 100644
--- a/chrome/browser/ui/android/infobars/translate_compact_infobar.h
+++ b/chrome/browser/ui/android/infobars/translate_compact_infobar.h
@@ -38,9 +38,6 @@
                                 int option,
                                 jboolean value);
 
-  // Check whether we should automatically trigger "Always Translate".
-  bool ShouldAutoAlwaysTranslate();
-
   // Check whether we should automatically trigger "Never Translate Language".
   jboolean ShouldAutoNeverTranslate(
       JNIEnv* env,
@@ -70,22 +67,6 @@
   // Get the value of a specified finch parameter in TranslateCompactUI.  If the
   // finch parameter does not exist, default_value will be returned.
   int GetParam(const std::string& paramName, int default_value);
-  // Get the value of the finch parameter: translate_auto_always_threshold.
-  // If the number of times a user consecutively translates is equal to this
-  // number, infobar will automatically trigger "Always Translate".
-  int AutoAlwaysThreshold();
-  // Get the value of the finch parameter: translate_auto_never_threshold.
-  // If the number of times a user consecutively denies the translate infobar is
-  // equal to this number, infobar will automatically trigger "Never Translate".
-  int AutoNeverThreshold();
-  // Get the value of the finch parameter: translate_max_number_of_auto_always.
-  // This is the maximum number of times to trigger "Always Translate"
-  // automatically.
-  int MaxNumberOfAutoAlways();
-  // Get the value of the finch parameter: translate_max_number_of_auto_never.
-  // This is the maximum number of times to trigger "Never Translate"
-  // automatically.
-  int MaxNumberOfAutoNever();
   // Get the value of the finch parameter: translate_tab_default_text_color.
   // Default value is 0, which means using TabLayout default color.
   // If it's not 0, we will set the text color manually based on the value.
diff --git a/chrome/browser/ui/android/usb_chooser_dialog_android.cc b/chrome/browser/ui/android/usb_chooser_dialog_android.cc
index 646fe3f..db9b27d 100644
--- a/chrome/browser/ui/android/usb_chooser_dialog_android.cc
+++ b/chrome/browser/ui/android/usb_chooser_dialog_android.cc
@@ -93,7 +93,7 @@
 
   DCHECK_LE(index, item_id_map_.size());
   int item_id = next_item_id_++;
-  std::string item_id_str = base::IntToString(item_id);
+  std::string item_id_str = base::NumberToString(item_id);
   item_id_map_.insert(item_id_map_.begin() + index, item_id_str);
 
   base::string16 device_name = controller_->GetOption(index);
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service_unittest.cc b/chrome/browser/ui/app_list/app_list_syncable_service_unittest.cc
index 98b4463..28eb8e0fb 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service_unittest.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service_unittest.cc
@@ -777,7 +777,7 @@
   for (int i = 0; i < max_items_in_first_page - 1; ++i) {
     std::unique_ptr<ChromeAppListItem> item =
         std::make_unique<ChromeAppListItem>(
-            profile_.get(), GenerateId("item_id" + base::IntToString(i)),
+            profile_.get(), GenerateId("item_id" + base::NumberToString(i)),
             model_updater());
     item->SetPosition(last_app_position);
     model_updater()->AddItem(std::move(item));
@@ -803,7 +803,7 @@
   std::unique_ptr<ChromeAppListItem> app_item =
       std::make_unique<ChromeAppListItem>(
           profile_.get(),
-          GenerateId("item_id" + base::IntToString(max_items_in_first_page)),
+          GenerateId("item_id" + base::NumberToString(max_items_in_first_page)),
           model_updater());
   app_item->SetPosition(last_app_position.CreateBetween(page_break_position));
   model_updater()->AddItem(std::move(app_item));
@@ -825,7 +825,7 @@
   for (int i = 0; i < max_items_in_first_page - 1; ++i) {
     std::unique_ptr<ChromeAppListItem> item =
         std::make_unique<ChromeAppListItem>(
-            profile_.get(), GenerateId("item_id" + base::IntToString(i)),
+            profile_.get(), GenerateId("item_id" + base::NumberToString(i)),
             model_updater());
     item->SetPosition(last_app_position);
     model_updater()->AddItem(std::move(item));
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
index 790e7606..c3ff5e8 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.cc
@@ -485,15 +485,6 @@
     return;
   }
 
-  if (app_connection_holder_->instance_version() <
-      arc::mojom::AppInstance::kRequestAppIconMinVersion) {
-    LOG(WARNING) << "Using depreciated interface ("
-                 << app_connection_holder_->instance_version()
-                 << ") to request icons.";
-    SendIconRequestDeprecated(app_id, *app_info, descriptor);
-    return;
-  }
-
   SendIconRequest(app_id, *app_info, descriptor);
 }
 
@@ -523,31 +514,6 @@
   }
 }
 
-void ArcAppListPrefs::SendIconRequestDeprecated(
-    const std::string& app_id,
-    const AppInfo& app_info,
-    const ArcAppIconDescriptor& descriptor) {
-  if (app_info.icon_resource_id.empty()) {
-    auto* app_instance = ARC_GET_INSTANCE_FOR_METHOD(app_connection_holder_,
-                                                     RequestAppIconDeprecated);
-    if (!app_instance)
-      return;  // Error is logged in macro.
-    app_instance->RequestAppIconDeprecated(
-        app_info.package_name, app_info.activity,
-        static_cast<arc::mojom::ScaleFactor>(descriptor.scale_factor));
-  } else {
-    auto* app_instance = ARC_GET_INSTANCE_FOR_METHOD(
-        app_connection_holder_, RequestShortcutIconDeprecated);
-    if (!app_instance)
-      return;  // Error is logged in macro.
-    app_instance->RequestShortcutIconDeprecated(
-        app_info.icon_resource_id,
-        static_cast<arc::mojom::ScaleFactor>(descriptor.scale_factor),
-        base::BindOnce(&ArcAppListPrefs::OnIconDeprecated,
-                       weak_ptr_factory_.GetWeakPtr(), app_id, descriptor));
-  }
-}
-
 void ArcAppListPrefs::MaybeRequestIcon(const std::string& app_id,
                                        const ArcAppIconDescriptor& descriptor) {
   if (!IsIconRequestRecorded(app_id, descriptor))
@@ -786,7 +752,7 @@
   const base::Time time = base::Time::Now();
   arc::ArcAppScopedPrefUpdate update(prefs_, app_id, arc::prefs::kArcApps);
   base::DictionaryValue* app_dict = update.Get();
-  const std::string string_value = base::Int64ToString(time.ToInternalValue());
+  const std::string string_value = base::NumberToString(time.ToInternalValue());
   app_dict->SetString(kLastLaunchTime, string_value);
 
   for (auto& observer : observer_list_)
@@ -1102,7 +1068,7 @@
   // actual install time in Android side.
   if (GetInstallTime(app_id).is_null()) {
     std::string install_time_str =
-        base::Int64ToString(base::Time::Now().ToInternalValue());
+        base::NumberToString(base::Time::Now().ToInternalValue());
     app_dict->SetString(kInstallTime, install_time_str);
   }
 
@@ -1213,8 +1179,8 @@
                                      arc::prefs::kArcPackages);
   base::DictionaryValue* package_dict = update.Get();
   const std::string id_str =
-      base::Int64ToString(package.last_backup_android_id);
-  const std::string time_str = base::Int64ToString(package.last_backup_time);
+      base::NumberToString(package.last_backup_android_id);
+  const std::string time_str = base::NumberToString(package.last_backup_time);
 
   int old_package_version = -1;
   package_dict->GetInteger(kPackageVersion, &old_package_version);
@@ -1507,18 +1473,6 @@
     observer.OnPackageRemoved(package_name, true);
 }
 
-void ArcAppListPrefs::OnAppIconDeprecated(
-    const std::string& package_name,
-    const std::string& activity,
-    arc::mojom::ScaleFactor scale_factor,
-    const std::vector<uint8_t>& icon_png_data) {
-  const std::string app_id = GetAppId(package_name, activity);
-  // There is no info about original request dimension. Use active requests to
-  // notify all.
-  for (const auto& descriptor : active_icons_[app_id])
-    OnIconDeprecated(app_id, descriptor, icon_png_data);
-}
-
 void ArcAppListPrefs::OnIcon(const std::string& app_id,
                              const ArcAppIconDescriptor& descriptor,
                              const std::vector<uint8_t>& icon_png_data) {
@@ -1537,32 +1491,6 @@
   InstallIcon(app_id, descriptor, icon_png_data);
 }
 
-void ArcAppListPrefs::OnIconDeprecated(
-    const std::string& app_id,
-    const ArcAppIconDescriptor& descriptor,
-    const std::vector<uint8_t>& icon_png_data) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
-
-  if (icon_png_data.empty()) {
-    LOG(WARNING) << "Cannot fetch icon for " << app_id;
-    return;
-  }
-
-  constexpr int kLegacyIconDimension = 48;
-
-  // Icon may not have required dimension. We only can safely use legacy app
-  // dimension.
-  if (descriptor.dip_size == kLegacyIconDimension) {
-    OnIcon(app_id, descriptor, icon_png_data);
-    return;
-  }
-
-  resize_requests_.emplace_back(std::make_unique<ResizeRequest>(
-      weak_ptr_factory_.GetWeakPtr(), app_id, descriptor));
-  // Result will be delivered to |OnIconResized|
-  ImageDecoder::Start(resize_requests_.back().get(), icon_png_data);
-}
-
 void ArcAppListPrefs::OnIconResized(const std::string& app_id,
                                     const ArcAppIconDescriptor& descriptor,
                                     const std::vector<uint8_t>& icon_png_data) {
diff --git a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
index 16f033b..f449d00 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_list_prefs.h
@@ -9,6 +9,7 @@
 
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
 #include <unordered_set>
 #include <vector>
@@ -47,6 +48,10 @@
 class PrefRegistrySyncable;
 }  // namespace user_prefs
 
+namespace app_list {
+class ArcAppShortcutsSearchProviderTest;
+}  // namespace app_list
+
 // Declares shareable ARC app specific preferences, that keep information
 // about app attributes (name, package_name, activity) and its state. This
 // information is used to pre-create non-ready app items while ARC bridge
@@ -318,6 +323,7 @@
  private:
   friend class ChromeLauncherControllerTest;
   friend class ArcAppModelBuilderTest;
+  friend class app_list::ArcAppShortcutsSearchProviderTest;
   // To support deprecated mojom icon requests.
   class ResizeRequest;
 
@@ -341,10 +347,6 @@
   void OnUninstallShortcut(const std::string& package_name,
                            const std::string& intent_uri) override;
   void OnPackageRemoved(const std::string& package_name) override;
-  void OnAppIconDeprecated(const std::string& package_name,
-                           const std::string& activity,
-                           arc::mojom::ScaleFactor scale_factor,
-                           const std::vector<uint8_t>& icon_png_data) override;
   void OnIcon(const std::string& app_id,
               const ArcAppIconDescriptor& descriptor,
               const std::vector<uint8_t>& icon_png_data);
@@ -480,17 +482,11 @@
   // Schedules deletion of app folder with icons on file thread.
   void ScheduleAppFolderDeletion(const std::string& app_id);
 
-  // TODO(b/112035954): Remove following block of 4 methods that supports icon
+  // TODO(b/112035954): Remove following block of 2 methods that supports icon
   // using deprecated mojom. Once Android side change is propagated in builds we
   // can safely remove this. Sends icon request view mojom using old protocol.
   // In this protocol only icons of 48 pixels are supported. This requires
   // resizing icon to the requested size.
-  void SendIconRequestDeprecated(const std::string& app_id,
-                                 const AppInfo& app,
-                                 const ArcAppIconDescriptor& descriptor);
-  void OnIconDeprecated(const std::string& app_id,
-                        const ArcAppIconDescriptor& descriptor,
-                        const std::vector<uint8_t>& icon_png_data);
   void OnIconResized(const std::string& app_id,
                      const ArcAppIconDescriptor& descriptor,
                      const std::vector<uint8_t>& icon_png_data);
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc
index 29e94322..a17929a 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc
+++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.cc
@@ -122,10 +122,8 @@
 }
 
 void CrostiniAppModelBuilder::MaybeCreateRootFolder() {
-  if (root_folder_created_)
-    return;
-
-  root_folder_created_ = true;
+  // If a sync item exists for the root folder, then it has been created
+  // already.
   const app_list::AppListSyncableService::SyncItem* sync_item =
       GetSyncItem(crostini::kCrostiniFolderId);
   if (sync_item)
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.h b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.h
index faae2c6..b0a2cbeb 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.h
+++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder.h
@@ -38,14 +38,12 @@
 
   void OnCrostiniEnabledChanged();
 
-  // Creates root folder for Crostini apps in case it was not created or sync
-  // item does not exist. Once it is created sync item is allocated and it will
-  // be reusedto restore root folder on demand automatically.
+  // Creates root folder for Crostini apps in case it was not created (in which
+  // case the sync item will not exist). Once it is created, a sync item is
+  // allocated, and it will be reused to restore the root folder on demand
+  // automatically.
   void MaybeCreateRootFolder();
 
-  // Set to true in case root folder was created on demand.
-  bool root_folder_created_ = false;
-
   // Observer Crostini installation so we can start showing The Terminal app.
   std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_;
 
diff --git a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
index 9c805c44..4db8472 100644
--- a/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
+++ b/chrome/browser/ui/app_list/crostini/crostini_app_model_builder_unittest.cc
@@ -4,10 +4,13 @@
 
 #include "chrome/browser/ui/app_list/crostini/crostini_app_model_builder.h"
 
+#include <vector>
+
 #include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
 #include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
 #include "chrome/browser/chromeos/crostini/crostini_test_helper.h"
 #include "chrome/browser/chromeos/crostini/crostini_util.h"
+#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
 #include "chrome/browser/ui/app_list/app_list_test_util.h"
 #include "chrome/browser/ui/app_list/chrome_app_list_item.h"
 #include "chrome/browser/ui/app_list/test/fake_app_list_model_updater.h"
@@ -15,6 +18,7 @@
 #include "chrome/common/chrome_features.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/test/base/testing_profile.h"
+#include "extensions/browser/extension_system.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -77,6 +81,22 @@
   return result;
 }
 
+// For testing purposes, we want to pretend there are only crostini apps on the
+// system. This method removes the others.
+void RemoveNonCrostiniApps(app_list::AppListSyncableService* sync_service) {
+  std::vector<std::string> existing_item_ids;
+  for (const auto& pair : sync_service->sync_items()) {
+    existing_item_ids.emplace_back(pair.first);
+  }
+  for (const std::string& id : existing_item_ids) {
+    if (id == crostini::kCrostiniFolderId ||
+        id == crostini::kCrostiniTerminalId) {
+      continue;
+    }
+    sync_service->RemoveItem(id);
+  }
+}
+
 }  // namespace
 
 class CrostiniAppModelBuilderTest : public AppListTestBase {
@@ -87,6 +107,11 @@
   void SetUp() override {
     AppListTestBase::SetUp();
     test_helper_ = std::make_unique<CrostiniTestHelper>(profile());
+    model_updater_factory_scope_ = std::make_unique<
+        app_list::AppListSyncableService::ScopedModelUpdaterFactoryForTest>(
+        base::BindRepeating([]() -> std::unique_ptr<AppListModelUpdater> {
+          return std::make_unique<FakeAppListModelUpdater>();
+        }));
     CreateBuilder();
   }
 
@@ -97,17 +122,26 @@
   }
 
  protected:
+  AppListModelUpdater* GetModelUpdater() const {
+    return sync_service_->GetModelUpdater();
+  }
+
+  size_t GetModelItemCount() const {
+    return sync_service_->GetModelUpdater()->ItemCount();
+  }
+
   void CreateBuilder() {
-    model_updater_ = std::make_unique<FakeAppListModelUpdater>();
     controller_ = std::make_unique<test::TestAppListControllerDelegate>();
     builder_ = std::make_unique<CrostiniAppModelBuilder>(controller_.get());
-    builder_->Initialize(nullptr, profile_.get(), model_updater_.get());
+    sync_service_ = std::make_unique<app_list::AppListSyncableService>(
+        profile_.get(), extensions::ExtensionSystem::Get(profile_.get()));
+    RemoveNonCrostiniApps(sync_service_.get());
   }
 
   void ResetBuilder() {
     builder_.reset();
     controller_.reset();
-    model_updater_.reset();
+    sync_service_.reset();
   }
 
   crostini::CrostiniRegistryService* RegistryService() {
@@ -118,12 +152,16 @@
     return l10n_util::GetStringUTF8(IDS_CROSTINI_TERMINAL_APP_NAME);
   }
 
-  std::unique_ptr<FakeAppListModelUpdater> model_updater_;
   std::unique_ptr<test::TestAppListControllerDelegate> controller_;
+  std::unique_ptr<app_list::AppListSyncableService> sync_service_;
   std::unique_ptr<CrostiniAppModelBuilder> builder_;
   std::unique_ptr<CrostiniTestHelper> test_helper_;
 
  private:
+  std::unique_ptr<
+      app_list::AppListSyncableService::ScopedModelUpdaterFactoryForTest>
+      model_updater_factory_scope_;
+
   DISALLOW_COPY_AND_ASSIGN(CrostiniAppModelBuilderTest);
 };
 
@@ -136,41 +174,41 @@
       profile(), /*enable_crostini=*/false);
   CreateBuilder();
 
-  EXPECT_EQ(0u, model_updater_->ItemCount());
+  EXPECT_EQ(0u, GetModelItemCount());
 
   CrostiniTestHelper::EnableCrostini(profile());
   // Root folder + terminal app.
-  EXPECT_THAT(GetAppIds(model_updater_.get()),
+  EXPECT_THAT(GetAppIds(GetModelUpdater()),
               testing::UnorderedElementsAre(crostini::kCrostiniFolderId,
                                             crostini::kCrostiniTerminalId));
-  EXPECT_THAT(GetAppNames(model_updater_.get()),
+  EXPECT_THAT(GetAppNames(GetModelUpdater()),
               testing::UnorderedElementsAre(kRootFolderName,
                                             GetFullName(TerminalAppName())));
 
   CrostiniTestHelper::DisableCrostini(profile());
-  EXPECT_THAT(GetAppIds(model_updater_.get()),
+  EXPECT_THAT(GetAppIds(GetModelUpdater()),
               testing::ElementsAre(crostini::kCrostiniFolderId));
 }
 
 TEST_F(CrostiniAppModelBuilderTest, AppInstallation) {
   // Root folder + terminal app.
-  EXPECT_EQ(2u, model_updater_->ItemCount());
+  EXPECT_EQ(2u, GetModelItemCount());
 
   test_helper_->SetupDummyApps();
-  EXPECT_THAT(GetAppIds(model_updater_.get()),
+  EXPECT_THAT(GetAppIds(GetModelUpdater()),
               testing::UnorderedElementsAreArray(AppendRootFolderId(
                   RegistryService()->GetRegisteredAppIds())));
-  EXPECT_THAT(GetAppNames(model_updater_.get()),
+  EXPECT_THAT(GetAppNames(GetModelUpdater()),
               testing::UnorderedElementsAre(
                   kRootFolderName, GetFullName(TerminalAppName()),
                   GetFullName(kDummpyApp1Name), GetFullName(kDummpyApp2Name)));
 
   test_helper_->AddApp(
       CrostiniTestHelper::BasicApp(kBananaAppId, kBananaAppName));
-  EXPECT_THAT(GetAppIds(model_updater_.get()),
+  EXPECT_THAT(GetAppIds(GetModelUpdater()),
               testing::UnorderedElementsAreArray(AppendRootFolderId(
                   RegistryService()->GetRegisteredAppIds())));
-  EXPECT_THAT(GetAppNames(model_updater_.get()),
+  EXPECT_THAT(GetAppNames(GetModelUpdater()),
               testing::UnorderedElementsAre(
                   kRootFolderName, GetFullName(TerminalAppName()),
                   GetFullName(kDummpyApp1Name), GetFullName(kDummpyApp2Name),
@@ -181,14 +219,14 @@
 TEST_F(CrostiniAppModelBuilderTest, UpdateApps) {
   test_helper_->SetupDummyApps();
   // 3 apps + root folder.
-  EXPECT_EQ(4u, model_updater_->ItemCount());
+  EXPECT_EQ(4u, GetModelItemCount());
 
   // Setting NoDisplay to true should hide an app.
   vm_tools::apps::App dummy1 = test_helper_->GetApp(0);
   dummy1.set_no_display(true);
   test_helper_->AddApp(dummy1);
-  EXPECT_EQ(3u, model_updater_->ItemCount());
-  EXPECT_THAT(GetAppIds(model_updater_.get()),
+  EXPECT_EQ(3u, GetModelItemCount());
+  EXPECT_THAT(GetAppIds(GetModelUpdater()),
               testing::UnorderedElementsAre(
                   crostini::kCrostiniFolderId, crostini::kCrostiniTerminalId,
                   CrostiniTestHelper::GenerateAppId(kDummpyApp2Id)));
@@ -196,8 +234,8 @@
   // Setting NoDisplay to false should unhide an app.
   dummy1.set_no_display(false);
   test_helper_->AddApp(dummy1);
-  EXPECT_EQ(4u, model_updater_->ItemCount());
-  EXPECT_THAT(GetAppIds(model_updater_.get()),
+  EXPECT_EQ(4u, GetModelItemCount());
+  EXPECT_THAT(GetAppIds(GetModelUpdater()),
               testing::UnorderedElementsAreArray(AppendRootFolderId(
                   RegistryService()->GetRegisteredAppIds())));
 
@@ -205,11 +243,11 @@
   vm_tools::apps::App dummy2 =
       CrostiniTestHelper::BasicApp(kDummpyApp2Id, kAppNewName);
   test_helper_->AddApp(dummy2);
-  EXPECT_EQ(4u, model_updater_->ItemCount());
-  EXPECT_THAT(GetAppIds(model_updater_.get()),
+  EXPECT_EQ(4u, GetModelItemCount());
+  EXPECT_THAT(GetAppIds(GetModelUpdater()),
               testing::UnorderedElementsAreArray(AppendRootFolderId(
                   RegistryService()->GetRegisteredAppIds())));
-  EXPECT_THAT(GetAppNames(model_updater_.get()),
+  EXPECT_THAT(GetAppNames(GetModelUpdater()),
               testing::UnorderedElementsAre(
                   kRootFolderName, GetFullName(TerminalAppName()),
                   GetFullName(kDummpyApp1Name), GetFullName(kAppNewName)));
@@ -219,27 +257,49 @@
 TEST_F(CrostiniAppModelBuilderTest, RemoveApps) {
   test_helper_->SetupDummyApps();
   // 3 apps + root folder.
-  EXPECT_EQ(4u, model_updater_->ItemCount());
+  EXPECT_EQ(4u, GetModelItemCount());
 
   // Remove dummy1
   test_helper_->RemoveApp(0);
-  EXPECT_EQ(3u, model_updater_->ItemCount());
+  EXPECT_EQ(3u, GetModelItemCount());
 
   // Remove dummy2
   test_helper_->RemoveApp(0);
-  EXPECT_EQ(2u, model_updater_->ItemCount());
+  EXPECT_EQ(2u, GetModelItemCount());
+}
+
+// Tests that the crostini folder is recreated on demand.
+TEST_F(CrostiniAppModelBuilderTest, RecreateFolder) {
+  CrostiniTestHelper::EnableCrostini(profile());
+  // Root folder + terminal app.
+  EXPECT_THAT(GetAppNames(GetModelUpdater()),
+              testing::UnorderedElementsAre(kRootFolderName,
+                                            GetFullName(TerminalAppName())));
+
+  // Move the terminal out and delete the old folder.
+  GetModelUpdater()->MoveItemToFolder(crostini::kCrostiniTerminalId, "");
+  GetModelUpdater()->RemoveItem(crostini::kCrostiniFolderId);
+  EXPECT_THAT(GetAppNames(GetModelUpdater()),
+              testing::UnorderedElementsAre(TerminalAppName()));
+
+  // Adding a new app should recreate the folder.
+  test_helper_->AddApp(
+      CrostiniTestHelper::BasicApp(kDummpyApp2Id, kDummpyApp2Name));
+  EXPECT_THAT(GetAppNames(GetModelUpdater()),
+              testing::UnorderedElementsAre(TerminalAppName(), kRootFolderName,
+                                            GetFullName(kDummpyApp2Name)));
 }
 
 // Test that the Terminal app is removed when Crostini is disabled.
 TEST_F(CrostiniAppModelBuilderTest, DisableCrostini) {
   test_helper_->SetupDummyApps();
   // 3 apps + root folder.
-  EXPECT_EQ(4u, model_updater_->ItemCount());
+  EXPECT_EQ(4u, GetModelItemCount());
 
   // The uninstall flow removes all apps before setting the CrostiniEnabled pref
   // to false, so we need to do that explicitly too.
   RegistryService()->ClearApplicationList(crostini::kCrostiniDefaultVmName);
   CrostiniTestHelper::DisableCrostini(profile());
   // Root folder is left. We rely on default handling of empty folder.
-  EXPECT_EQ(1u, model_updater_->ItemCount());
+  EXPECT_EQ(1u, GetModelItemCount());
 }
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
new file mode 100644
index 0000000..f9e831a
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.cc
@@ -0,0 +1,54 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h"
+
+#include <utility>
+
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+
+namespace app_list {
+
+namespace {
+constexpr char kPlayStoreAppUrlPrefix[] =
+    "https://play.google.com/store/apps/details?id=";
+
+// We choose a default app reinstallation relevance; This ranks app reinstall
+// app result as a top result typically.
+constexpr float kAppReinstallRelevance = 0.7;
+
+}  // namespace
+
+ArcAppReinstallAppResult::ArcAppReinstallAppResult(
+    const arc::mojom::AppReinstallCandidatePtr& mojom_data,
+    const gfx::ImageSkia& skia_icon,
+    bool is_recommendation) {
+  ash::mojom::SearchResultMetadataPtr metadata = {base::in_place};
+  set_id(kPlayStoreAppUrlPrefix + mojom_data->package_name);
+  SetResultType(ash::SearchResultType::kPlayStoreApp);
+  SetTitle(base::UTF8ToUTF16(mojom_data->title));
+  SetDetails(base::UTF8ToUTF16(metadata->id));
+  SetDisplayType(is_recommendation
+                     ? ash::SearchResultDisplayType::kRecommendation
+                     : ash::SearchResultDisplayType::kTile);
+  set_relevance(kAppReinstallRelevance);
+
+  SetIcon(skia_icon);
+  SetChipIcon(skia_icon);
+
+  if (mojom_data->star_rating != 0.0f)
+    SetRating(mojom_data->star_rating);
+}
+
+ArcAppReinstallAppResult::~ArcAppReinstallAppResult() = default;
+
+void ArcAppReinstallAppResult::Open(int /*event_flags*/) {
+  RecordAction(base::UserMetricsAction("ArcAppReinstall_Clicked"));
+  arc::LaunchPlayStoreWithUrl(id());
+}
+
+}  // 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
new file mode 100644
index 0000000..9309662
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h
@@ -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.
+
+#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 "base/macros.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+#include "components/arc/common/app.mojom.h"
+#include "ui/gfx/image/image_skia.h"
+
+namespace app_list {
+
+// A ChromeSearchResult that shows an App Reinstall candidate result. These are
+// Arc++ apps that can be installed on this device. Opens the app in the play
+// store when Open is called.
+class ArcAppReinstallAppResult : public ChromeSearchResult {
+ public:
+  ArcAppReinstallAppResult(
+      const arc::mojom::AppReinstallCandidatePtr& mojom_data,
+      const gfx::ImageSkia& skia_icon,
+      bool is_recommendation);
+  ~ArcAppReinstallAppResult() override;
+
+  // ArcAppResult:
+  void Open(int event_flags) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ArcAppReinstallAppResult);
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_ARC_ARC_APP_REINSTALL_APP_RESULT_H_
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
new file mode 100644
index 0000000..bd5ea9e
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.cc
@@ -0,0 +1,313 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/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 "base/metrics/histogram_macros.h"
+#include "base/strings/strcat.h"
+#include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/time/time.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ui/app_list/search/arc/arc_app_reinstall_app_result.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+#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 "extensions/grit/extensions_browser_resources.h"
+
+namespace {
+// Seconds in between refreshes;
+constexpr int64_t kRefreshSeconds = 1800;
+constexpr char kAppListLatency[] = "Apps.AppListRecommendedResponse.Latency";
+constexpr char kAppListCounts[] = "Apps.AppListRecommendedResponse.Count";
+
+void RecordUmaResponseParseResult(arc::mojom::AppReinstallState result) {
+  UMA_HISTOGRAM_ENUMERATION("Apps.AppListRecommendedResponse", result);
+}
+
+std::string LimitIconSizeWithFife(const std::string& icon_url,
+                                  int icon_dimension) {
+  // We append a suffix to icon url
+  DCHECK_GT(icon_dimension, 0);
+  return base::StrCat({icon_url, "=s", base::NumberToString(icon_dimension)});
+}
+
+}  // namespace
+
+namespace app_list {
+
+ArcAppReinstallSearchProvider::ArcAppReinstallSearchProvider(
+    Profile* profile,
+    unsigned int max_result_count)
+    : profile_(profile),
+      max_result_count_(max_result_count),
+      icon_dimension_(
+          app_list::AppListConfig::instance().GetPreferredIconDimension(
+              ash::SearchResultDisplayType::kRecommendation)),
+      app_fetch_timer_(std::make_unique<base::RepeatingTimer>()),
+      weak_ptr_factory_(this) {
+  DCHECK(profile_ != nullptr);
+  ArcAppListPrefs::Get(profile_)->AddObserver(this);
+  MaybeUpdateFetching();
+}
+
+ArcAppReinstallSearchProvider::~ArcAppReinstallSearchProvider() {
+  ArcAppListPrefs::Get(profile_)->RemoveObserver(this);
+}
+
+void ArcAppReinstallSearchProvider::BeginRepeatingFetch() {
+  // If already running, do not re-start.
+  if (app_fetch_timer_->IsRunning())
+    return;
+
+  app_fetch_timer_->Start(FROM_HERE,
+                          base::TimeDelta::FromSeconds(kRefreshSeconds), this,
+                          &ArcAppReinstallSearchProvider::StartFetch);
+  StartFetch();
+}
+
+void ArcAppReinstallSearchProvider::StopRepeatingFetch() {
+  weak_ptr_factory_.InvalidateWeakPtrs();
+  app_fetch_timer_->AbandonAndStop();
+  loaded_value_.clear();
+  icon_urls_.clear();
+  loading_icon_urls_.clear();
+  UpdateResults();
+}
+
+void ArcAppReinstallSearchProvider::Start(const base::string16& query) {
+  if (query_is_empty_ == query.empty())
+    return;
+
+  query_is_empty_ = query.empty();
+  UpdateResults();
+}
+
+void ArcAppReinstallSearchProvider::StartFetch() {
+  arc::mojom::AppInstance* app_instance =
+      arc::ArcServiceManager::Get()
+          ? ARC_GET_INSTANCE_FOR_METHOD(
+                arc::ArcServiceManager::Get()->arc_bridge_service()->app(),
+                GetAppReinstallCandidates)
+          : nullptr;
+  if (app_instance == nullptr)
+    return;
+
+  app_instance->GetAppReinstallCandidates(base::BindOnce(
+      &ArcAppReinstallSearchProvider::OnGetAppReinstallCandidates,
+      weak_ptr_factory_.GetWeakPtr(), base::Time::Now()));
+}
+
+void ArcAppReinstallSearchProvider::OnGetAppReinstallCandidates(
+    base::Time start_time,
+    arc::mojom::AppReinstallState state,
+    std::vector<arc::mojom::AppReinstallCandidatePtr> results) {
+  RecordUmaResponseParseResult(state);
+  DCHECK_NE(start_time, base::Time::UnixEpoch());
+  UMA_HISTOGRAM_TIMES(kAppListLatency, base::Time::Now() - start_time);
+  UMA_HISTOGRAM_COUNTS_100(kAppListCounts, results.size());
+
+  if (state != arc::mojom::AppReinstallState::REQUEST_SUCCESS) {
+    LOG(ERROR) << "Failed to get reinstall candidates: " << state;
+    return;
+  }
+  loaded_value_.clear();
+
+  for (const auto& candidate : results) {
+    // only keep candidates with icons.
+    if (candidate->icon_url != base::nullopt) {
+      loaded_value_.push_back(candidate.Clone());
+    }
+  }
+  UpdateResults();
+}
+
+void ArcAppReinstallSearchProvider::UpdateResults() {
+  // We clear results if there are none from the server, or the user has entered
+  // a non-zero query.
+  if (loaded_value_.empty() || !query_is_empty_) {
+    if (loaded_value_.empty())
+      icon_urls_.clear();
+    ClearResults();
+    return;
+  }
+
+  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_);
+  DCHECK(prefs != nullptr);
+
+  std::vector<std::unique_ptr<ChromeSearchResult>> new_results;
+  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;
+
+    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);
+
+    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, /*is_recommendation=*/true));
+    }
+  }
+
+  // Remove unused icons.
+  std::unordered_set<std::string> unused_icon_urls;
+  for (const auto& it : icon_urls_) {
+    if (used_icon_urls.find(it.first) == used_icon_urls.end()) {
+      // This url is used, remove.
+      unused_icon_urls.insert(it.first);
+    }
+  }
+
+  for (const std::string& url : unused_icon_urls) {
+    icon_urls_.erase(url);
+    loading_icon_urls_.erase(url);
+  }
+
+  // Now we are ready with new_results. do we actually need to replace things on
+  // screen?
+  if (!ResultsIdentical(results(), new_results)) {
+    SwapResults(&new_results);
+  }
+}
+
+void ArcAppReinstallSearchProvider::MaybeUpdateFetching() {
+  ArcAppListPrefs* prefs = ArcAppListPrefs::Get(profile_);
+  std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
+      prefs->GetApp(arc::kPlayStoreAppId);
+  if (app_info && app_info->ready)
+    BeginRepeatingFetch();
+  else
+    StopRepeatingFetch();
+}
+
+void ArcAppReinstallSearchProvider::OnAppRegistered(
+    const std::string& app_id,
+    const ArcAppListPrefs::AppInfo& app_info) {
+  OnAppStatesChanged(app_id, app_info);
+}
+
+void ArcAppReinstallSearchProvider::OnAppStatesChanged(
+    const std::string& app_id,
+    const ArcAppListPrefs::AppInfo& app_info) {
+  if (app_id == arc::kPlayStoreAppId)
+    MaybeUpdateFetching();
+}
+
+void ArcAppReinstallSearchProvider::OnAppRemoved(const std::string& app_id) {
+  if (app_id == arc::kPlayStoreAppId)
+    MaybeUpdateFetching();
+}
+
+void ArcAppReinstallSearchProvider::OnInstallationStarted(
+    const std::string& package_name) {
+  UpdateResults();
+}
+void ArcAppReinstallSearchProvider::OnInstallationFinished(
+    const std::string& package_name,
+    bool success) {
+  UpdateResults();
+}
+
+void ArcAppReinstallSearchProvider::OnPackageInstalled(
+    const arc::mojom::ArcPackageInfo& package_info) {
+  UpdateResults();
+}
+
+void ArcAppReinstallSearchProvider::OnPackageRemoved(
+    const std::string& package_name,
+    bool uninstalled) {
+  UpdateResults();
+}
+
+void ArcAppReinstallSearchProvider::SetTimerForTesting(
+    std::unique_ptr<base::RepeatingTimer> timer) {
+  app_fetch_timer_ = std::move(timer);
+}
+
+// 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;
+  }
+  const std::vector<gfx::ImageSkiaRep> image_reps =
+      skia_ptr->second.image_reps();
+  for (const gfx::ImageSkiaRep& rep : image_reps)
+    skia_ptr->second.RemoveRepresentation(rep.scale());
+  DCHECK_LE(skia_ptr->second.width(), icon_dimension_);
+
+  // ImageSkia is now ready to serve, move to the done list and update the
+  // screen.
+  icon_urls_[icon_url] = skia_ptr->second;
+  loading_icon_urls_.erase(icon_url);
+  UpdateResults();
+}
+
+bool ArcAppReinstallSearchProvider::ResultsIdentical(
+    const std::vector<std::unique_ptr<ChromeSearchResult>>& old_results,
+    const std::vector<std::unique_ptr<ChromeSearchResult>>& new_results) {
+  if (old_results.size() != new_results.size()) {
+    return false;
+  }
+  for (size_t i = 0; i < old_results.size(); ++i) {
+    const ChromeSearchResult& old_result = *(old_results[i]);
+    const ChromeSearchResult& new_result = *(new_results[i]);
+    if (!old_result.icon().BackedBySameObjectAs(new_result.icon())) {
+      return false;
+    }
+    if (old_result.title() != new_result.title()) {
+      return false;
+    }
+    if (old_result.id() != new_result.id()) {
+      return false;
+    }
+    if (old_result.relevance() != new_result.relevance()) {
+      return false;
+    }
+    if (old_result.rating() != new_result.rating()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+}  // namespace app_list
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
new file mode 100644
index 0000000..2ea9f9d
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.h
@@ -0,0 +1,135 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UI_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>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#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/search_provider.h"
+#include "ui/gfx/image/image_skia.h"
+
+class Profile;
+FORWARD_DECLARE_TEST(ArcAppReinstallSearchProviderTest,
+                     TestResultsWithSearchChanged);
+FORWARD_DECLARE_TEST(ArcAppReinstallSearchProviderTest,
+                     TestResultsWithAppsChanged);
+FORWARD_DECLARE_TEST(ArcAppReinstallSearchProviderTest,
+                     TestResultListComparison);
+
+namespace app_list {
+
+// A search provider that returns app candidates that are reinstallation
+// candidates. The current provider of candidates for this provider is the Fast
+// App Reinstall API. This Provider returns a list of applications, in
+// preference order. Apps are returned as cards for empty query items - and with
+// queries we match with regular expression and then return only matching as app
+// list results.
+//
+// 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 {
+ public:
+  // Constructor receives the Profile in order to
+  // instantiate App results. Ownership is not taken.
+  //
+  // We also give a max result count, so that the provider only instantiates the
+  // correct number of results for output.
+  ArcAppReinstallSearchProvider(Profile* profile,
+                                unsigned int max_result_count);
+
+  ~ArcAppReinstallSearchProvider() override;
+
+  // SearchProvider:
+  void Start(const base::string16& query) override;
+
+  // Used by unit tests. SearchProvider takes ownership of pointer.
+  void SetTimerForTesting(std::unique_ptr<base::RepeatingTimer> timer);
+
+ private:
+  FRIEND_TEST_ALL_PREFIXES(::ArcAppReinstallSearchProviderTest,
+                           TestResultsWithSearchChanged);
+  FRIEND_TEST_ALL_PREFIXES(::ArcAppReinstallSearchProviderTest,
+                           TestResultsWithAppsChanged);
+  FRIEND_TEST_ALL_PREFIXES(::ArcAppReinstallSearchProviderTest,
+                           TestResultListComparison);
+
+  // Called to start fetching from our server for this result set. Called when
+  // the play store becomes available.
+  void StartFetch();
+
+  void BeginRepeatingFetch();
+  void StopRepeatingFetch();
+
+  // Based on any change in results or query, updates the results appropriately.
+  void UpdateResults();
+
+  // If start_time is UnixEpoch, indicates a manual call.
+  void OnGetAppReinstallCandidates(
+      base::Time start_time,
+      arc::mojom::AppReinstallState state,
+      std::vector<arc::mojom::AppReinstallCandidatePtr> results);
+
+  void MaybeUpdateFetching();
+
+  // ArcAppListPrefs::Observer:
+  // We listen for app state / registration for the play store, which is our
+  // gate to turn on fetching.
+  void OnAppRegistered(const std::string& app_id,
+                       const ArcAppListPrefs::AppInfo& app_info) override;
+  void OnAppStatesChanged(const std::string& app_id,
+                          const ArcAppListPrefs::AppInfo& app_info) override;
+  void OnAppRemoved(const std::string& id) override;
+  void OnInstallationStarted(const std::string& package_name) override;
+  void OnInstallationFinished(const std::string& package_name,
+                              bool success) override;
+  void OnPackageInstalled(
+      const arc::mojom::ArcPackageInfo& package_info) override;
+  void OnPackageRemoved(const std::string& package_name,
+                        bool uninstalled) override;
+
+  // For icon load callback, in OnGetAppReinstallCandidates
+  void OnIconLoaded(const std::string& icon_url);
+
+  // Are both lists of chrome search results of the same title in the same
+  // order?
+  static bool ResultsIdentical(
+      const std::vector<std::unique_ptr<ChromeSearchResult>>& old_results,
+      const std::vector<std::unique_ptr<ChromeSearchResult>>& new_results);
+
+  Profile* const profile_;
+  const unsigned int max_result_count_;
+  const int icon_dimension_;
+
+  // This is the latest loaded set of reinstallation candidates loaded from the
+  // Play Store. This is used inside UpdateResults() to choose a subset of at
+  // most max_result_count_ results, which are then shown to the user._
+  std::vector<arc::mojom::AppReinstallCandidatePtr> loaded_value_;
+  bool query_is_empty_ = true;
+
+  // Repeating timer to fetch results.
+  std::unique_ptr<base::RepeatingTimer> app_fetch_timer_;
+
+  // Url to imageskia. This list is for icons that have been fully loaded.
+  std::unordered_map<std::string, gfx::ImageSkia> icon_urls_;
+
+  // url to imageskia of icons being loaded.
+  std::unordered_map<std::string, gfx::ImageSkia> loading_icon_urls_;
+
+  base::WeakPtrFactory<ArcAppReinstallSearchProvider> weak_ptr_factory_;
+  DISALLOW_COPY_AND_ASSIGN(ArcAppReinstallSearchProvider);
+};
+
+}  // namespace app_list
+
+#endif  // CHROME_BROWSER_UI_APP_LIST_SEARCH_ARC_ARC_APP_REINSTALL_SEARCH_PROVIDER_H_
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
new file mode 100644
index 0000000..bb4e842
--- /dev/null
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider_unittest.cc
@@ -0,0 +1,242 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.h"
+
+#include <map>
+#include <utility>
+
+#include "base/command_line.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"
+#include "chrome/browser/ui/app_list/arc/arc_app_test.h"
+#include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
+#include "chrome/browser/ui/app_list/search/chrome_search_result.h"
+#include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/arc/common/app.mojom.h"
+#include "components/arc/test/fake_app_instance.h"
+#include "extensions/grit/extensions_browser_resources.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/image/image_skia.h"
+
+using testing::ByRef;
+using testing::Eq;
+
+class ArcAppReinstallSearchProviderTest : public AppListTestBase {
+ protected:
+  void SetUp() override {
+    AppListTestBase::SetUp();
+    arc_app_test_.SetUp(profile_.get());
+    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();
+    app_provider_->SetTimerForTesting(std::move(timer));
+    app_candidate_ptr_ = arc::mojom::AppReinstallCandidate::New();
+  }
+
+  void TearDown() override {
+    mock_timer_ = nullptr;
+    app_provider_.reset(nullptr);
+    arc_app_test_.TearDown();
+    AppListTestBase::TearDown();
+  }
+
+  arc::FakeAppInstance* app_instance() { return arc_app_test_.app_instance(); }
+
+  void SendPlayStoreApp() {
+    arc::mojom::AppInfo app;
+    app.name = "Play Store";
+    app.package_name = arc::kPlayStorePackage;
+    app.activity = arc::kPlayStoreActivity;
+
+    app_instance()->RefreshAppList();
+    app_instance()->SendRefreshAppList({app});
+  }
+
+  arc::mojom::ArcPackageInfoPtr GetPackagePtr(const std::string& package_name) {
+    arc::mojom::ArcPackageInfo package;
+    package.package_name = package_name;
+    package.sync = false;
+    return package.Clone();
+  }
+
+  // Owned by |app_provider_|.
+  base::MockRepeatingTimer* mock_timer_;
+  ArcAppTest arc_app_test_;
+  std::unique_ptr<app_list::ArcAppReinstallSearchProvider> app_provider_;
+  arc::mojom::AppReinstallCandidatePtr app_candidate_ptr_;
+};
+
+namespace {
+class TestSearchResult : public ChromeSearchResult {
+ public:
+  void Open(int event_flags) override {}
+  void SetId(const std::string& str) {
+    // set_id is protected in chromesearchresult.
+    ChromeSearchResult::set_id(str);
+  }
+};
+}  // namespace
+
+TEST_F(ArcAppReinstallSearchProviderTest, NoResultWithoutPlayStore) {
+  EXPECT_EQ(0u, app_provider_->results().size());
+  EXPECT_FALSE(mock_timer_->IsRunning());
+}
+
+TEST_F(ArcAppReinstallSearchProviderTest, TestTimerOn) {
+  EXPECT_EQ(0, app_instance()->get_app_reinstall_callback_count());
+  SendPlayStoreApp();
+  EXPECT_TRUE(mock_timer_->IsRunning());
+  EXPECT_EQ(1, app_instance()->get_app_reinstall_callback_count());
+  mock_timer_->Fire();
+  EXPECT_EQ(2, app_instance()->get_app_reinstall_callback_count());
+  // We expect no results since there are no apps we added to the candidate
+  // list.
+  EXPECT_EQ(0u, app_provider_->results().size());
+
+  // Now, stop!
+  arc_app_test_.StopArcInstance();
+  EXPECT_FALSE(mock_timer_->IsRunning());
+}
+
+TEST_F(ArcAppReinstallSearchProviderTest, TestResultsWithSearchChanged) {
+  std::vector<arc::mojom::AppReinstallCandidatePtr> candidates;
+  candidates.emplace_back(arc::mojom::AppReinstallCandidate::New(
+      "com.package.fakepackage1", "Title of first package",
+      "http://icon.com/icon1", 15, 4.7));
+  candidates.emplace_back(arc::mojom::AppReinstallCandidate::New(
+      "com.package.fakepackage2", "Title of second package",
+      "http://icon.com/icon2", 15, 4.3));
+  app_instance()->SetAppReinstallCandidates(candidates);
+  EXPECT_EQ(0, app_instance()->get_app_reinstall_callback_count());
+  SendPlayStoreApp();
+  EXPECT_EQ(1, app_instance()->get_app_reinstall_callback_count());
+  // So we should have populated exactly 0 apps, since we expect loading to
+  // finish later.
+  EXPECT_EQ(0u, app_provider_->results().size());
+  EXPECT_EQ(2u, app_provider_->loading_icon_urls_.size());
+
+  // Fake the load of 2 icons.
+  app_provider_->OnIconLoaded("http://icon.com/icon1");
+  EXPECT_EQ(1u, app_provider_->loading_icon_urls_.size());
+  app_provider_->OnIconLoaded("http://icon.com/icon2");
+  ASSERT_EQ(2u, app_provider_->results().size());
+
+  EXPECT_EQ(
+      "https://play.google.com/store/apps/details?id=com.package.fakepackage1",
+      app_provider_->results()[0]->id());
+
+  // Test that we set to 0 results when having a query, or when arc is turned
+  // off.
+  app_provider_->Start(base::UTF8ToUTF16("non empty query"));
+  EXPECT_EQ(0u, app_provider_->results().size());
+  // Verify that all icons are still loaded.
+  EXPECT_EQ(2u, app_provider_->icon_urls_.size());
+  EXPECT_EQ(0u, app_provider_->loading_icon_urls_.size());
+  app_provider_->Start(base::string16());
+  EXPECT_EQ(2u, app_provider_->results().size());
+
+  app_instance()->SendInstallationStarted("com.package.fakepackage1");
+  EXPECT_EQ(1u, app_provider_->results().size());
+
+  // We should see that only 1 icon is loaded in our final results.
+  EXPECT_EQ(1u, app_provider_->icon_urls_.size());
+
+  // When arc is turned off, we should get no results and that we get no
+  arc_app_test_.StopArcInstance();
+  EXPECT_EQ(0u, app_provider_->results().size());
+  // We expect icons to be deleted when arc is stopped.
+  EXPECT_EQ(0u, app_provider_->icon_urls_.size());
+}
+
+TEST_F(ArcAppReinstallSearchProviderTest, TestResultsWithAppsChanged) {
+  std::vector<arc::mojom::AppReinstallCandidatePtr> candidates;
+  // We do something unlikely and sneaky here: we give the same icon to two
+  // apps. Unlikely, but possible.
+  candidates.emplace_back(arc::mojom::AppReinstallCandidate::New(
+      "com.package.fakepackage1", "Title of first package",
+      "http://icon.com/icon1", 15, 4.7));
+  candidates.emplace_back(arc::mojom::AppReinstallCandidate::New(
+      "com.package.fakepackage2", "Title of second package",
+      "http://icon.com/icon1", 15, 4.3));
+  app_instance()->SetAppReinstallCandidates(candidates);
+  EXPECT_EQ(0, app_instance()->get_app_reinstall_callback_count());
+  SendPlayStoreApp();
+  EXPECT_EQ(1, app_instance()->get_app_reinstall_callback_count());
+  // So we should have populated exactly 0 apps, since we expect loading to
+  // finish later.
+  EXPECT_EQ(0u, app_provider_->results().size());
+
+  // Fake the load of 1 icon.
+  app_provider_->OnIconLoaded("http://icon.com/icon1");
+  ASSERT_EQ(2u, app_provider_->results().size());
+
+  EXPECT_EQ(
+      "https://play.google.com/store/apps/details?id=com.package.fakepackage1",
+      app_provider_->results()[0]->id());
+
+  app_instance()->SendInstallationStarted("com.package.fakepackage1");
+  EXPECT_EQ(1u, app_provider_->results().size());
+  app_instance()->InstallPackage(GetPackagePtr("com.package.fakepackage1"));
+  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());
+}
+
+TEST_F(ArcAppReinstallSearchProviderTest, TestResultListComparison) {
+  std::vector<std::unique_ptr<ChromeSearchResult>> a, b;
+  EXPECT_TRUE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+  a.emplace_back(new TestSearchResult);
+
+  // different sizes.
+  EXPECT_FALSE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+  b.emplace_back(new TestSearchResult);
+  EXPECT_TRUE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+  // Different Titles.
+  a[0]->SetTitle(base::UTF8ToUTF16("fake_title"));
+  EXPECT_FALSE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+  b[0]->SetTitle(base::UTF8ToUTF16("fake_title"));
+  EXPECT_TRUE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+
+  // Different ID.
+  static_cast<TestSearchResult*>(a[0].get())->SetId("id");
+  EXPECT_FALSE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+  static_cast<TestSearchResult*>(b[0].get())->SetId(a[0]->id());
+  EXPECT_TRUE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+
+  // Different relevance
+  a[0]->set_relevance(0.7);
+  EXPECT_FALSE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+  b[0]->set_relevance(a[0]->relevance());
+  EXPECT_TRUE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+
+  // Different Rating.
+  a[0]->SetRating(0.3);
+  EXPECT_FALSE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+  b[0]->SetRating(a[0]->rating());
+  EXPECT_TRUE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+
+  // for icon equality.
+  gfx::ImageSkia aimg =
+                     *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+                         IDR_APP_DEFAULT_ICON),
+                 bimg =
+                     *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
+                         IDR_EXTENSION_DEFAULT_ICON);
+  ASSERT_FALSE(aimg.BackedBySameObjectAs(bimg));
+  a[0]->SetIcon(aimg);
+  b[0]->SetIcon(bimg);
+  EXPECT_FALSE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+  b[0]->SetIcon(aimg);
+  EXPECT_TRUE(app_list::ArcAppReinstallSearchProvider::ResultsIdentical(a, b));
+}
diff --git a/chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider.cc b/chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider.cc
index f5fe268..8ba7586 100644
--- a/chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider.cc
@@ -9,6 +9,7 @@
 
 #include "base/bind.h"
 #include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/app_list/search/arc/arc_app_shortcut_search_result.h"
 #include "components/arc/arc_bridge_service.h"
@@ -51,8 +52,18 @@
 
 void ArcAppShortcutsSearchProvider::OnGetAppShortcutGlobalQueryItems(
     std::vector<arc::mojom::AppShortcutItemPtr> shortcut_items) {
+  const ArcAppListPrefs* arc_prefs = ArcAppListPrefs::Get(profile_);
+  DCHECK(arc_prefs);
+
   SearchProvider::Results search_results;
   for (auto& item : shortcut_items) {
+    const std::string app_id =
+        arc_prefs->GetAppIdByPackageName(item->package_name.value());
+    std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
+        arc_prefs->GetApp(app_id);
+    // Ignore shortcuts for apps that are not present in the launcher.
+    if (!app_info || !app_info->show_in_launcher)
+      continue;
     search_results.emplace_back(std::make_unique<ArcAppShortcutSearchResult>(
         std::move(item), profile_, list_controller_));
   }
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 9af600e9..4de7f00 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
@@ -12,13 +12,21 @@
 #include "base/strings/utf_string_conversions.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"
 #include "chrome/browser/ui/app_list/arc/arc_app_test.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 #include "chrome/browser/ui/app_list/test/test_app_list_controller_delegate.h"
+#include "testing/gtest/include/gtest/gtest.h"
 
 namespace app_list {
 
-class ArcAppShortcutsSearchProviderTest : public AppListTestBase {
+namespace {
+constexpr char kFakeAppPackageName[] = "FakeAppPackageName";
+}  // namespace
+
+class ArcAppShortcutsSearchProviderTest
+    : public AppListTestBase,
+      public ::testing::WithParamInterface<bool> {
  protected:
   ArcAppShortcutsSearchProviderTest() = default;
   ~ArcAppShortcutsSearchProviderTest() override = default;
@@ -36,6 +44,32 @@
     AppListTestBase::TearDown();
   }
 
+  arc::mojom::AppInfo CreateAppInfo(const std::string& name,
+                                    const std::string& activity,
+                                    const std::string& package_name) {
+    arc::mojom::AppInfo appinfo;
+    appinfo.name = name;
+    appinfo.package_name = package_name;
+    appinfo.activity = activity;
+    return appinfo;
+  }
+
+  std::string AddArcAppAndShortcut(const arc::mojom::AppInfo& app_info,
+                                   bool launchable) {
+    ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
+    // Adding app to the prefs, and check that the app is accessible by id.
+    prefs->AddAppAndShortcut(
+        app_info.name, app_info.package_name, app_info.activity,
+        std::string() /* intent_uri */, std::string() /* icon_resource_id */,
+        false /* sticky */, true /* notifications_enabled */,
+        true /* app_ready */, false /* suspended */, false /* shortcut */,
+        launchable);
+    const std::string app_id =
+        ArcAppListPrefs::GetAppId(app_info.package_name, app_info.activity);
+    EXPECT_TRUE(prefs->GetApp(app_id));
+    return app_id;
+  }
+
   std::unique_ptr<test::TestAppListControllerDelegate> controller_;
   ArcAppTest arc_test_;
 
@@ -43,8 +77,14 @@
   DISALLOW_COPY_AND_ASSIGN(ArcAppShortcutsSearchProviderTest);
 };
 
-TEST_F(ArcAppShortcutsSearchProviderTest, Basic) {
-  constexpr size_t kMaxResults = 4;
+TEST_P(ArcAppShortcutsSearchProviderTest, Basic) {
+  const bool launchable = GetParam();
+
+  AddArcAppAndShortcut(
+      CreateAppInfo("FakeName", "FakeActivity", kFakeAppPackageName),
+      launchable);
+
+  const size_t kMaxResults = launchable ? 4 : 0;
   constexpr char kQuery[] = "shortlabel";
 
   auto provider = std::make_unique<ArcAppShortcutsSearchProvider>(
@@ -63,4 +103,6 @@
   }
 }
 
+INSTANTIATE_TEST_CASE_P(, ArcAppShortcutsSearchProviderTest, testing::Bool());
+
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_provider.cc b/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_provider.cc
index 934eae1..0dd08d98 100644
--- a/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_provider.cc
+++ b/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_provider.cc
@@ -27,7 +27,7 @@
   new_results.reserve(app_names.size());
   for (auto& app_name : app_names) {
     new_results.emplace_back(
-        std::make_unique<CrostiniRepositorySearchResult>(app_name));
+        std::make_unique<CrostiniRepositorySearchResult>(profile_, app_name));
     // Todo(https://crbug.com/921429): Improve relevance logic, this will likely
     // be implemented in garcon then piped to Chrome
     new_results.back()->set_relevance(static_cast<float>(query_.size()) /
diff --git a/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_result.cc b/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_result.cc
index 9df56f4..7d01e2c 100644
--- a/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_result.cc
+++ b/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_result.cc
@@ -10,6 +10,8 @@
 #include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chrome/app/vector_icons/vector_icons.h"
+#include "chrome/browser/chromeos/crostini/crostini_package_service.h"
+#include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -20,13 +22,14 @@
 namespace {
 
 // TODO(https://crbug.com/921429): Need UX spec.
-constexpr SkColor kListIconColor = SkColorSetARGB(0xDE, 0xAD, 0xAD, 0xAD);
+constexpr SkColor kListIconColor = SkColorSetARGB(0xDE, 0x00, 0x00, 0x00);
 
 }  // namespace
 
 CrostiniRepositorySearchResult::CrostiniRepositorySearchResult(
+    Profile* profile,
     const std::string& app_name)
-    : app_name_(app_name), weak_ptr_factory_(this) {
+    : profile_(profile), app_name_(app_name), weak_ptr_factory_(this) {
   // TODO(https://crbug.com/921429): Need UX spec.
   set_id("crostini:" + app_name_);
   SetResultType(ash::SearchResultType::kOmnibox);
@@ -37,14 +40,35 @@
       icon, AppListConfig::instance().search_list_icon_dimension(),
       kListIconColor));
   SetTitle(l10n_util::GetStringFUTF16(
-      IDS_CROSTINI_REPOSITORY_SEARCH_RESULT_PLACEHOLDER_TEXT,
+      IDS_CROSTINI_REPOSITORY_SEARCH_RESULT_TITLE_PLACEHOLDER_TEXT,
       base::UTF8ToUTF16(app_name_)));
+  SetDetails(l10n_util::GetStringUTF16(
+      IDS_CROSTINI_REPOSITORY_SEARCH_RESULT_DETAILS_PLACEHOLDER_TEXT));
 }
 
 CrostiniRepositorySearchResult::~CrostiniRepositorySearchResult() = default;
 
+// TODO(https://crbug.com/921429): Change Open() to open up an installation
+// confirmation dialogue that then calls crostini::InstallLinuxPackageFromApt.
+
+void CrostiniRepositorySearchResult::OnOpen(
+    const crostini::LinuxPackageInfo& package_info) {
+  if (package_info.success) {
+    crostini::CrostiniPackageService::GetForProfile(profile_)
+        ->InstallLinuxPackageFromApt(crostini::kCrostiniDefaultVmName,
+                                     crostini::kCrostiniDefaultContainerName,
+                                     package_info.package_id,
+                                     base::DoNothing());
+  }
+}
+
 void CrostiniRepositorySearchResult::Open(int event_flags) {
-  // TODO(https://crbug.com/921429): connect to logic in crostini_manager.
+  crostini::CrostiniManager::GetForProfile(profile_)
+      ->GetLinuxPackageInfoFromApt(
+          crostini::kCrostiniDefaultVmName,
+          crostini::kCrostiniDefaultContainerName, app_name_,
+          base::BindOnce(&CrostiniRepositorySearchResult::OnOpen,
+                         weak_ptr_factory_.GetWeakPtr()));
 }
 
 }  // namespace app_list
diff --git a/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_result.h b/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_result.h
index cdcbb36..d289cd4 100644
--- a/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_result.h
+++ b/chrome/browser/ui/app_list/search/crostini/crostini_repository_search_result.h
@@ -8,22 +8,26 @@
 #include <string>
 
 #include "base/macros.h"
+#include "chrome/browser/chromeos/crostini/crostini_manager.h"
 #include "chrome/browser/ui/app_list/app_context_menu_delegate.h"
 #include "chrome/browser/ui/app_list/search/chrome_search_result.h"
 
+class Profile;
+
 namespace app_list {
 
 class CrostiniRepositorySearchResult : public ChromeSearchResult {
  public:
-  explicit CrostiniRepositorySearchResult(const std::string& app_name);
+  CrostiniRepositorySearchResult(Profile* profile, const std::string& app_name);
   ~CrostiniRepositorySearchResult() override;
 
   // ChromeSearchResult overrides:
-  // TODO(https://crbug.com/921429): Implement open functionality (confirmation
-  // and installation of app).
   void Open(int event_flags) override;
 
  private:
+  void OnOpen(const crostini::LinuxPackageInfo& package);
+
+  Profile* profile_;
   std::string app_name_;
   base::WeakPtrFactory<CrostiniRepositorySearchResult> weak_ptr_factory_;
 
diff --git a/chrome/browser/ui/app_list/search/search_controller_factory.cc b/chrome/browser/ui/app_list/search/search_controller_factory.cc
index 4d79eee..ddacd97 100644
--- a/chrome/browser/ui/app_list/search/search_controller_factory.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_factory.cc
@@ -16,6 +16,7 @@
 #include "chrome/browser/ui/app_list/search/answer_card/answer_card_search_provider.h"
 #include "chrome/browser/ui/app_list/search/app_search_provider.h"
 #include "chrome/browser/ui/app_list/search/arc/arc_app_data_search_provider.h"
+#include "chrome/browser/ui/app_list/search/arc/arc_app_reinstall_search_provider.h"
 #include "chrome/browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider.h"
 #include "chrome/browser/ui/app_list/search/arc/arc_playstore_search_provider.h"
 #include "chrome/browser/ui/app_list/search/crostini/crostini_repository_search_provider.h"
@@ -40,6 +41,7 @@
 constexpr size_t kMaxAppsGroupResults = 7;
 constexpr size_t kMaxOmniboxResults = 4;
 constexpr size_t kMaxLauncherSearchResults = 2;
+constexpr size_t kMaxAppReinstallSearchResults = 1;
 // We show up to 6 Play Store results. However, part of Play Store results may
 // be filtered out because they may correspond to already installed Web apps. So
 // we request twice as many Play Store apps as we can show. Note that this still
@@ -106,6 +108,16 @@
                             std::make_unique<LauncherSearchProvider>(profile));
   }
 
+  // reinstallation candidates for Arc++ apps.
+  if (app_list_features::IsAppReinstallZeroStateEnabled() &&
+      arc::IsArcAllowedForProfile(profile)) {
+    size_t recommended_app_group_id =
+        controller->AddGroup(kMaxAppReinstallSearchResults, 1.0, kBoostOfApps);
+    controller->AddProvider(recommended_app_group_id,
+                            std::make_unique<ArcAppReinstallSearchProvider>(
+                                profile, kMaxAppReinstallSearchResults));
+  }
+
   if (app_list_features::IsPlayStoreAppSearchEnabled()) {
     // Set same boost as apps group since Play store results are placed
     // with apps.
diff --git a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
index d84f892..6b74596 100644
--- a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.cc
@@ -123,14 +123,6 @@
   ShowAndActivateOrMinimize(*it);
 }
 
-ui::BaseWindow* AppWindowLauncherItemController::GetLastActiveWindow() {
-  if (last_active_window_)
-    return last_active_window_;
-  if (windows_.empty())
-    return nullptr;
-  return windows_.front();
-}
-
 void AppWindowLauncherItemController::OnWindowPropertyChanged(
     aura::Window* window,
     const void* key,
@@ -150,6 +142,14 @@
   }
 }
 
+ui::BaseWindow* AppWindowLauncherItemController::GetLastActiveWindow() {
+  if (last_active_window_)
+    return last_active_window_;
+  if (windows_.empty())
+    return nullptr;
+  return windows_.front();
+}
+
 ash::ShelfAction AppWindowLauncherItemController::ShowAndActivateOrMinimize(
     ui::BaseWindow* app_window) {
   // Either show or minimize windows when shown from the launcher.
diff --git a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
index 24eda4ee..1279c72 100644
--- a/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
+++ b/chrome/browser/ui/ash/launcher/app_window_launcher_item_controller.h
@@ -72,6 +72,10 @@
 
   const WindowList& windows() const { return windows_; }
 
+ protected:
+  // Returns last active window in the controller or first window.
+  ui::BaseWindow* GetLastActiveWindow();
+
  private:
   friend class ChromeLauncherControllerTest;
 
@@ -86,9 +90,6 @@
   ash::ShelfAction ActivateOrAdvanceToNextAppWindow(
       ui::BaseWindow* window_to_show);
 
-  // Returns last active window in the controller or first window.
-  ui::BaseWindow* GetLastActiveWindow();
-
   WindowList::iterator GetFromNativeWindow(aura::Window* window);
 
   // Handles the case when the app window in this controller has been changed,
diff --git a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
index 78db7bf..1e72f40 100644
--- a/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
+++ b/chrome/browser/ui/ash/launcher/arc_app_window_launcher_item_controller.cc
@@ -6,6 +6,10 @@
 
 #include <utility>
 
+#include "ash/public/cpp/window_properties.h"
+#include "ash/public/interfaces/window_state_type.mojom.h"
+#include "chrome/browser/chromeos/arc/pip/arc_pip_bridge.h"
+#include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
@@ -37,6 +41,17 @@
     ash::ShelfLaunchSource source,
     ItemSelectedCallback callback) {
   if (window_count()) {
+    // Exit PIP when the shelf button is pressed.
+    ui::BaseWindow* window = GetLastActiveWindow();
+    aura::Window* native_window = window->GetNativeWindow();
+    if (native_window->GetProperty(ash::kWindowStateTypeKey) ==
+        ash::mojom::WindowStateType::PIP) {
+      Profile* profile = ChromeLauncherController::instance()->profile();
+      arc::ArcPipBridge* pip_bridge =
+          arc::ArcPipBridge::GetForBrowserContext(profile);
+      pip_bridge->ClosePip();
+    }
+
     AppWindowLauncherItemController::ItemSelected(std::move(event), display_id,
                                                   source, std::move(callback));
     return;
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 1a7e770d..763b177 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -209,6 +209,10 @@
   virtual void OnTabDetached(content::WebContents* contents,
                              bool was_active) = 0;
 
+  // Called when the user restores a tab from the recently closed tabs menu.
+  // |command_id| is the menu command associated with the restored tab.
+  virtual void OnTabRestoredFromMenu(int command_id) = 0;
+
   // Called to force the zoom state to for the active tab to be recalculated.
   // |can_show_bubble| is true when a user presses the zoom up or down keyboard
   // shortcuts and will be false in other cases (e.g. switching tabs, "clicking"
diff --git a/chrome/browser/ui/chrome_pages.cc b/chrome/browser/ui/chrome_pages.cc
index b952c7b..c229d30 100644
--- a/chrome/browser/ui/chrome_pages.cc
+++ b/chrome/browser/ui/chrome_pages.cc
@@ -68,7 +68,7 @@
 void OpenBookmarkManagerForNode(Browser* browser, int64_t node_id) {
   GURL url = GURL(kChromeUIBookmarksURL)
                  .Resolve(base::StringPrintf(
-                     "/?id=%s", base::Int64ToString(node_id).c_str()));
+                     "/?id=%s", base::NumberToString(node_id).c_str()));
   NavigateParams params(GetSingletonTabNavigateParams(browser, url));
   params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
   ShowSingletonTabOverwritingNTP(browser, std::move(params));
diff --git a/chrome/browser/ui/cocoa/applescript/apple_event_util_unittest.mm b/chrome/browser/ui/cocoa/applescript/apple_event_util_unittest.mm
index c02b8a7b..ae507f0 100644
--- a/chrome/browser/ui/cocoa/applescript/apple_event_util_unittest.mm
+++ b/chrome/browser/ui/cocoa/applescript/apple_event_util_unittest.mm
@@ -52,7 +52,6 @@
       }
 
       return FourCharToString(code);
-      break;
     }
     case typeSInt16:
     case typeSInt32:
@@ -71,8 +70,7 @@
         return std::string();
       }
 
-      return base::Int64ToString(value);
-      break;
+      return base::NumberToString(value);
     }
     case typeIEEE32BitFloatingPoint:
     case typeIEEE64BitFloatingPoint: {
@@ -92,7 +90,6 @@
       }
 
       return base::NumberToString(value);
-      break;
     }
     // Text formats look like:
     //  'utxt'("string here")
@@ -108,8 +105,6 @@
              base::UTF16ToUTF8(
                  base::string16(data_vector.begin(), data_vector.end())) +
              "\")";
-
-      break;
     }
     // Lists look like:
     //  [ item1, item2, item3 ]
@@ -149,7 +144,6 @@
 
       result += is_record ? " }" : " ]";
       return result;
-      break;
     }
     default: {
       NOTREACHED() << "unexpected descriptor type "
diff --git a/chrome/browser/ui/cocoa/notifications/alert_notification_service.mm b/chrome/browser/ui/cocoa/notifications/alert_notification_service.mm
index 39e6792..b88d8918 100644
--- a/chrome/browser/ui/cocoa/notifications/alert_notification_service.mm
+++ b/chrome/browser/ui/cocoa/notifications/alert_notification_service.mm
@@ -23,7 +23,7 @@
   static crashpad::SimpleStringDictionary* annotations = []() {
     auto* annotations = new crashpad::SimpleStringDictionary();
     annotations->SetKeyValue("ptype", "AlertNotificationService.xpc");
-    annotations->SetKeyValue("pid", base::IntToString(getpid()).c_str());
+    annotations->SetKeyValue("pid", base::NumberToString(getpid()).c_str());
     return annotations;
   }();
   return annotations;
diff --git a/chrome/browser/ui/cocoa/test/cocoa_profile_test.mm b/chrome/browser/ui/cocoa/test/cocoa_profile_test.mm
index dd02d82..fae8628d 100644
--- a/chrome/browser/ui/cocoa/test/cocoa_profile_test.mm
+++ b/chrome/browser/ui/cocoa/test/cocoa_profile_test.mm
@@ -11,8 +11,8 @@
 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
-#include "chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/gaia_cookie_manager_service_test_util.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
diff --git a/chrome/browser/ui/extensions/hosted_app_browsertest.cc b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
index 22185ef..b3af115 100644
--- a/chrome/browser/ui/extensions/hosted_app_browsertest.cc
+++ b/chrome/browser/ui/extensions/hosted_app_browsertest.cc
@@ -151,7 +151,7 @@
           web_contents->GetInterstitialPage();
       ASSERT_TRUE(interstitial);
       interstitial->GetDelegateForTesting()->CommandReceived(
-          base::IntToString(security_interstitials::CMD_PROCEED));
+          base::NumberToString(security_interstitials::CMD_PROCEED));
     }
     observer.Wait();
   }
diff --git a/chrome/browser/ui/input_method/input_method_engine_base.cc b/chrome/browser/ui/input_method/input_method_engine_base.cc
index bb087ac..55cc8b9 100644
--- a/chrome/browser/ui/input_method/input_method_engine_base.cc
+++ b/chrome/browser/ui/input_method/input_method_engine_base.cc
@@ -420,7 +420,7 @@
 std::string InputMethodEngineBase::AddRequest(
     const std::string& component_id,
     ui::IMEEngineHandlerInterface::KeyEventDoneCallback key_data) {
-  std::string request_id = base::IntToString(next_request_id_);
+  std::string request_id = base::NumberToString(next_request_id_);
   ++next_request_id_;
 
   request_map_[request_id] = std::make_pair(component_id, std::move(key_data));
diff --git a/chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer_unittest.cc b/chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer_unittest.cc
index cf775da..9e20104 100644
--- a/chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer_unittest.cc
+++ b/chrome/browser/ui/omnibox/chrome_omnibox_navigation_observer_unittest.cc
@@ -183,7 +183,7 @@
 
   base::string16 query = base::ASCIIToUTF16(" text");
   for (size_t i = 0; i < cases.size(); ++i) {
-    SCOPED_TRACE("case #" + base::IntToString(i));
+    SCOPED_TRACE("case #" + base::NumberToString(i));
     // The keyword should always exist at the beginning.
     EXPECT_TRUE(model()->GetTemplateURLForKeyword(cases[i].keyword) != nullptr);
 
@@ -284,7 +284,7 @@
        true},
   };
   for (size_t i = 0; i < base::size(cases); ++i) {
-    SCOPED_TRACE("case #" + base::IntToString(i));
+    SCOPED_TRACE("case #" + base::NumberToString(i));
     const Case& test_case = cases[i];
     const Response& response = test_case.response;
 
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index 0f114603..6ebb8cf7 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -703,7 +703,7 @@
 
   base::string16 old_text = omnibox_view->GetText();
 
-  // Make sure inline autocomplete is triggerred.
+  // Make sure inline autocomplete is triggered.
   EXPECT_GT(old_text.length(), base::size(kInlineAutocompleteText) - 1);
 
   size_t old_selected_line = popup_model->selected_line();
@@ -726,6 +726,45 @@
   EXPECT_EQ(old_selected_line, popup_model->selected_line());
 }
 
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
+                       RevertDefaultRevertInlineTextWhenSelectingDefaultMatch) {
+  OmniboxView* omnibox_view = NULL;
+  ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
+  OmniboxPopupModel* popup_model = omnibox_view->model()->popup_model();
+  ASSERT_TRUE(popup_model);
+
+  // Input something to trigger inline autocomplete.
+  ASSERT_NO_FATAL_FAILURE(SendKeySequence(kInlineAutocompleteTextKeys));
+  ASSERT_NO_FATAL_FAILURE(WaitForAutocompleteControllerDone());
+  ASSERT_TRUE(popup_model->IsOpen());
+
+  base::string16 old_text = omnibox_view->GetText();
+
+  // Make sure inline autocomplete is triggered.
+  EXPECT_GT(old_text.length(), base::size(kInlineAutocompleteText) - 1);
+
+  size_t old_selected_line = popup_model->selected_line();
+  EXPECT_EQ(0U, old_selected_line);
+
+  // Move to another line with different text.
+  size_t size = popup_model->result().size();
+  while (popup_model->selected_line() < size - 1) {
+    ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_DOWN, 0));
+    ASSERT_NE(old_selected_line, popup_model->selected_line());
+    if (old_text != omnibox_view->GetText())
+      break;
+  }
+
+  EXPECT_NE(old_text, omnibox_view->GetText());
+
+  // Move back to the first line
+  while (popup_model->selected_line() > 0)
+    ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_UP, 0));
+
+  EXPECT_EQ(old_text, omnibox_view->GetText());
+  EXPECT_EQ(old_selected_line, popup_model->selected_line());
+}
+
 IN_PROC_BROWSER_TEST_F(OmniboxViewTest, BasicTextOperations) {
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
   chrome::FocusLocationBar(browser());
@@ -1049,12 +1088,6 @@
   omnibox_view->model()->OnUpOrDownKeyPressed(1);
   omnibox_view->model()->OnUpOrDownKeyPressed(-1);
   ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, 0));
-  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, 0));
-  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, 0));
-  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, 0));
-  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, 0));
-  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, 0));
-  ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_LEFT, 0));
   ASSERT_NO_FATAL_FAILURE(SendKey(ui::VKEY_SPACE, 0));
   ASSERT_FALSE(omnibox_view->model()->is_keyword_hint());
   ASSERT_EQ(search_keyword, omnibox_view->model()->keyword());
@@ -1420,7 +1453,7 @@
 
   base::string16 old_text = omnibox_view->GetText();
 
-  // Make sure inline autocomplete is triggerred.
+  // Make sure inline autocomplete is triggered.
   EXPECT_GT(old_text.length(), base::size(kInlineAutocompleteText) - 1);
 
   // Press ctrl key.
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index 0d8d67b3..2bf2ac84 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -163,7 +163,7 @@
   using password_bubble_experiment::kSmartBubbleThresholdParam;
   std::map<std::string, std::string> params;
   params[kSmartBubbleThresholdParam] =
-      base::IntToString(kGreatDissmisalCount / 2);
+      base::NumberToString(kGreatDissmisalCount / 2);
   variations::AssociateVariationParams(kSmartBubbleExperimentName, "A", params);
   ASSERT_TRUE(
       base::FieldTrialList::CreateFieldTrial(kSmartBubbleExperimentName, "A"));
diff --git a/chrome/browser/ui/signin_view_controller.cc b/chrome/browser/ui/signin_view_controller.cc
index 3a7d190..841ebae 100644
--- a/chrome/browser/ui/signin_view_controller.cc
+++ b/chrome/browser/ui/signin_view_controller.cc
@@ -189,10 +189,15 @@
       signin::AccountConsistencyMethod::kDiceMigration));
 
   // If redirect_url is empty, we would like to redirect to the NTP, but it's
-  // not possible through the continue_url. Use the google base URL instead
-  // here, and the DiceTabHelper may do the redirect to the NTP later.
+  // not possible through the continue_url, because Gaia cannot redirect to
+  // chrome:// URLs. Use the google base URL instead here, and the DiceTabHelper
+  // may do the redirect to the NTP later.
+  // Note: Gaia rejects some continue URLs as invalid and responds with HTTP
+  // error 400. This seems to happen in particular if the continue URL is not a
+  // Google-owned domain. Chrome cannot enforce that only valid URLs are used,
+  // because the set of valid URLs is not specified.
   std::string continue_url =
-      redirect_url.is_empty()
+      (redirect_url.is_empty() || !redirect_url.SchemeIsHTTPOrHTTPS())
           ? UIThreadSearchTermsData(profile).GoogleBaseURLValue()
           : redirect_url.spec();
 
@@ -232,8 +237,8 @@
   DiceTabHelper::CreateForWebContents(active_contents);
   DiceTabHelper* tab_helper = DiceTabHelper::FromWebContents(active_contents);
 
-  // Use |redirect_url| and not |continue_url|, so that the flow can redirect to
-  // the NTP.
+  // Use |redirect_url| and not |continue_url|, so that the DiceTabHelper can
+  // redirect to chrome:// URLs such as the NTP.
   tab_helper->InitializeSigninFlow(signin_url, access_point, signin_reason,
                                    promo_action, redirect_url);
 }
diff --git a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
index 6dc1df0..be2f464 100644
--- a/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
+++ b/chrome/browser/ui/tabs/tab_strip_model_unittest.cc
@@ -419,7 +419,7 @@
       if (i > 0)
         actual += " ";
 
-      actual += base::IntToString(GetID(model.GetWebContentsAt(i)));
+      actual += base::NumberToString(GetID(model.GetWebContentsAt(i)));
 
       if (model.IsTabPinned(i))
         actual += "p";
@@ -1336,7 +1336,7 @@
         for (size_t i = 0; i < indices.size(); ++i) {
           if (i != 0)
             result += " ";
-          result += base::IntToString(indices[i]);
+          result += base::NumberToString(indices[i]);
         }
         return result;
       };
diff --git a/chrome/browser/ui/task_manager/task_manager_table_model.cc b/chrome/browser/ui/task_manager/task_manager_table_model.cc
index 19cb421c..40a28b9 100644
--- a/chrome/browser/ui/task_manager/task_manager_table_model.cc
+++ b/chrome/browser/ui/task_manager/task_manager_table_model.cc
@@ -119,6 +119,8 @@
   ~TaskManagerValuesStringifier() {}
 
   base::string16 GetCpuUsageText(double cpu_usage) {
+    if (std::isnan(cpu_usage))
+      return n_a_string_;
     return base::UTF8ToUTF16(base::StringPrintf(kCpuTextFormatString,
                                                 cpu_usage));
   }
@@ -187,13 +189,13 @@
     if (nacl_port == nacl::kGdbDebugStubPortUnknown)
       return unknown_string_;
 
-    return base::IntToString16(nacl_port);
+    return base::NumberToString16(nacl_port);
   }
 
   base::string16 GetWindowsHandlesText(int64_t current, int64_t peak) {
     return l10n_util::GetStringFUTF16(IDS_TASK_MANAGER_HANDLES_CELL_TEXT,
-                                      base::Int64ToString16(current),
-                                      base::Int64ToString16(peak));
+                                      base::NumberToString16(current),
+                                      base::NumberToString16(peak));
   }
 
   base::string16 GetNetworkUsageText(int64_t network_usage) {
@@ -209,7 +211,7 @@
   }
 
   base::string16 GetProcessIdText(base::ProcessId proc_id) {
-    return base::IntToString16(proc_id);
+    return base::NumberToString16(proc_id);
   }
 
   base::string16 FormatAllocatedAndUsedMemory(int64_t allocated, int64_t used) {
@@ -227,7 +229,7 @@
   base::string16 GetKeepaliveCountText(int keepalive_count) const {
     if (keepalive_count < 0)
       return n_a_string();
-    return base::IntToString16(keepalive_count);
+    return base::NumberToString16(keepalive_count);
   }
 
   const base::string16& n_a_string() const { return n_a_string_; }
diff --git a/chrome/browser/ui/toolbar/back_forward_menu_model.cc b/chrome/browser/ui/toolbar/back_forward_menu_model.cc
index 66543de..3935133 100644
--- a/chrome/browser/ui/toolbar/back_forward_menu_model.cc
+++ b/chrome/browser/ui/toolbar/back_forward_menu_model.cc
@@ -449,7 +449,7 @@
   metric_string += action;
   if (index != -1) {
     // +1 is for historical reasons (indices used to start at 1).
-    metric_string += base::IntToString(index + 1);
+    metric_string += base::NumberToString(index + 1);
   }
   return metric_string;
 }
diff --git a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
index 7388bdf..e66da11 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.cc
@@ -43,11 +43,11 @@
 }
 
 std::string ToSessionTag(SessionID session_id) {
-  return std::string(kBaseSessionTag + base::IntToString(session_id.id()));
+  return std::string(kBaseSessionTag + base::NumberToString(session_id.id()));
 }
 
 std::string ToSessionName(SessionID session_id) {
-  return std::string(kBaseSessionName + base::IntToString(session_id.id()));
+  return std::string(kBaseSessionName + base::NumberToString(session_id.id()));
 }
 
 std::string ToTabTitle(SessionID session_id,
diff --git a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
index 60cc1a0d..8ccdd5ca 100644
--- a/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
+++ b/chrome/browser/ui/toolbar/recent_tabs_sub_menu_model.cc
@@ -25,8 +25,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/browser_live_tab_context.h"
-#include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h"
-#include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_factory.h"
+#include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/toolbar/app_menu_model.h"
 #include "chrome/grit/generated_resources.h"
@@ -312,11 +311,7 @@
     }
   }
 
-#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
-  auto* reopen_tab_iph =
-      ReopenTabInProductHelpFactory::GetForProfile(browser_->profile());
-  reopen_tab_iph->TabReopened();
-#endif
+  browser_->window()->OnTabRestoredFromMenu(command_id);
 
   UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.OpenRecentTab",
                              menu_opened_timer_.Elapsed());
diff --git a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
index de6f7a6..62e632cb 100644
--- a/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
+++ b/chrome/browser/ui/views/apps/chrome_native_app_window_views_aura_ash.cc
@@ -624,6 +624,12 @@
 }
 
 void ChromeNativeAppWindowViewsAuraAsh::UpdateImmersiveMode() {
+  bool immersive = ShouldEnableImmersiveMode();
   ash::ImmersiveFullscreenController::EnableForWidget(
       widget(), ShouldEnableImmersiveMode());
+  // TODO(estade): de-dupe this logic with WmToplevelWindowEventHandler.
+  TabletModeClient* client = TabletModeClient::Get();
+  GetNativeWindow()->SetProperty(
+      aura::client::kGestureDragFromClientAreaTopMovesWindow,
+      client && client->tablet_mode_enabled() && immersive);
 }
diff --git a/chrome/browser/ui/views/autofill/local_card_migration_browsertest.cc b/chrome/browser/ui/views/autofill/local_card_migration_browsertest.cc
index a7d5acf..639212a 100644
--- a/chrome/browser/ui/views/autofill/local_card_migration_browsertest.cc
+++ b/chrome/browser/ui/views/autofill/local_card_migration_browsertest.cc
@@ -14,7 +14,6 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
 #include "chrome/browser/autofill/autofill_uitest_util.h"
-#include "chrome/browser/signin/account_fetcher_service_factory.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
@@ -47,8 +46,6 @@
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "components/prefs/pref_registry_simple.h"
-#include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/fake_account_fetcher_service.h"
 #include "components/sync/test/fake_server/fake_server.h"
 #include "components/sync/test/fake_server/fake_server_network_resources.h"
 #include "content/public/browser/web_contents_observer.h"
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 66d30a452..392c11b 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -392,8 +392,8 @@
     model_->AddURL(f1, 2, ASCIIToUTF16("f1b"), GURL(test_base + "f1b"));
     if (big_menu) {
       for (int i = 1; i <= 100; ++i) {
-        model_->AddURL(f1, i + 1, ASCIIToUTF16("f") + base::IntToString16(i),
-                       GURL(test_base + "f" + base::IntToString(i)));
+        model_->AddURL(f1, i + 1, ASCIIToUTF16("f") + base::NumberToString16(i),
+                       GURL(test_base + "f" + base::NumberToString(i)));
       }
     }
     model_->AddURL(bb_node, 1, ASCIIToUTF16("a"), GURL(test_base + "a"));
diff --git a/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest_views.cc b/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest_views.cc
index d3cf11c..227e323 100644
--- a/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest_views.cc
+++ b/chrome/browser/ui/views/extensions/extension_message_bubble_view_browsertest_views.cc
@@ -11,42 +11,39 @@
 #include "ui/base/ui_base_features.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 
-namespace {
-
-// Returns the ToolbarView for the given |browser|.
-ToolbarView* GetToolbarViewForBrowser(Browser* browser) {
-  return BrowserView::GetBrowserViewForBrowser(browser)->toolbar();
-}
-
-}  // namespace
-
 // static
 ToolbarActionsBarBubbleViews*
 ExtensionMessageBubbleBrowserTest::GetViewsBubbleForBrowser(Browser* browser) {
   return static_cast<ToolbarActionsBarBubbleViews*>(
-      GetToolbarViewForBrowser(browser)->browser_actions()->active_bubble());
+      BrowserView::GetBrowserViewForBrowser(browser)
+          ->toolbar_button_provider()
+          ->GetBrowserActionsContainer()
+          ->active_bubble());
 }
 
 // static
 gfx::Rect ExtensionMessageBubbleBrowserTest::GetAnchorReferenceBoundsForBrowser(
     Browser* browser,
     AnchorPosition anchor) {
-  ToolbarView* toolbar_view = GetToolbarViewForBrowser(browser);
-  BrowserActionsContainer* container = toolbar_view->browser_actions();
+  auto* toolbar_button_provider =
+      BrowserView::GetBrowserViewForBrowser(browser)->toolbar_button_provider();
+  auto* browser_actions_container =
+      toolbar_button_provider->GetBrowserActionsContainer();
   views::View* anchor_view = nullptr;
   switch (anchor) {
     case ExtensionMessageBubbleBrowserTest::ANCHOR_BROWSER_ACTION:
-      EXPECT_GT(container->num_toolbar_actions(), 0u);
-      if (container->num_toolbar_actions() == 0)
+      EXPECT_GT(browser_actions_container->num_toolbar_actions(), 0u);
+      if (browser_actions_container->num_toolbar_actions() == 0)
         return gfx::Rect();
-      anchor_view = container->GetToolbarActionViewAt(0);
+      anchor_view = browser_actions_container->GetToolbarActionViewAt(0);
       break;
     case ExtensionMessageBubbleBrowserTest::ANCHOR_APP_MENU:
-      anchor_view = toolbar_view->app_menu_button();
+      anchor_view = toolbar_button_provider->GetAppMenuButton();
       break;
   }
 
   EXPECT_TRUE(anchor_view);
-  EXPECT_EQ(anchor_view, container->active_bubble()->GetAnchorView());
+  EXPECT_EQ(anchor_view,
+            browser_actions_container->active_bubble()->GetAnchorView());
   return anchor_view->GetBoundsInScreen();
 }
diff --git a/chrome/browser/ui/views/feature_promos/feature_promo_dialog_browsertest.cc b/chrome/browser/ui/views/feature_promos/feature_promo_dialog_browsertest.cc
index 0db8ebf6..ed7ef5d 100644
--- a/chrome/browser/ui/views/feature_promos/feature_promo_dialog_browsertest.cc
+++ b/chrome/browser/ui/views/feature_promos/feature_promo_dialog_browsertest.cc
@@ -17,8 +17,8 @@
   // DialogBrowserTest:
   void ShowUi(const std::string& name) override {
     auto* app_menu_button = BrowserView::GetBrowserViewForBrowser(browser())
-                                ->toolbar()
-                                ->app_menu_button();
+                                ->toolbar_button_provider()
+                                ->GetAppMenuButton();
     // We use one of the strings for the new tab feature promo since there is
     // currently no infrastructure for test-only string resources.
     int placeholder_string = IDS_NEWTAB_PROMO_0;
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
index 770f325c..693b40f 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.cc
@@ -49,11 +49,6 @@
     : iph_service_(ReopenTabInProductHelpFactory::GetForProfile(
           browser_view->browser()->profile())),
       browser_view_(browser_view) {
-  // Check that the app menu button exists. It should only not exist when there
-  // is no tab strip, in which case this shouldn't trigger in the first place.
-  BrowserAppMenuButton* app_menu_button =
-      browser_view_->toolbar()->app_menu_button();
-  DCHECK(app_menu_button);
 }
 
 void ReopenTabPromoController::ShowPromo() {
@@ -74,18 +69,27 @@
   promo_bubble_->GetWidget()->AddObserver(this);
 }
 
+void ReopenTabPromoController::OnTabReopened(int command_id) {
+  iph_service_->TabReopened();
+
+  if (command_id == AppMenuModel::kMinRecentTabsCommandId) {
+    DCHECK(!tab_reopened_before_app_menu_closed_);
+    UMA_HISTOGRAM_ENUMERATION(kReopenTabPromoDismissedAtHistogram,
+                              ReopenTabPromoStepAtDismissal::kTabReopened);
+    tab_reopened_before_app_menu_closed_ = true;
+  }
+}
+
 void ReopenTabPromoController::OnMenuOpened() {
   // The user followed the promo and opened the menu. First, we close the promo
   // bubble since it doesn't automatically close on click. Then, we highlight
   // the history item and observe for the history submenu opening.
   promo_bubble_->GetWidget()->Close();
 
-  BrowserAppMenuButton* app_menu_button =
-      browser_view_->toolbar()->app_menu_button();
+  auto* app_menu_button = browser_view_->toolbar()->app_menu_button();
   app_menu_button->RemoveMenuListener(this);
 
-  AppMenu* app_menu = app_menu_button->app_menu();
-  app_menu->AddObserver(this);
+  app_menu_button->app_menu()->AddObserver(this);
 }
 
 void ReopenTabPromoController::OnWidgetDestroying(views::Widget* widget) {
@@ -94,12 +98,11 @@
 
   // If the menu isn't showing, that means the promo bubble timed out. We should
   // notify our IPH service that help was dismissed.
-  if (!browser_view_->toolbar()->app_menu_button()->IsMenuShowing()) {
+  auto* app_menu_button = browser_view_->toolbar()->app_menu_button();
+  if (!app_menu_button->IsMenuShowing()) {
     UMA_HISTOGRAM_ENUMERATION(kReopenTabPromoDismissedAtHistogram,
                               ReopenTabPromoStepAtDismissal::kBubbleShown);
 
-    BrowserAppMenuButton* app_menu_button =
-        browser_view_->toolbar()->app_menu_button();
     app_menu_button->RemoveMenuListener(this);
     app_menu_button->SetPromoFeature(base::nullopt);
     iph_service_->HelpDismissed();
@@ -117,17 +120,7 @@
 
   iph_service_->HelpDismissed();
 
-  browser_view_->toolbar()->app_menu_button()->SetPromoFeature(base::nullopt);
-
-  AppMenu* app_menu = browser_view_->toolbar()->app_menu_button()->app_menu();
-  app_menu->RemoveObserver(this);
-}
-
-void ReopenTabPromoController::OnExecuteCommand(int command_id) {
-  if (command_id == AppMenuModel::kMinRecentTabsCommandId) {
-    DCHECK(!tab_reopened_before_app_menu_closed_);
-    UMA_HISTOGRAM_ENUMERATION(kReopenTabPromoDismissedAtHistogram,
-                              ReopenTabPromoStepAtDismissal::kTabReopened);
-    tab_reopened_before_app_menu_closed_ = true;
-  }
+  auto* app_menu_button = browser_view_->toolbar()->app_menu_button();
+  app_menu_button->SetPromoFeature(base::nullopt);
+  app_menu_button->app_menu()->RemoveObserver(this);
 }
diff --git a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
index 6178f56..170a6a3 100644
--- a/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
+++ b/chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h
@@ -26,6 +26,10 @@
   // Shows the IPH promo. Should only be called once.
   void ShowPromo();
 
+  // Called when the user activates the entry with |command_id| in the "recent
+  // tabs" menu.
+  void OnTabReopened(int command_id);
+
  private:
   // views::MenuListener:
   void OnMenuOpened() override;
@@ -35,16 +39,17 @@
 
   // AppMenuObserver:
   void AppMenuClosed() override;
-  void OnExecuteCommand(int command_id) override;
 
   ReopenTabInProductHelp* const iph_service_;
   BrowserView* const browser_view_;
   FeaturePromoBubbleView* promo_bubble_ = nullptr;
 
   // Flag used to determine whether the promo completed successfully or not upon
-  // the app menu closing. This is set to true if
-  // OnExecuteCommand(AppMenuModel::kMinRecentTabsCommandId) is called.
+  // the app menu closing. This is set to true if the user reopened the most
+  // recently closed tab.
   bool tab_reopened_before_app_menu_closed_ = false;
+
+  DISALLOW_COPY_AND_ASSIGN(ReopenTabPromoController);
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_FEATURE_PROMOS_REOPEN_TAB_PROMO_CONTROLLER_H_
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 7e3c9fa..5f549cdf4 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -198,7 +198,6 @@
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 #include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help.h"
 #include "chrome/browser/ui/in_product_help/reopen_tab_in_product_help_factory.h"
-#include "chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h"
 #endif  // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 
 #if BUILDFLAG(ENABLE_ONE_CLICK_SIGNIN)
@@ -480,7 +479,11 @@
 // static
 const char BrowserView::kViewClassName[] = "BrowserView";
 
-BrowserView::BrowserView() : views::ClientView(nullptr, nullptr) {}
+BrowserView::BrowserView(std::unique_ptr<Browser> browser)
+    : views::ClientView(nullptr, nullptr), browser_(std::move(browser)) {
+  browser_->tab_strip_model()->AddObserver(this);
+  immersive_mode_controller_.reset(chrome::CreateImmersiveModeController());
+}
 
 BrowserView::~BrowserView() {
   // Destroy the top controls slide controller first as it depends on the
@@ -530,12 +533,6 @@
   toolbar_ = nullptr;
 }
 
-void BrowserView::Init(std::unique_ptr<Browser> browser) {
-  browser_ = std::move(browser);
-  browser_->tab_strip_model()->AddObserver(this);
-  immersive_mode_controller_.reset(chrome::CreateImmersiveModeController());
-}
-
 // static
 BrowserView* BrowserView::GetBrowserViewForNativeWindow(
     gfx::NativeWindow window) {
@@ -963,6 +960,12 @@
   }
 }
 
+void BrowserView::OnTabRestoredFromMenu(int command_id) {
+#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
+  reopen_tab_promo_controller_.OnTabReopened(command_id);
+#endif
+}
+
 void BrowserView::ZoomChangedForActiveTab(bool can_show_bubble) {
   const AppMenuButton* app_menu_button =
       toolbar_button_provider()->GetAppMenuButton();
@@ -1853,11 +1856,6 @@
 
 base::string16 BrowserView::GetAccessibleTabLabel(bool include_app_name,
                                                   int index) const {
-  // ChromeVox provides an invalid index on browser start up before
-  // any tabs are created.
-  if (index == -1)
-    return base::string16();
-
   base::string16 title =
       browser_->GetWindowTitleForTab(include_app_name, index);
 
@@ -3030,13 +3028,8 @@
 
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
 void BrowserView::ShowInProductHelpPromo(InProductHelpFeature iph_feature) {
-  if (iph_feature == InProductHelpFeature::kReopenTab) {
-    if (!reopen_tab_promo_controller_) {
-      reopen_tab_promo_controller_ =
-          std::make_unique<ReopenTabPromoController>(this);
-    }
-    reopen_tab_promo_controller_->ShowPromo();
-  }
+  if (iph_feature == InProductHelpFeature::kReopenTab)
+    reopen_tab_promo_controller_.ShowPromo();
 }
 #endif
 
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index bf8bfc65..61252ddc 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -54,6 +54,10 @@
 #include "chrome/browser/ui/views/quit_instruction_bubble_controller.h"
 #endif
 
+#if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
+#include "chrome/browser/ui/views/feature_promos/reopen_tab_promo_controller.h"
+#endif  // BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
+
 // NOTE: For more information about the objects and files in this directory,
 // view: http://dev.chromium.org/developers/design-documents/browser-window
 
@@ -66,7 +70,6 @@
 class FullscreenControlHost;
 class InfoBarContainerView;
 class LocationBarView;
-class ReopenTabPromoController;
 class StatusBubbleViews;
 class TabStrip;
 class ToolbarButtonProvider;
@@ -112,11 +115,9 @@
   // The browser view's class name.
   static const char kViewClassName[];
 
-  BrowserView();
+  explicit BrowserView(std::unique_ptr<Browser> browser);
   ~BrowserView() override;
 
-  void Init(std::unique_ptr<Browser> browser);
-
   void set_frame(BrowserFrame* frame) { frame_ = frame; }
   BrowserFrame* frame() const { return frame_; }
 
@@ -317,6 +318,7 @@
                           int index,
                           int reason) override;
   void OnTabDetached(content::WebContents* contents, bool was_active) override;
+  void OnTabRestoredFromMenu(int command_id) override;
   void ZoomChangedForActiveTab(bool can_show_bubble) override;
   gfx::Rect GetRestoredBounds() const override;
   ui::WindowShowState GetRestoredState() const override;
@@ -830,7 +832,7 @@
   std::unique_ptr<FullscreenControlHost> fullscreen_control_host_;
 
 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP)
-  std::unique_ptr<ReopenTabPromoController> reopen_tab_promo_controller_;
+  ReopenTabPromoController reopen_tab_promo_controller_{this};
 #endif
 
   struct ResizeSession {
diff --git a/chrome/browser/ui/views/frame/browser_window_factory.cc b/chrome/browser/ui/views/frame/browser_window_factory.cc
index b7cd3a0..3e4c00a 100644
--- a/chrome/browser/ui/views/frame/browser_window_factory.cc
+++ b/chrome/browser/ui/views/frame/browser_window_factory.cc
@@ -22,8 +22,7 @@
     bool user_gesture) {
   // Create the view and the frame. The frame will attach itself via the view
   // so we don't need to do anything with the pointer.
-  BrowserView* view = new BrowserView();
-  view->Init(std::move(browser));
+  BrowserView* view = new BrowserView(std::move(browser));
   (new BrowserFrame(view))->InitBrowserFrame();
   view->GetWidget()->non_client_view()->SetAccessibleName(
       l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
diff --git a/chrome/browser/ui/views/frame/test_with_browser_view.cc b/chrome/browser/ui/views/frame/test_with_browser_view.cc
index 1ad2154..8cca3dc 100644
--- a/chrome/browser/ui/views/frame/test_with_browser_view.cc
+++ b/chrome/browser/ui/views/frame/test_with_browser_view.cc
@@ -15,8 +15,8 @@
 #include "chrome/browser/search_engines/chrome_template_url_service_client.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
 #include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
-#include "chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h"
 #include "chrome/browser/signin/gaia_cookie_manager_service_factory.h"
+#include "chrome/browser/signin/gaia_cookie_manager_service_test_util.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/web_data_service_factory.h"
diff --git a/chrome/browser/ui/views/keyboard_access_browsertest.cc b/chrome/browser/ui/views/keyboard_access_browsertest.cc
index 35b3e1c1..d6bf791 100644
--- a/chrome/browser/ui/views/keyboard_access_browsertest.cc
+++ b/chrome/browser/ui/views/keyboard_access_browsertest.cc
@@ -207,8 +207,9 @@
 
   BrowserView* browser_view = reinterpret_cast<BrowserView*>(
       browser()->window());
-  SendKeysMenuListener menu_listener(browser_view->toolbar()->app_menu_button(),
-                                     browser(), false);
+  SendKeysMenuListener menu_listener(
+      browser_view->toolbar_button_provider()->GetAppMenuButton(), browser(),
+      false);
 
   if (focus_omnibox)
     browser()->window()->GetLocationBar()->FocusLocation();
@@ -318,8 +319,9 @@
 
   BrowserView* browser_view = reinterpret_cast<BrowserView*>(
       browser()->window());
-  SendKeysMenuListener menu_listener(browser_view->toolbar()->app_menu_button(),
-                                     browser(), true);
+  SendKeysMenuListener menu_listener(
+      browser_view->toolbar_button_provider()->GetAppMenuButton(), browser(),
+      true);
 
   browser()->window()->GetLocationBar()->FocusLocation();
 
diff --git a/chrome/browser/ui/views/location_bar/OWNERS b/chrome/browser/ui/views/location_bar/OWNERS
index fe2b68db..ac9b56c 100644
--- a/chrome/browser/ui/views/location_bar/OWNERS
+++ b/chrome/browser/ui/views/location_bar/OWNERS
@@ -1,4 +1,6 @@
 estade@chromium.org
 file://components/omnibox/OWNERS
 
+per-file custom_tab_bar_view.*=harrisjay@chromium.org
+
 # COMPONENT: UI>Browser>Omnibox
diff --git a/chrome/browser/ui/views/network_profile_bubble_view.cc b/chrome/browser/ui/views/network_profile_bubble_view.cc
index 2c2cddfa..7b4d7d7 100644
--- a/chrome/browser/ui/views/network_profile_bubble_view.cc
+++ b/chrome/browser/ui/views/network_profile_bubble_view.cc
@@ -130,7 +130,7 @@
   views::View* anchor = NULL;
   BrowserView* browser_view = BrowserView::GetBrowserViewForBrowser(browser);
   if (browser_view && browser_view->toolbar())
-    anchor = browser_view->toolbar()->app_menu_button();
+    anchor = browser_view->toolbar_button_provider()->GetAppMenuButton();
   NetworkProfileBubbleView* bubble =
       new NetworkProfileBubbleView(anchor, browser, browser->profile());
   views::BubbleDialogDelegateView::CreateBubble(bubble)->Show();
diff --git a/chrome/browser/ui/views/overlay/overlay_window_views.cc b/chrome/browser/ui/views/overlay/overlay_window_views.cc
index 3c9c323..307865ca 100644
--- a/chrome/browser/ui/views/overlay/overlay_window_views.cc
+++ b/chrome/browser/ui/views/overlay/overlay_window_views.cc
@@ -238,8 +238,10 @@
 gfx::Rect OverlayWindowViews::CalculateAndUpdateWindowBounds() {
   gfx::Rect work_area =
       display::Screen::GetScreen()
-          ->GetDisplayNearestWindow(
-              controller_->GetInitiatorWebContents()->GetTopLevelNativeWindow())
+          ->GetDisplayNearestWindow(IsVisible()
+                                        ? GetNativeWindow()
+                                        : controller_->GetInitiatorWebContents()
+                                              ->GetTopLevelNativeWindow())
           .work_area();
 
   // Upper bound size of the window is 50% of the display width and height.
@@ -610,7 +612,7 @@
 }
 
 bool OverlayWindowViews::IsVisible() const {
-  return views::Widget::IsVisible();
+  return is_initialized_ ? views::Widget::IsVisible() : false;
 }
 
 bool OverlayWindowViews::IsAlwaysOnTop() const {
diff --git a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index a75049a..0bfac5f 100644
--- a/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -543,7 +543,7 @@
 
   // Check the number of cookies shown is correct.
   base::string16 expected = l10n_util::GetPluralStringFUTF16(
-      IDS_PAGE_INFO_NUM_COOKIES,
+      IDS_PAGE_INFO_NUM_COOKIES_PARENTHESIZED,
       first_party_cookies.allowed + third_party_cookies.allowed);
   size_t index = api_->GetCookiesLinkText().find(expected);
   EXPECT_NE(std::string::npos, index);
diff --git a/chrome/browser/ui/views/payments/payment_request_can_make_payment_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_can_make_payment_browsertest.cc
index a9a76355..2a63c14 100644
--- a/chrome/browser/ui/views/payments/payment_request_can_make_payment_browsertest.cc
+++ b/chrome/browser/ui/views/payments/payment_request_can_make_payment_browsertest.cc
@@ -21,7 +21,10 @@
 class PaymentRequestCanMakePaymentQueryTest
     : public PaymentRequestBrowserTestBase {
  protected:
-  PaymentRequestCanMakePaymentQueryTest() {}
+  PaymentRequestCanMakePaymentQueryTest() {
+    feature_list_.InitAndDisableFeature(
+        ::features::kPaymentRequestHasEnrolledInstrument);
+  }
 
   void SetUpOnMainThread() override {
     PaymentRequestBrowserTestBase::SetUpOnMainThread();
@@ -39,6 +42,8 @@
   }
 
  private:
+  base::test::ScopedFeatureList feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(PaymentRequestCanMakePaymentQueryTest);
 };
 
@@ -72,8 +77,8 @@
 // in basic-card is disabled.
 IN_PROC_BROWSER_TEST_F(PaymentRequestCanMakePaymentQueryTest,
                        CanMakePayment_Supported_GooglePayCardsDisabled) {
-  base::test::ScopedFeatureList scoped_feature_list_;
-  scoped_feature_list_.InitAndDisableFeature(
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
       payments::features::kReturnGooglePayInBasicCard);
   NavigateTo("/payment_request_can_make_payment_query_test.html");
   autofill::CreditCard card = autofill::test::GetMaskedServerCard();
@@ -93,8 +98,8 @@
 // in basic-card is enabled.
 IN_PROC_BROWSER_TEST_F(PaymentRequestCanMakePaymentQueryTest,
                        CanMakePayment_Supported_GooglePayCardsEnabled) {
-  base::test::ScopedFeatureList scoped_feature_list_;
-  scoped_feature_list_.InitAndEnableFeature(
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
       payments::features::kReturnGooglePayInBasicCard);
   NavigateTo("/payment_request_can_make_payment_query_test.html");
   autofill::CreditCard card = autofill::test::GetMaskedServerCard();
@@ -171,7 +176,10 @@
 class PaymentRequestCanMakePaymentQueryCCTest
     : public PaymentRequestBrowserTestBase {
  protected:
-  PaymentRequestCanMakePaymentQueryCCTest() {}
+  PaymentRequestCanMakePaymentQueryCCTest() {
+    feature_list_.InitAndDisableFeature(
+        ::features::kPaymentRequestHasEnrolledInstrument);
+  }
 
   // If |visa| is true, then the method data is:
   //
@@ -189,6 +197,8 @@
   }
 
  private:
+  base::test::ScopedFeatureList feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(PaymentRequestCanMakePaymentQueryCCTest);
 };
 
@@ -271,6 +281,9 @@
     script_[CheckFor::BOB_PAY] = "checkBobPay();";
     script_[CheckFor::BOB_PAY_AND_BASIC_CARD] = "checkBobPayAndBasicCard();";
     script_[CheckFor::BOB_PAY_AND_VISA] = "checkBobPayAndVisa();";
+
+    feature_list_.InitAndDisableFeature(
+        ::features::kPaymentRequestHasEnrolledInstrument);
   }
 
   void CallCanMakePayment(CheckFor check_for) {
@@ -283,6 +296,8 @@
 
  private:
   std::map<CheckFor, std::string> script_;
+  base::test::ScopedFeatureList feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(PaymentRequestCanMakePaymentQueryPMITest);
 };
 
diff --git a/chrome/browser/ui/views/payments/payment_request_has_enrolled_instrument_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_has_enrolled_instrument_browsertest.cc
new file mode 100644
index 0000000..cda6c6b
--- /dev/null
+++ b/chrome/browser/ui/views/payments/payment_request_has_enrolled_instrument_browsertest.cc
@@ -0,0 +1,514 @@
+// 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 <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "components/autofill/core/browser/credit_card.h"
+#include "components/payments/core/features.h"
+#include "content/public/common/content_features.h"
+#include "content/public/test/browser_test_utils.h"
+
+namespace payments {
+
+class PaymentRequestHasEnrolledInstrumentQueryTest
+    : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestHasEnrolledInstrumentQueryTest() {
+    feature_list_.InitAndEnableFeature(
+        ::features::kPaymentRequestHasEnrolledInstrument);
+  }
+
+  void SetUpOnMainThread() override {
+    PaymentRequestBrowserTestBase::SetUpOnMainThread();
+
+    // By default for these tests, can make payment is enabled. Individual tests
+    // may override to false.
+    SetCanMakePaymentEnabledPref(true);
+  }
+
+  void CallCanMakePayment() {
+    ResetEventWaiterForSequence({DialogEvent::CAN_MAKE_PAYMENT_CALLED,
+                                 DialogEvent::CAN_MAKE_PAYMENT_RETURNED});
+    ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), "buy();"));
+    WaitForObservedEvent();
+  }
+
+  void CallHasEnrolledInstrument() {
+    ResetEventWaiterForSequence(
+        {DialogEvent::HAS_ENROLLED_INSTRUMENT_CALLED,
+         DialogEvent::HAS_ENROLLED_INSTRUMENT_RETURNED});
+    ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
+                                       "hasEnrolledInstrument();"));
+    WaitForObservedEvent();
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestHasEnrolledInstrumentQueryTest);
+};
+
+// Visa is required, and user has a visa instrument.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryTest,
+                       HasEnrolledInstrument) {
+  NavigateTo("/payment_request_can_make_payment_query_test.html");
+  const autofill::CreditCard card = autofill::test::GetCreditCard();  // Visa.
+  AddCreditCard(card);
+
+  CallCanMakePayment();
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument();
+  ExpectBodyContains({"true"});
+}
+
+// Visa is required, and user has a visa instrument, but canMakePayment and
+// hasEnrolledInstrument are disabled by user preference.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryTest,
+                       HasEnrolledInstrumentButDisabled) {
+  SetCanMakePaymentEnabledPref(false);
+  NavigateTo("/payment_request_can_make_payment_query_test.html");
+  const autofill::CreditCard card = autofill::test::GetCreditCard();  // Visa.
+  AddCreditCard(card);
+
+  CallCanMakePayment();
+  ExpectBodyContains({"false"});
+
+  CallHasEnrolledInstrument();
+  ExpectBodyContains({"false"});
+}
+
+// Visa is required, and user has a masked visa instrument, and Google Pay cards
+// in basic-card is disabled.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryTest,
+                       HasEnrolledInstrument_GooglePayCardsDisabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndDisableFeature(
+      payments::features::kReturnGooglePayInBasicCard);
+  NavigateTo("/payment_request_can_make_payment_query_test.html");
+  autofill::CreditCard card = autofill::test::GetMaskedServerCard();
+  card.SetNumber(base::ASCIIToUTF16("4111111111111111"));  // We need a visa.
+  card.SetNetworkForMaskedCard(autofill::kVisaCard);
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);
+
+  CallCanMakePayment();
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument();
+  ExpectBodyContains({"false"});
+}
+
+// Visa is required, and user has a masked visa instrument, and Google Pay cards
+// in basic-card is enabled.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryTest,
+                       HasEnrolledInstrument_GooglePayCardsEnabled) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      payments::features::kReturnGooglePayInBasicCard);
+  NavigateTo("/payment_request_can_make_payment_query_test.html");
+  autofill::CreditCard card = autofill::test::GetMaskedServerCard();
+  card.SetNumber(base::ASCIIToUTF16("4111111111111111"));  // We need a visa.
+  card.SetNetworkForMaskedCard(autofill::kVisaCard);
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);
+
+  CallCanMakePayment();
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument();
+  ExpectBodyContains({"true"});
+}
+
+// Pages without a valid SSL certificate always get "false" from
+// .canMakePayment() and .hasEnrolledInstrument().
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryTest,
+                       HasEnrolledInstrument_InvalidSSL) {
+  NavigateTo("/payment_request_can_make_payment_query_test.html");
+  SetInvalidSsl();
+
+  const autofill::CreditCard card = autofill::test::GetCreditCard();  // Visa.
+  AddCreditCard(card);
+
+  CallCanMakePayment();
+  ExpectBodyContains({"false"});
+
+  CallHasEnrolledInstrument();
+  ExpectBodyContains({"false"});
+}
+
+// Visa is required, user has a visa instrument, and user is in incognito
+// mode.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryTest,
+                       HasEnrolledInstrument_InIncognitoMode) {
+  NavigateTo("/payment_request_can_make_payment_query_test.html");
+  SetIncognito();
+
+  const autofill::CreditCard card = autofill::test::GetCreditCard();  // Visa.
+  AddCreditCard(card);
+
+  CallCanMakePayment();
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument();
+  ExpectBodyContains({"true"});
+}
+
+// Visa is required, and user doesn't have a visa instrument.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryTest,
+                       HasEnrolledInstrument_NotSupported) {
+  NavigateTo("/payment_request_can_make_payment_query_test.html");
+  const autofill::CreditCard card = autofill::test::GetCreditCard2();  // Amex.
+  AddCreditCard(card);
+
+  CallCanMakePayment();
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument();
+  ExpectBodyContains({"false"});
+}
+
+// Visa is required, user doesn't have a visa instrument and the user is in
+// incognito mode. In this case canMakePayment returns false as in a normal
+// profile.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryTest,
+                       HasEnrolledInstrument_NotSupported_InIncognitoMode) {
+  NavigateTo("/payment_request_can_make_payment_query_test.html");
+  SetIncognito();
+
+  const autofill::CreditCard card = autofill::test::GetCreditCard2();  // Amex.
+  AddCreditCard(card);
+
+  CallCanMakePayment();
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument();
+  ExpectBodyContains({"false"});
+}
+
+class PaymentRequestHasEnrolledInstrumentQueryCCTest
+    : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestHasEnrolledInstrumentQueryCCTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        ::features::kPaymentRequestHasEnrolledInstrument);
+  }
+
+  // If |visa| is true, then the method data is:
+  //
+  //   [{supportedMethods: ['visa']}]
+  //
+  // If |visa| is false, then the method data is:
+  //
+  //   [{supportedMethods: ['mastercard']}]
+  void CallCanMakePayment(bool visa) {
+    ResetEventWaiterForSequence({DialogEvent::CAN_MAKE_PAYMENT_CALLED,
+                                 DialogEvent::CAN_MAKE_PAYMENT_RETURNED});
+    ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(),
+                                       visa ? "buy();" : "other_buy();"));
+    WaitForObservedEvent();
+  }
+
+  void CallHasEnrolledInstrument(bool visa) {
+    ResetEventWaiterForSequence(
+        {DialogEvent::HAS_ENROLLED_INSTRUMENT_CALLED,
+         DialogEvent::HAS_ENROLLED_INSTRUMENT_RETURNED});
+    ASSERT_TRUE(content::ExecuteScript(
+        GetActiveWebContents(), visa ? "hasEnrolledInstrument('visa');"
+                                     : "hasEnrolledInstrument('mastercard');"));
+    WaitForObservedEvent();
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestHasEnrolledInstrumentQueryCCTest);
+};
+
+// Test that repeated canMakePayment and hasEnrolledInstrument queries are
+// allowed when the payment method specifics don't change.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryCCTest,
+                       HasEnrolledInstrumentQueryQuota) {
+  NavigateTo("/payment_request_can_make_payment_query_cc_test.html");
+
+  CallCanMakePayment(/*visa=*/true);
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument(/*visa=*/true);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(/*visa=*/false);
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument(/*visa=*/true);
+  ExpectBodyContains({"false"});
+
+  AddCreditCard(autofill::test::GetCreditCard());  // visa
+
+  CallCanMakePayment(/*visa=*/true);
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument(/*visa=*/true);
+  ExpectBodyContains({"true"});
+
+  CallCanMakePayment(/*visa=*/false);
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument(/*visa=*/true);
+  ExpectBodyContains({"true"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryCCTest,
+                       QueryQuota) {
+  NavigateTo("/payment_request_can_make_payment_query_cc_test.html");
+
+  CallHasEnrolledInstrument(/*visa=*/true);
+  ExpectBodyContains({"false"});
+
+  CallHasEnrolledInstrument(/*visa=*/false);
+  ExpectBodyContains({"NotAllowedError"});
+
+  AddCreditCard(autofill::test::GetCreditCard());  // visa
+
+  CallHasEnrolledInstrument(/*visa=*/true);
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument(/*visa=*/false);
+  ExpectBodyContains({"NotAllowedError"});
+}
+
+// hasEnrolledInstrument behaves the same in incognito mode as in normal mode to
+// avoid incognito mode detection.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryCCTest,
+                       QueryQuotaInIncognito) {
+  NavigateTo("/payment_request_can_make_payment_query_cc_test.html");
+  SetIncognito();
+
+  CallHasEnrolledInstrument(/*visa=*/true);
+  ExpectBodyContains({"false"});
+
+  CallHasEnrolledInstrument(/*visa=*/false);
+  ExpectBodyContains({"NotAllowedError"});
+
+  AddCreditCard(autofill::test::GetCreditCard());  // visa
+
+  CallHasEnrolledInstrument(/*visa=*/true);
+  ExpectBodyContains({"true"});
+
+  CallHasEnrolledInstrument(/*visa=*/false);
+  ExpectBodyContains({"NotAllowedError"});
+}
+
+class PaymentRequestHasEnrolledInstrumentQueryPMITest
+    : public PaymentRequestBrowserTestBase {
+ protected:
+  enum class CheckFor {
+    BASIC_VISA,
+    BASIC_CARD,
+    ALICE_PAY,
+    BOB_PAY,
+    BOB_PAY_AND_BASIC_CARD,
+    BOB_PAY_AND_VISA,
+  };
+
+  PaymentRequestHasEnrolledInstrumentQueryPMITest() {
+    script_[CheckFor::BASIC_VISA] = "[basicVisaMethod]";
+    script_[CheckFor::BASIC_CARD] = "[basicCardMethod]";
+    script_[CheckFor::ALICE_PAY] = "[alicePayMethod]";
+    script_[CheckFor::BOB_PAY] = "[bobPayMethod]";
+    script_[CheckFor::BOB_PAY_AND_BASIC_CARD] =
+        "[bobPayMethod, basicCardMethod]";
+    script_[CheckFor::BOB_PAY_AND_VISA] = "[bobPayMethod, visaMethod]";
+    scoped_feature_list_.InitAndEnableFeature(
+        ::features::kPaymentRequestHasEnrolledInstrument);
+  }
+
+  void CallCanMakePayment(CheckFor check_for) {
+    ResetEventWaiterForSequence({DialogEvent::CAN_MAKE_PAYMENT_CALLED,
+                                 DialogEvent::CAN_MAKE_PAYMENT_RETURNED});
+
+    ASSERT_TRUE(content::ExecuteScript(
+        GetActiveWebContents(),
+        "checkCanMakePayment(" + script_[check_for] + ");"));
+    WaitForObservedEvent();
+  }
+
+  void CallHasEnrolledInstrument(CheckFor check_for) {
+    ResetEventWaiterForSequence(
+        {DialogEvent::HAS_ENROLLED_INSTRUMENT_CALLED,
+         DialogEvent::HAS_ENROLLED_INSTRUMENT_RETURNED});
+    ASSERT_TRUE(content::ExecuteScript(
+        GetActiveWebContents(),
+        "checkHasEnrolledInstrument(" + script_[check_for] + ");"));
+    WaitForObservedEvent();
+  }
+
+ private:
+  std::map<CheckFor, std::string> script_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestHasEnrolledInstrumentQueryPMITest);
+};
+
+// If the device does not have any payment apps installed, canMakePayment() and
+// hasEnrolledInstrument() should return false for them.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryPMITest,
+                       QueryQuotaForPaymentApps) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      features::kWebPaymentsPerMethodCanMakePaymentQuota);
+  NavigateTo("/payment_request_payment_method_identifier_test.html");
+
+  CallCanMakePayment(CheckFor::ALICE_PAY);
+  ExpectBodyContains({"false"});
+  CallHasEnrolledInstrument(CheckFor::ALICE_PAY);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(CheckFor::BOB_PAY);
+  ExpectBodyContains({"false"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY);
+  ExpectBodyContains({"false"});
+}
+
+// If the device does not have any payment apps installed,
+// hasEnrolledInstrument() queries for both payment apps and
+// basic-card depend only on what cards the user has on file.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryPMITest,
+                       QueryQuotaForPaymentAppsAndCards) {
+  NavigateTo("/payment_request_payment_method_identifier_test.html");
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"NotAllowedError"});
+
+  AddCreditCard(autofill::test::GetCreditCard2());  // Amex
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"NotAllowedError"});
+
+  AddCreditCard(autofill::test::GetCreditCard());  // Visa
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"true"});
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"NotAllowedError"});
+}
+
+// Querying for payment apps in incognito returns result as normal mode to avoid
+// incognito mode detection. Multiple queries for different apps are rejected
+// with NotSupportedError to avoid user fingerprinting.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryPMITest,
+                       QueryQuotaForPaymentAppsInIncognitoMode) {
+  NavigateTo("/payment_request_payment_method_identifier_test.html");
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitWithFeatures(
+      /*enable_features=*/{::features::kServiceWorkerPaymentApps,
+                           features::kWebPaymentsPerMethodCanMakePaymentQuota},
+      /*disable_features=*/{});
+
+  SetIncognito();
+
+  CallCanMakePayment(CheckFor::ALICE_PAY);
+  ExpectBodyContains({"false"});
+  CallHasEnrolledInstrument(CheckFor::ALICE_PAY);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(CheckFor::BOB_PAY);
+  ExpectBodyContains({"false"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(CheckFor::ALICE_PAY);
+  ExpectBodyContains({"false"});
+  CallHasEnrolledInstrument(CheckFor::ALICE_PAY);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(CheckFor::BOB_PAY);
+  ExpectBodyContains({"false"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY);
+  ExpectBodyContains({"false"});
+}
+
+// Querying for both payment apps and autofill cards in incognito returns result
+// as in normal mode to avoid incognito mode detection.
+// Multiple queries for different payment methods are rejected with
+// NotSupportedError to avoid user fingerprinting.
+IN_PROC_BROWSER_TEST_F(PaymentRequestHasEnrolledInstrumentQueryPMITest,
+                       NoQueryQuotaForPaymentAppsAndCardsInIncognito) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      features::kWebPaymentsPerMethodCanMakePaymentQuota);
+  NavigateTo("/payment_request_payment_method_identifier_test.html");
+  SetIncognito();
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"NotAllowedError"});
+
+  AddCreditCard(autofill::test::GetCreditCard2());  // Amex
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_BASIC_CARD);
+  ExpectBodyContains({"NotAllowedError"});
+
+  AddCreditCard(autofill::test::GetCreditCard());  // Visa
+
+  CallCanMakePayment(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY_AND_VISA);
+  ExpectBodyContains({"true"});
+
+  CallCanMakePayment(CheckFor::BOB_PAY);
+  ExpectBodyContains({"false"});
+  CallHasEnrolledInstrument(CheckFor::BOB_PAY);
+  ExpectBodyContains({"false"});
+
+  CallCanMakePayment(CheckFor::BASIC_VISA);
+  ExpectBodyContains({"true"});
+  CallHasEnrolledInstrument(CheckFor::BASIC_VISA);
+  ExpectBodyContains({"true"});
+}
+
+}  // namespace payments
diff --git a/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc b/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc
new file mode 100644
index 0000000..159e790
--- /dev/null
+++ b/chrome/browser/ui/views/payments/payment_request_update_with_browsertest.cc
@@ -0,0 +1,117 @@
+// 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 "chrome/browser/ui/views/payments/payment_request_browsertest_base.h"
+#include "chrome/browser/ui/views/payments/payment_request_dialog_view_ids.h"
+#include "components/autofill/core/browser/autofill_profile.h"
+#include "components/autofill/core/browser/autofill_test_utils.h"
+#include "content/public/test/browser_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace payments {
+
+class PaymentRequestUpdateWithTest : public PaymentRequestBrowserTestBase {
+ protected:
+  PaymentRequestUpdateWithTest() {}
+
+  void RunJavaScriptFunctionToOpenPaymentRequestUI(
+      const std::string& function_name) {
+    ResetEventWaiterForDialogOpened();
+
+    content::WebContents* web_contents = GetActiveWebContents();
+    ASSERT_TRUE(content::ExecuteScript(web_contents, function_name + "();"));
+
+    WaitForObservedEvent();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PaymentRequestUpdateWithTest);
+};
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestUpdateWithTest,
+                       UpdateWithNoShippingOptions) {
+  NavigateTo("/payment_request_update_with_test.html");
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);
+
+  RunJavaScriptFunctionToOpenPaymentRequestUI("updateWithNoShippingOptions");
+
+  OpenOrderSummaryScreen();
+  EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  ClickOnBackArrow();
+
+  OpenShippingAddressSectionScreen();
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
+                               DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::SPEC_DONE_UPDATING,
+                               DialogEvent::BACK_NAVIGATION});
+  ClickOnChildInListViewAndWait(
+      /* child_index=*/1, /*total_num_children=*/2,
+      DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW,
+      /*wait_for_animation=*/false);
+  // Wait for the animation here explicitly, otherwise
+  // ClickOnChildInListViewAndWait tries to install an AnimationDelegate before
+  // the animation is kicked off (since that's triggered off of the spec being
+  // updated) and this hits a DCHECK.
+  WaitForAnimation();
+
+  OpenOrderSummaryScreen();
+  EXPECT_EQ(base::ASCIIToUTF16("$10.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  ClickOnBackArrow();
+
+  PayWithCreditCardAndWait(base::ASCIIToUTF16("123"));
+
+  ExpectBodyContains({"freeShipping"});
+}
+
+IN_PROC_BROWSER_TEST_F(PaymentRequestUpdateWithTest,
+                       UpdateWithShippingOptions) {
+  NavigateTo("/payment_request_update_with_test.html");
+  autofill::AutofillProfile billing_address = autofill::test::GetFullProfile();
+  AddAutofillProfile(billing_address);
+  AddAutofillProfile(autofill::test::GetFullProfile2());
+  autofill::CreditCard card = autofill::test::GetCreditCard();
+  card.set_billing_address_id(billing_address.guid());
+  AddCreditCard(card);
+
+  RunJavaScriptFunctionToOpenPaymentRequestUI("updateWithShippingOptions");
+
+  OpenOrderSummaryScreen();
+  EXPECT_EQ(base::ASCIIToUTF16("$5.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  ClickOnBackArrow();
+
+  OpenShippingAddressSectionScreen();
+  ResetEventWaiterForSequence({DialogEvent::PROCESSING_SPINNER_SHOWN,
+                               DialogEvent::PROCESSING_SPINNER_HIDDEN,
+                               DialogEvent::SPEC_DONE_UPDATING,
+                               DialogEvent::BACK_NAVIGATION});
+  ClickOnChildInListViewAndWait(
+      /* child_index=*/1, /*total_num_children=*/2,
+      DialogViewID::SHIPPING_ADDRESS_SHEET_LIST_VIEW,
+      /*wait_for_animation=*/false);
+  // Wait for the animation here explicitly, otherwise
+  // ClickOnChildInListViewAndWait tries to install an AnimationDelegate before
+  // the animation is kicked off (since that's triggered off of the spec being
+  // updated) and this hits a DCHECK.
+  WaitForAnimation();
+
+  OpenOrderSummaryScreen();
+  EXPECT_EQ(base::ASCIIToUTF16("$10.00"),
+            GetLabelText(DialogViewID::ORDER_SUMMARY_TOTAL_AMOUNT_LABEL));
+  ClickOnBackArrow();
+
+  PayWithCreditCardAndWait(base::ASCIIToUTF16("123"));
+
+  ExpectBodyContains({"updatedShipping"});
+}
+
+}  // namespace payments
diff --git a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
index 09f7a2f..145deb87 100644
--- a/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
+++ b/chrome/browser/ui/views/profiles/avatar_toolbar_button.cc
@@ -126,7 +126,7 @@
     const int incognito_window_count =
         BrowserList::GetIncognitoSessionsActiveForProfile(profile_);
     if (incognito_window_count > 1) {
-      text = base::IntToString16(incognito_window_count);
+      text = base::NumberToString16(incognito_window_count);
       if (GetThemeProvider()) {
         color = GetThemeProvider()->GetColor(
             ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON);
@@ -366,8 +366,7 @@
     const bool should_show_sync_paused_ui =
         AccountConsistencyModeManager::IsDiceEnabledForProfile(profile_) &&
         sync_ui_util::GetMessagesForAvatarSyncError(
-            profile_, IdentityManagerFactory::GetForProfile(profile_), &unused,
-            &unused) == sync_ui_util::AUTH_ERROR;
+            profile_, &unused, &unused) == sync_ui_util::AUTH_ERROR;
     return should_show_sync_paused_ui ? SyncState::kPaused : SyncState::kError;
   }
 #endif  // !defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/views/profiles/incognito_window_count_view.cc b/chrome/browser/ui/views/profiles/incognito_window_count_view.cc
index 40759a9a..07717ddc 100644
--- a/chrome/browser/ui/views/profiles/incognito_window_count_view.cc
+++ b/chrome/browser/ui/views/profiles/incognito_window_count_view.cc
@@ -10,6 +10,7 @@
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/views/chrome_typography.h"
 #include "chrome/browser/ui/views/hover_button.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
@@ -63,10 +64,13 @@
       views::BoxLayout::Orientation::kVertical));
 
   auto incognito_icon = std::make_unique<views::ImageView>();
-  // TODO(https://crbug.com/896235): Try replacing |kIncognitoCircleIcon| with
-  // drawing a circle under |kIncognitoIcon| and removing the circled icon.
+  // TODO(https://crbug.com/896235): Color to be updated after incognito
+  // redesign is finalized. Text color is chosen to have maximum contrast with
+  // background color.
+  const SkColor icon_color = views::style::GetColor(
+      *this, views::style::CONTEXT_TEXTFIELD, STYLE_PRIMARY_MONOSPACED);
   incognito_icon->SetImage(
-      gfx::CreateVectorIcon(kIncognitoCircleIcon, 40, gfx::kGoogleGrey100));
+      gfx::CreateVectorIcon(kIncognitoIcon, 40, icon_color));
 
   // TODO(https://crbug.com/915120): This Button is never clickable. Replace
   // by an alternative list item.
@@ -85,6 +89,10 @@
       l10n_util::GetStringUTF16(IDS_INCOGNITO_WINDOW_COUNTER_CLOSE_BUTTON)));
 }
 
+bool IncognitoWindowCountView::ShouldSnapFrameWidth() const {
+  return true;
+}
+
 void IncognitoWindowCountView::ButtonPressed(views::Button* sender,
                                              const ui::Event& event) {
   BrowserList::CloseAllBrowsersWithIncognitoProfile(
diff --git a/chrome/browser/ui/views/profiles/incognito_window_count_view.h b/chrome/browser/ui/views/profiles/incognito_window_count_view.h
index 141d56a7..4ef8747e 100644
--- a/chrome/browser/ui/views/profiles/incognito_window_count_view.h
+++ b/chrome/browser/ui/views/profiles/incognito_window_count_view.h
@@ -28,6 +28,7 @@
   // BubbleDialogDelegateView:
   int GetDialogButtons() const override;
   void Init() override;
+  bool ShouldSnapFrameWidth() const override;
 
   // BrowserListObserver:
   void OnBrowserRemoved(Browser* browser) override;
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index 3d4a0973..5d19aee 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -911,12 +911,9 @@
 views::View* ProfileChooserView::CreateSyncErrorViewIfNeeded(
     const AvatarMenu::Item& avatar_item) {
   int content_string_id, button_string_id;
-  auto* identity_manager =
-      IdentityManagerFactory::GetForProfile(browser_->profile());
   sync_ui_util::AvatarSyncErrorType error =
       sync_ui_util::GetMessagesForAvatarSyncError(
-          browser_->profile(), identity_manager, &content_string_id,
-          &button_string_id);
+          browser_->profile(), &content_string_id, &button_string_id);
   if (error == sync_ui_util::NO_SYNC_ERROR)
     return nullptr;
 
diff --git a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc
index 984d304..5a065fb 100644
--- a/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc
+++ b/chrome/browser/ui/views/relaunch_notification/relaunch_recommended_bubble_view.cc
@@ -48,8 +48,8 @@
 
   // Anchor the popup to the browser's app menu.
   auto* anchor_button = BrowserView::GetBrowserViewForBrowser(browser)
-                            ->toolbar()
-                            ->app_menu_button();
+                            ->toolbar_button_provider()
+                            ->GetAppMenuButton();
   auto* bubble_view = new RelaunchRecommendedBubbleView(
       anchor_button, gfx::Point(), detection_time, std::move(on_accept));
   bubble_view->SetArrow(views::BubbleBorder::TOP_RIGHT);
diff --git a/chrome/browser/ui/views/session_crashed_bubble_view_browsertest.cc b/chrome/browser/ui/views/session_crashed_bubble_view_browsertest.cc
index c1ff93b2..fcf0343 100644
--- a/chrome/browser/ui/views/session_crashed_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/session_crashed_bubble_view_browsertest.cc
@@ -26,8 +26,8 @@
     views::View* anchor_view = nullptr;
     if (anchor_rect == gfx::Rect()) {
       anchor_view = BrowserView::GetBrowserViewForBrowser(browser())
-                        ->toolbar()
-                        ->app_menu_button();
+                        ->toolbar_button_provider()
+                        ->GetAppMenuButton();
     }
     SessionCrashedBubbleView* crash_bubble =
         new SessionCrashedBubbleView(anchor_view, anchor_rect, browser(),
diff --git a/chrome/browser/ui/views/tabs/stacked_tab_strip_layout.cc b/chrome/browser/ui/views/tabs/stacked_tab_strip_layout.cc
index ee009c6..61cb3a8 100644
--- a/chrome/browser/ui/views/tabs/stacked_tab_strip_layout.cc
+++ b/chrome/browser/ui/views/tabs/stacked_tab_strip_layout.cc
@@ -265,7 +265,7 @@
       result += " ";
     if (i == active_index())
       result += "[";
-    result += base::IntToString(view_model_->ideal_bounds(i).x());
+    result += base::NumberToString(view_model_->ideal_bounds(i).x());
     if (i == active_index())
       result += "]";
   }
diff --git a/chrome/browser/ui/views/tabs/stacked_tab_strip_layout_unittest.cc b/chrome/browser/ui/views/tabs/stacked_tab_strip_layout_unittest.cc
index 4bd125e..05307e42 100644
--- a/chrome/browser/ui/views/tabs/stacked_tab_strip_layout_unittest.cc
+++ b/chrome/browser/ui/views/tabs/stacked_tab_strip_layout_unittest.cc
@@ -103,7 +103,7 @@
     for (int i = 0; i < view_model_.view_size(); ++i) {
       if (!result.empty())
         result += " ";
-      result += base::IntToString(view_model_.ideal_bounds(i).x());
+      result += base::NumberToString(view_model_.ideal_bounds(i).x());
     }
     return result;
   }
@@ -115,7 +115,7 @@
         result += " ";
       if (i == active_index)
         result += "[";
-      result += base::IntToString(view_model_.ideal_bounds(i).x());
+      result += base::NumberToString(view_model_.ideal_bounds(i).x());
       if (i == active_index)
         result += "]";
     }
diff --git a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
index b9513ea..3d7653e 100644
--- a/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
+++ b/chrome/browser/ui/views/tabs/tab_drag_controller_interactive_uitest.cc
@@ -184,7 +184,7 @@
             contents->GetUserData(
                 &kTabDragControllerInteractiveUITestUserDataKey));
     if (user_data)
-      result += base::IntToString(user_data->id());
+      result += base::NumberToString(user_data->id());
     else
       result += "?";
   }
diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc
index f0696ba..4fdadac 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip.cc
@@ -423,11 +423,15 @@
   Tab* tab = new Tab(this);
   AddChildViewAt(tab, view_index);
   const bool pinned = data.pinned;
-  tab->SetData(std::move(data));
   UpdateTabsClosingMap(model_index, 1);
   tabs_.Add(tab, model_index);
   selected_tabs_.IncrementFrom(model_index);
 
+  // Setting data must come after all state from the model has been updated
+  // above for the tab. Accessibility, in particular, reacts to data changed
+  // callbacks.
+  tab->SetData(std::move(data));
+
   // If the new tab button is visually after the tabs, make sure it is logically
   // afterwards as well so that the focus traversal order is correct.
   NewTabButtonPosition position = controller_->GetNewTabButtonPosition();
@@ -1341,7 +1345,7 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-// TabStrip, views::View overrides:
+// TabStrip, views::AccessiblePaneView overrides:
 
 void TabStrip::Layout() {
   // Only do a layout if our size changed.
@@ -1352,6 +1356,34 @@
   DoLayout();
 }
 
+bool TabStrip::OnMouseWheel(const ui::MouseWheelEvent& event) {
+  // Change the selected tab on horizontal wheel scrolls.
+  // Honor wheel scrolls over the tabs themselves, but not the NTB/surrounding
+  // space.
+  if ((!event.x_offset() && !event.IsShiftDown()) || tab_count() < 2 ||
+      !FindTabHitByPoint(event.location()))
+    return false;
+
+  accumulated_horizontal_scroll_ +=
+      (event.x_offset() ? event.x_offset() : event.y_offset());
+
+  int horizontal_offset =
+      accumulated_horizontal_scroll_ / ui::MouseWheelEvent::kWheelDelta;
+  if (!horizontal_offset)
+    return true;
+
+  accumulated_horizontal_scroll_ %= ui::MouseWheelEvent::kWheelDelta;
+
+  int new_active_index =
+      (controller_->GetActiveIndex() + horizontal_offset) % tab_count();
+  if (new_active_index < 0)
+    new_active_index += tab_count();
+
+  DCHECK(IsValidModelIndex(new_active_index));
+  controller_->SelectTab(new_active_index);
+  return true;
+}
+
 void TabStrip::PaintChildren(const views::PaintInfo& paint_info) {
   // The view order doesn't match the paint order (tabs_ contains the tab
   // ordering). Additionally we need to paint the tabs that are closing in
diff --git a/chrome/browser/ui/views/tabs/tab_strip.h b/chrome/browser/ui/views/tabs/tab_strip.h
index 402e409b..78aff58 100644
--- a/chrome/browser/ui/views/tabs/tab_strip.h
+++ b/chrome/browser/ui/views/tabs/tab_strip.h
@@ -290,8 +290,9 @@
   // MouseWatcherListener:
   void MouseMovedOutOfHost() override;
 
-  // views::View:
+  // views::AccessiblePaneView:
   void Layout() override;
+  bool OnMouseWheel(const ui::MouseWheelEvent& event) override;
   void PaintChildren(const views::PaintInfo& paint_info) override;
   void OnPaint(gfx::Canvas* canvas) override;
   const char* GetClassName() const override;
@@ -711,6 +712,10 @@
   // Number of mouse moves.
   int mouse_move_count_ = 0;
 
+  // Accumulatated offsets from thumb wheel. Used to throttle horizontal
+  // scroll from thumb wheel.
+  int accumulated_horizontal_scroll_ = 0;
+
   // Timer used when a tab is closed and we need to relayout. Only used when a
   // tab close comes from a touch device.
   base::OneShotTimer resize_layout_timer_;
diff --git a/chrome/browser/ui/views/tabs/tab_strip_layout_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_layout_unittest.cc
index 39405f4..6e830de8 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_layout_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_layout_unittest.cc
@@ -20,8 +20,8 @@
   for (const auto& bounds : tabs_bounds) {
     if (!result.empty())
       result += ", ";
-    result +=
-        base::IntToString(bounds.x()) + " " + base::IntToString(bounds.width());
+    result += base::NumberToString(bounds.x()) + " " +
+              base::NumberToString(bounds.width());
   }
   return result;
 }
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index 335c0fe..7c30cc8 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -896,4 +896,50 @@
   EXPECT_EQ(first_tab, tab_strip_->GetEventHandlerForPoint(tab_center));
 }
 
+// Switch selected tabs on horizontal scroll events.
+TEST_P(TabStripTest, HorizontalScroll) {
+  tab_strip_->SetBounds(0, 0, 200, 20);
+
+  for (int i = 0; i < 3; i++)
+    controller_->AddTab(i, true /* is_active */);
+
+  Tab* tab = tab_strip_->tab_at(0);
+  gfx::Point tab_center = tab->bounds().CenterPoint();
+
+  for (int i = 0; i < tab_strip_->tab_count(); ++i) {
+    ui::MouseWheelEvent wheel_event(
+        gfx::Vector2d(ui::MouseWheelEvent::kWheelDelta, 0), tab_center,
+        tab_center, ui::EventTimeForNow(), 0, 0);
+    tab_strip_->OnMouseWheel(wheel_event);
+    EXPECT_EQ(i, controller_->GetActiveIndex());
+  }
+
+  controller_->SelectTab(0);
+  for (int i = tab_strip_->tab_count() - 1; i >= 0; --i) {
+    ui::MouseWheelEvent wheel_event(
+        gfx::Vector2d(-ui::MouseWheelEvent::kWheelDelta, 0), tab_center,
+        tab_center, ui::EventTimeForNow(), 0, 0);
+    tab_strip_->OnMouseWheel(wheel_event);
+    EXPECT_EQ(i, controller_->GetActiveIndex());
+  }
+
+  // When offset is smaller than kWheelDelta, we don't scroll immediately.
+  // We wait offset until accumulated offset gets bigger than kWheelDelta.
+  const int small_offset = ui::MouseWheelEvent::kWheelDelta / 3;
+  int next_accumulated_offset = small_offset;
+  while (next_accumulated_offset < ui::MouseWheelEvent::kWheelDelta) {
+    ui::MouseWheelEvent wheel_event(gfx::Vector2d(small_offset, 0), tab_center,
+                                    tab_center, ui::EventTimeForNow(), 0, 0);
+    tab_strip_->OnMouseWheel(wheel_event);
+
+    EXPECT_EQ(0, controller_->GetActiveIndex());
+    next_accumulated_offset += small_offset;
+  }
+
+  ui::MouseWheelEvent wheel_event(gfx::Vector2d(small_offset, 0), tab_center,
+                                  tab_center, ui::EventTimeForNow(), 0, 0);
+  tab_strip_->OnMouseWheel(wheel_event);
+  EXPECT_EQ(1, controller_->GetActiveIndex());
+}
+
 INSTANTIATE_TEST_SUITE_P(, TabStripTest, ::testing::Values(false, true));
diff --git a/chrome/browser/ui/views/toolbar/app_menu.cc b/chrome/browser/ui/views/toolbar/app_menu.cc
index efa1185..4d7c885 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.cc
+++ b/chrome/browser/ui/views/toolbar/app_menu.cc
@@ -295,20 +295,12 @@
 };
 
 // AppMenuView is a view that can contain label buttons.
-class AppMenuView : public views::View,
-                    public views::ButtonListener,
-                    public AppMenuObserver {
+class AppMenuView : public views::View, public views::ButtonListener {
  public:
   AppMenuView(AppMenu* menu, ButtonMenuItemModel* menu_model)
-      : menu_(menu),
-        menu_model_(menu_model) {
-    menu_->AddObserver(this);
-  }
+      : menu_(menu->AsWeakPtr()), menu_model_(menu_model) {}
 
-  ~AppMenuView() override {
-    if (menu_)
-      menu_->RemoveObserver(this);
-  }
+  ~AppMenuView() override = default;
 
   // Overridden from views::View.
   void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
@@ -356,25 +348,21 @@
     return button;
   }
 
-  // Overridden from AppMenuObserver:
-  void AppMenuDestroyed() override {
-    menu_->RemoveObserver(this);
-    menu_ = nullptr;
-    menu_model_ = nullptr;
-  }
-
  protected:
-  AppMenu* menu() { return menu_; }
-  const AppMenu* menu() const { return menu_; }
-  ButtonMenuItemModel* menu_model() { return menu_model_; }
+  base::WeakPtr<AppMenu> menu() { return menu_; }
+  base::WeakPtr<const AppMenu> menu() const { return menu_; }
+  ButtonMenuItemModel* menu_model() {
+    // The menu and the items in the menu model have similar lifetimes; it's
+    // only safe to access model items if the menu is still alive.
+    DCHECK(menu_);
+    return menu_model_;
+  }
 
  private:
   // Hosting AppMenu.
-  // WARNING: this may be nullptr during shutdown.
-  AppMenu* menu_;
+  base::WeakPtr<AppMenu> menu_;
 
   // The menu model containing the increment/decrement/reset items.
-  // WARNING: this may be nullptr during shutdown.
   ButtonMenuItemModel* menu_model_;
 
   DISALLOW_COPY_AND_ASSIGN(AppMenuView);
@@ -800,8 +788,6 @@
     if (model)
       model->RemoveObserver(this);
   }
-  for (AppMenuObserver& observer : observer_list_)
-    observer.AppMenuDestroyed();
 }
 
 void AppMenu::Init(ui::MenuModel* model) {
@@ -980,10 +966,6 @@
 }
 
 void AppMenu::ExecuteCommand(int command_id, int mouse_event_flags) {
-  for (AppMenuObserver& observer : observer_list_) {
-    observer.OnExecuteCommand(command_id);
-  }
-
   if (IsBookmarkCommand(command_id)) {
     UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.OpenBookmark",
                         menu_opened_timer_.Elapsed());
diff --git a/chrome/browser/ui/views/toolbar/app_menu.h b/chrome/browser/ui/views/toolbar/app_menu.h
index ecf5701..08545ad 100644
--- a/chrome/browser/ui/views/toolbar/app_menu.h
+++ b/chrome/browser/ui/views/toolbar/app_menu.h
@@ -10,6 +10,7 @@
 #include <utility>
 
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
 #include "base/timer/elapsed_timer.h"
@@ -33,7 +34,8 @@
 // AppMenu adapts the AppMenuModel to view's menu related classes.
 class AppMenu : public views::MenuDelegate,
                 public bookmarks::BaseBookmarkModelObserver,
-                public content::NotificationObserver {
+                public content::NotificationObserver,
+                public base::SupportsWeakPtr<AppMenu> {
  public:
   enum RunFlags {
     NO_FLAGS = 0,
diff --git a/chrome/browser/ui/views/toolbar/app_menu_observer.h b/chrome/browser/ui/views/toolbar/app_menu_observer.h
index bd01c37..32d1a75 100644
--- a/chrome/browser/ui/views/toolbar/app_menu_observer.h
+++ b/chrome/browser/ui/views/toolbar/app_menu_observer.h
@@ -7,18 +7,11 @@
 
 class AppMenuObserver {
  public:
-  // Invoked when the AppMenu is about to be destroyed (from its destructor).
-  virtual void AppMenuDestroyed() {}
-
   virtual void AppMenuClosed() {}
 
   // Called after AppMenu::RunMenu().
   virtual void AppMenuShown() {}
 
-  // Called when a menu item is activated, just before the associated command is
-  // executed.
-  virtual void OnExecuteCommand(int command_id) {}
-
  protected:
   virtual ~AppMenuObserver() {}
 };
diff --git a/chrome/browser/ui/views/toolbar/browser_actions_container.cc b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
index 3aedc8d..8741e10 100644
--- a/chrome/browser/ui/views/toolbar/browser_actions_container.cc
+++ b/chrome/browser/ui/views/toolbar/browser_actions_container.cc
@@ -27,7 +27,6 @@
 #include "chrome/browser/ui/views/extensions/browser_action_drag_data.h"
 #include "chrome/browser/ui/views/frame/app_menu_button.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
-#include "chrome/browser/ui/views/toolbar/browser_app_menu_button.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_actions_bar_bubble_views.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_ink_drop_util.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
@@ -314,12 +313,10 @@
 }
 
 bool BrowserActionsContainer::CloseOverflowMenuIfOpen() {
-  // TODO(mgiuca): Use toolbar_button_provider() instead of toolbar(), so this
-  // also works for hosted app windows.
-  BrowserAppMenuButton* app_menu_button =
+  AppMenuButton* app_menu_button =
       BrowserView::GetBrowserViewForBrowser(browser_)
-          ->toolbar()
-          ->app_menu_button();
+          ->toolbar_button_provider()
+          ->GetAppMenuButton();
   if (!app_menu_button || !app_menu_button->IsMenuShowing())
     return false;
 
diff --git a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
index 5c11e0ec..c2c31b5 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_action_view_interactive_uitest.cc
@@ -35,10 +35,10 @@
 
 namespace {
 
-BrowserAppMenuButton* GetAppButtonFromBrowser(Browser* browser) {
+AppMenuButton* GetAppMenuButtonFromBrowser(Browser* browser) {
   return BrowserView::GetBrowserViewForBrowser(browser)
-      ->toolbar()
-      ->app_menu_button();
+      ->toolbar_button_provider()
+      ->GetAppMenuButton();
 }
 
 class AppMenuShowingWaiter : public views::MenuListener {
@@ -89,7 +89,7 @@
                                  ui_controls::MouseButton button,
                                  ToolbarActionView** toolbar_action_view) {
   // A bunch of plumbing to safely get at the overflowed toolbar action.
-  BrowserAppMenuButton* app_menu_button = GetAppButtonFromBrowser(browser);
+  auto* app_menu_button = GetAppMenuButtonFromBrowser(browser);
   EXPECT_TRUE(app_menu_button->IsMenuShowing());
   AppMenu* app_menu = app_menu_button->app_menu();
   ASSERT_TRUE(app_menu);
@@ -132,7 +132,7 @@
   EXPECT_TRUE(first_menu_item->enabled());
 
   // Get the overflow container.
-  BrowserAppMenuButton* app_menu_button = GetAppButtonFromBrowser(browser);
+  auto* app_menu_button = GetAppMenuButtonFromBrowser(browser);
   AppMenu* app_menu = app_menu_button->app_menu();
   ASSERT_TRUE(app_menu);
   ExtensionToolbarMenuView* menu_view =
@@ -216,7 +216,7 @@
   // opened. Listen for the message.
   ExtensionTestMessageListener listener("Popup opened", false);
 
-  BrowserAppMenuButton* app_menu_button = GetAppButtonFromBrowser(browser());
+  auto* app_menu_button = GetAppMenuButtonFromBrowser(browser());
 
   // Click on the app button.
   gfx::Point app_button_loc =
@@ -261,7 +261,7 @@
   // is spread across multiple rows.
   for (int i = 0; i < 15; ++i) {
     scoped_refptr<const extensions::Extension> extension =
-        extensions::ExtensionBuilder(base::IntToString(i))
+        extensions::ExtensionBuilder(base::NumberToString(i))
             .SetAction(extensions::ExtensionBuilder::ActionType::BROWSER_ACTION)
             .SetLocation(extensions::Manifest::INTERNAL)
             .Build();
@@ -274,7 +274,7 @@
   // Reduce visible count to 0 so that all actions are overflowed.
   ToolbarActionsModel::Get(profile())->SetVisibleIconCount(0);
 
-  BrowserAppMenuButton* app_menu_button = GetAppButtonFromBrowser(browser());
+  auto* app_menu_button = GetAppMenuButtonFromBrowser(browser());
   // Click on the app button.
   gfx::Point app_button_loc =
       ui_test_utils::GetCenterInScreenCoordinates(app_menu_button);
@@ -382,7 +382,7 @@
 
   // Open the app menu, navigate to the toolbar action, and activate it via the
   // keyboard.
-  BrowserAppMenuButton* app_menu_button = GetAppButtonFromBrowser(browser());
+  auto* app_menu_button = GetAppMenuButtonFromBrowser(browser());
   gfx::Point app_button_loc =
       ui_test_utils::GetCenterInScreenCoordinates(app_menu_button);
   EXPECT_TRUE(
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 fe53135..550ed19 100644
--- a/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/toolbar/toolbar_view_interactive_uitest.cc
@@ -50,7 +50,7 @@
   void OnMenuOpened() override;
 
  protected:
-  ToolbarView* toolbar_view() { return toolbar_view_; }
+  AppMenuButton* app_menu_button() { return app_menu_button_; }
   BrowserActionsContainer* browser_actions() { return browser_actions_; }
 
   // Performs a drag-and-drop operation by moving the mouse to |start|, clicking
@@ -63,7 +63,7 @@
   void SetUpOnMainThread() override;
   void TearDownOnMainThread() override;
 
-  ToolbarView* toolbar_view_ = nullptr;
+  AppMenuButton* app_menu_button_ = nullptr;
   BrowserActionsContainer* browser_actions_ = nullptr;
   bool menu_opened_ = false;
   base::OnceClosure quit_closure_;
@@ -83,7 +83,7 @@
   // TODO(devlin): In a perfect world, this would be factored better.
 
   // Begin listening for the app menu to open.
-  toolbar_view()->app_menu_button()->AddMenuListener(this);
+  app_menu_button()->AddMenuListener(this);
 
   // 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
@@ -114,9 +114,9 @@
   EXPECT_TRUE(menu_opened_);
 
   // The app menu should have closed once the drag-and-drop completed.
-  EXPECT_FALSE(toolbar_view()->app_menu_button()->IsMenuShowing());
+  EXPECT_FALSE(app_menu_button()->IsMenuShowing());
 
-  toolbar_view()->app_menu_button()->RemoveMenuListener(this);
+  app_menu_button()->RemoveMenuListener(this);
 }
 
 void ToolbarViewInteractiveUITest::SetUpCommandLine(
@@ -130,8 +130,10 @@
   extensions::ExtensionBrowserTest::SetUpOnMainThread();
   ExtensionToolbarMenuView::set_close_menu_delay_for_testing(0);
 
-  toolbar_view_ = BrowserView::GetBrowserViewForBrowser(browser())->toolbar();
-  browser_actions_ = toolbar_view_->browser_actions();
+  auto* browser_view = BrowserView::GetBrowserViewForBrowser(browser());
+  auto* toolbar_button_provider = browser_view->toolbar_button_provider();
+  app_menu_button_ = toolbar_button_provider->GetAppMenuButton();
+  browser_actions_ = toolbar_button_provider->GetBrowserActionsContainer();
 }
 
 void ToolbarViewInteractiveUITest::TearDownOnMainThread() {
@@ -168,8 +170,8 @@
 
   gfx::Point browser_action_view_loc =
       ui_test_utils::GetCenterInScreenCoordinates(view);
-  gfx::Point app_button_loc = ui_test_utils::GetCenterInScreenCoordinates(
-      toolbar_view()->app_menu_button());
+  gfx::Point app_button_loc =
+      ui_test_utils::GetCenterInScreenCoordinates(app_menu_button());
 
   // Perform a drag and drop from the browser action view to the app button,
   // which should open the app menu.
diff --git a/chrome/browser/ui/web_applications/bookmark_app_browsertest.cc b/chrome/browser/ui/web_applications/bookmark_app_browsertest.cc
index 3b4bef9..a345bc0 100644
--- a/chrome/browser/ui/web_applications/bookmark_app_browsertest.cc
+++ b/chrome/browser/ui/web_applications/bookmark_app_browsertest.cc
@@ -50,7 +50,7 @@
 }
 
 GURL GetUrlForSuffix(const std::string& prefix, int suffix) {
-  return GURL(prefix + base::IntToString(suffix) + ".com/");
+  return GURL(prefix + base::NumberToString(suffix) + ".com/");
 }
 
 // Must be zero-based as this will be stored in a bitset.
diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc
index 3524d74..3a2a079 100644
--- a/chrome/browser/ui/webui/about_ui.cc
+++ b/chrome/browser/ui/webui/about_ui.cc
@@ -508,7 +508,7 @@
   output->append("<meta charset='utf-8'>\n");
   if (refresh > 0) {
     output->append("<meta http-equiv='refresh' content='");
-    output->append(base::IntToString(refresh));
+    output->append(base::NumberToString(refresh));
     output->append("'/>\n");
   }
 }
diff --git a/chrome/browser/ui/webui/certificates_handler.cc b/chrome/browser/ui/webui/certificates_handler.cc
index f0d1c1f..799279d 100644
--- a/chrome/browser/ui/webui/certificates_handler.cc
+++ b/chrome/browser/ui/webui/certificates_handler.cc
@@ -32,7 +32,10 @@
 #include "chrome/browser/ui/crypto_module_password_dialog_nss.h"
 #include "chrome/browser/ui/webui/certificate_viewer_webui.h"
 #include "chrome/common/net/x509_certificate_model_nss.h"
+#include "chrome/common/pref_names.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/net_errors.h"
 #include "net/cert/x509_certificate.h"
@@ -75,6 +78,19 @@
   IMPORT_CA_FILE_SELECTED,
 };
 
+#if defined(OS_CHROMEOS)
+// Enumeration of certificate management permissions which corresponds to
+// values of policy CertificateManagementAllowed.
+enum class CertificateManagementPermission : int {
+  // Allow users to manage all certificates
+  kAll = 0,
+  // Allow users to manage user certificates
+  kUserOnly = 1,
+  // Disallow users from managing certificates
+  kNone = 2
+};
+#endif
+
 std::string OrgNameToId(const std::string& org) {
   return "org-" + org;
 }
@@ -560,6 +576,11 @@
   AssignWebUICallbackId(args);
   CHECK(args->GetBoolean(1, &use_hardware_backed_));
 
+#if defined(OS_CHROMEOS)
+  CHECK(IsCertificateManagementAllowedPolicy(Slot::kUser))
+      << "Importing certificates not allowed by policy";
+#endif
+
   ui::SelectFileDialog::FileTypeInfo file_type_info;
   file_type_info.extensions.resize(1);
   file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("p12"));
@@ -720,6 +741,11 @@
   CHECK_EQ(1U, args->GetSize());
   AssignWebUICallbackId(args);
 
+#if defined(OS_CHROMEOS)
+  CHECK(IsCertificateManagementAllowedPolicy(Slot::kUser))
+      << "Importing certificates not allowed by policy";
+#endif
+
   select_file_dialog_ = ui::SelectFileDialog::Create(
       this,
       std::make_unique<ChromeSelectFilePolicy>(web_ui()->GetWebContents()));
@@ -787,6 +813,11 @@
   CHECK_EQ(1U, args->GetSize());
   AssignWebUICallbackId(args);
 
+#if defined(OS_CHROMEOS)
+  CHECK(IsCertificateManagementAllowedPolicy(Slot::kUser))
+      << "Importing certificates not allowed by policy";
+#endif
+
   select_file_dialog_ = ui::SelectFileDialog::Create(
       this,
       std::make_unique<ChromeSelectFilePolicy>(web_ui()->GetWebContents()));
@@ -919,13 +950,12 @@
 }
 
 void CertificatesHandler::CertificateManagerModelReady() {
-  base::Value user_db_available_value(
-      certificate_manager_model_->is_user_db_available());
-  base::Value tpm_available_value(
-      certificate_manager_model_->is_tpm_available());
+  bool import_allowed = true;
+#if defined(OS_CHROMEOS)
+  import_allowed = IsCertificateManagementAllowedPolicy(Slot::kUser);
+#endif
   if (IsJavascriptAllowed()) {
-    FireWebUIListener("certificates-model-ready", user_db_available_value,
-                      tpm_available_value);
+    FireWebUIListener("certificates-model-ready", base::Value(import_allowed));
   }
   certificate_manager_model_->Refresh();
 }
@@ -985,14 +1015,14 @@
       // Move the CertInfo into |cert_info_id_map_|.
       CertificateManagerModel::CertInfo* cert_info = org_cert.get();
       std::string id =
-          base::IntToString(cert_info_id_map_.Add(std::move(org_cert)));
+          base::NumberToString(cert_info_id_map_.Add(std::move(org_cert)));
 
       base::DictionaryValue cert_dict;
       cert_dict.SetKey(kCertificatesHandlerKeyField, base::Value(id));
       cert_dict.SetKey(kCertificatesHandlerNameField,
                        base::Value(cert_info->name()));
       cert_dict.SetKey(kCertificatesHandlerReadonlyField,
-                       base::Value(cert_info->read_only()));
+                       base::Value(IsCertificateReadOnly(cert_info)));
       cert_dict.SetKey(kCertificatesHandlerUntrustedField,
                        base::Value(cert_info->untrusted()));
       cert_dict.SetKey(
@@ -1105,4 +1135,44 @@
   return cert_info_id_map_.Lookup(cert_info_id);
 }
 
+#if defined(OS_CHROMEOS)
+bool CertificatesHandler::IsCertificateManagementAllowedPolicy(
+    Slot slot) const {
+  Profile* profile = Profile::FromWebUI(web_ui());
+  PrefService* prefs = profile->GetPrefs();
+  auto policy_value = static_cast<CertificateManagementPermission>(
+      prefs->GetInteger(prefs::kCertificateManagementAllowed));
+
+  if (slot == Slot::kUser) {
+    return policy_value != CertificateManagementPermission::kNone;
+  }
+  return policy_value == CertificateManagementPermission::kAll;
+}
+#endif  // defined(OS_CHROMEOS)
+
+bool CertificatesHandler::IsCertificateReadOnly(
+    const CertificateManagerModel::CertInfo* cert_info) {
+  if (cert_info->read_only()) {
+    return true;
+  }
+
+#if defined(OS_CHROMEOS)
+  return !IsCertificateManagementAllowedPolicy(
+      cert_info->device_wide() ? Slot::kSystem : Slot::kUser);
+#else
+  return false;
+#endif
+}
+
+#if defined(OS_CHROMEOS)
+void CertificatesHandler::RegisterProfilePrefs(
+    user_prefs::PrefRegistrySyncable* registry) {
+  // Allow users to manage all certificates by default. This can be overridden
+  // by enterprise policy.
+  registry->RegisterIntegerPref(
+      prefs::kCertificateManagementAllowed,
+      static_cast<int>(CertificateManagementPermission::kAll));
+}
+#endif  // defined(OS_CHROMEOS)
+
 }  // namespace certificate_manager
diff --git a/chrome/browser/ui/webui/certificates_handler.h b/chrome/browser/ui/webui/certificates_handler.h
index d7d4a83..50df107 100644
--- a/chrome/browser/ui/webui/certificates_handler.h
+++ b/chrome/browser/ui/webui/certificates_handler.h
@@ -19,6 +19,14 @@
 #include "ui/gfx/native_widget_types.h"
 #include "ui/shell_dialogs/select_file_dialog.h"
 
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+#if defined(OS_CHROMEOS)
+enum class Slot { kUser, kSystem };
+#endif
+
 namespace certificate_manager {
 
 class FileAccessProvider;
@@ -42,6 +50,11 @@
                     void* params) override;
   void FileSelectionCanceled(void* params) override;
 
+#if defined(OS_CHROMEOS)
+  // Register profile preferences.
+  static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+#endif
+
  private:
   // View certificate.
   void HandleViewCertificate(const base::ListValue* args);
@@ -156,13 +169,25 @@
 
   gfx::NativeWindow GetParentWindow() const;
 
-  // Assuming that |args| is a list, parses the list element at |arg_index| as
-  // an id for |cert_info_id_map_| and looks up the corresponding CertInfo. If
-  // anything goes wrong, returns nullptr.
+  // If |args| is a list, parses the list element at |arg_index| as an id for
+  // |cert_info_id_map_| and looks up the corresponding CertInfo. If there is
+  // an error parsing the list, returns nullptr.
   CertificateManagerModel::CertInfo* GetCertInfoFromCallbackArgs(
       const base::Value& args,
       size_t arg_index);
 
+#if defined(OS_CHROMEOS)
+  // Returns true if the user may manage certificates on |slot| according
+  // CertificateManagementAllowed to policy.
+  bool IsCertificateManagementAllowedPolicy(Slot slot) const;
+#endif
+
+  // Returns true if the certificate represented by |cert_info| is read-only
+  // (i.e. can not be deleted). Evaluates the certificate attributes and, on
+  // Chrome OS devices, the enterprise policy CertificateManagementAllowed.
+  bool IsCertificateReadOnly(
+      const CertificateManagerModel::CertInfo* cert_info);
+
   // The Certificates Manager model
   bool requested_certificate_manager_model_;
   std::unique_ptr<CertificateManagerModel> certificate_manager_model_;
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 04b957a..957584b 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -455,7 +455,7 @@
     return &NewWebUI<BookmarksUI>;
   // Downloads list on Android uses the built-in download manager.
   if (url.host_piece() == chrome::kChromeUIDownloadsHost)
-    return &NewWebUI<MdDownloadsUI>;
+    return &NewWebUI<DownloadsUI>;
   // Identity API is not available on Android.
   if (url.host_piece() == chrome::kChromeUIIdentityInternalsHost)
     return &NewWebUI<IdentityInternalsUI>;
@@ -876,7 +876,7 @@
 
   // Android uses the native download manager.
   if (page_url.host_piece() == chrome::kChromeUIDownloadsHost)
-    return MdDownloadsUI::GetFaviconResourceBytes(scale_factor);
+    return DownloadsUI::GetFaviconResourceBytes(scale_factor);
 
   // Android doesn't use the Options/Settings pages.
   if (page_url.host_piece() == chrome::kChromeUISettingsHost)
diff --git a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
index ef2ba539..4baec26 100644
--- a/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
+++ b/chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/ui/webui/chromeos/assistant_optin/assistant_optin_ui.h"
 
 #include <memory>
+#include <string>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/macros.h"
@@ -122,7 +124,7 @@
   PrefService* prefs = ProfileManager::GetActiveUserProfile()->GetPrefs();
   const bool completed =
       prefs->GetBoolean(arc::prefs::kVoiceInteractionEnabled) &&
-      prefs->GetBoolean(arc::prefs::kArcVoiceInteractionValuePropAccepted);
+      prefs->GetBoolean(arc::prefs::kVoiceInteractionActivityControlAccepted);
   std::move(callback_).Run(completed);
   SystemWebDialogDelegate::OnDialogClosed(json_retval);
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.cc b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.cc
index b3747d6..1beaa08 100644
--- a/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.cc
+++ b/chrome/browser/ui/webui/chromeos/login/discover/modules/discover_module_pin_setup.cc
@@ -82,7 +82,7 @@
 
   // Format numbers to be used on the pin keyboard.
   for (int j = 0; j <= 9; j++) {
-    builder->Add("pinKeyboard" + base::IntToString(j),
+    builder->Add("pinKeyboard" + base::NumberToString(j),
                  base::FormatNumber(int64_t{j}));
   }
   builder->Add("pinKeyboardPlaceholderPin", IDS_PIN_KEYBOARD_HINT_TEXT_PIN);
diff --git a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
index 03f0584..0bd7f65f 100644
--- a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
@@ -479,7 +479,7 @@
   const std::string description = base::StringPrintf(
       "Auto generated feedback for http://crbug.com/719266.\n"
       "(uniquifier:%s)",
-      base::Int64ToString(base::Time::Now().ToInternalValue()).c_str());
+      base::NumberToString(base::Time::Now().ToInternalValue()).c_str());
   login_feedback_.reset(new LoginFeedback(Profile::FromWebUI(web_ui())));
   login_feedback_->Request(description, base::Closure());
 }
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
index 095fab6..9192c59 100644
--- a/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
+++ b/chrome/browser/ui/webui/chromeos/login/oobe_display_chooser.cc
@@ -98,7 +98,7 @@
       auto config_properties = ash::mojom::DisplayConfigProperties::New();
       config_properties->set_primary = true;
       cros_display_config_ptr_->SetDisplayProperties(
-          base::Int64ToString(device.target_display_id),
+          base::NumberToString(device.target_display_id),
           std::move(config_properties), base::DoNothing());
       break;
     }
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 0323e94..b4555044 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -302,7 +302,7 @@
     ::login::LocalizedValuesBuilder* builder) {
   // Format numbers to be used on the pin keyboard.
   for (int j = 0; j <= 9; j++) {
-    builder->Add("pinKeyboard" + base::IntToString(j),
+    builder->Add("pinKeyboard" + base::NumberToString(j),
                  base::FormatNumber(int64_t{j}));
   }
 
@@ -1497,7 +1497,7 @@
   const std::string description = base::StringPrintf(
       "Auto generated feedback for http://crbug.com/547857.\n"
       "(uniquifier:%s)",
-      base::Int64ToString(base::Time::Now().ToInternalValue()).c_str());
+      base::NumberToString(base::Time::Now().ToInternalValue()).c_str());
 
   login_feedback_ =
       std::make_unique<LoginFeedback>(Profile::FromWebUI(web_ui()));
diff --git a/chrome/browser/ui/webui/cookies_tree_model_util.cc b/chrome/browser/ui/webui/cookies_tree_model_util.cc
index f5a3c69..6beaf18 100644
--- a/chrome/browser/ui/webui/cookies_tree_model_util.cc
+++ b/chrome/browser/ui/webui/cookies_tree_model_util.cc
@@ -49,7 +49,6 @@
 const char kKeyPath[] = "path";
 const char kKeySendFor[] = "sendfor";
 const char kKeyAccessibleToScript[] = "accessibleToScript";
-const char kKeyDesc[] = "desc";
 const char kKeySize[] = "size";
 const char kKeyOrigin[] = "origin";
 const char kKeyManifest[] = "manifest";
@@ -79,11 +78,11 @@
 std::string CookiesTreeModelUtil::GetTreeNodeId(const CookieTreeNode* node) {
   CookieTreeNodeMap::const_iterator iter = node_map_.find(node);
   if (iter != node_map_.end())
-    return base::IntToString(iter->second);
+    return base::NumberToString(iter->second);
 
   int32_t new_id = id_map_.Add(node);
   node_map_[node] = new_id;
-  return base::IntToString(new_id);
+  return base::NumberToString(new_id);
 }
 
 bool CookiesTreeModelUtil::GetCookieTreeNodeDictionary(
@@ -127,16 +126,14 @@
     case CookieTreeNode::DetailedInfo::TYPE_DATABASE: {
       dict->SetString(kKeyType, "database");
 
-      const BrowsingDataDatabaseHelper::DatabaseInfo& database_info =
-          *node.GetDetailedInfo().database_info;
+      const content::StorageUsageInfo& usage_info =
+          *node.GetDetailedInfo().usage_info;
 
-      dict->SetString(kKeyName, database_info.database_name.empty() ?
-          l10n_util::GetStringUTF8(IDS_COOKIES_WEB_DATABASE_UNNAMED_NAME) :
-          database_info.database_name);
-      dict->SetString(kKeyDesc, database_info.description);
-      dict->SetString(kKeySize, ui::FormatBytes(database_info.size));
-      dict->SetString(kKeyModified, base::UTF16ToUTF8(
-          base::TimeFormatFriendlyDateAndTime(database_info.last_modified)));
+      dict->SetString(kKeyOrigin, usage_info.origin.Serialize());
+      dict->SetString(kKeySize, ui::FormatBytes(usage_info.total_size_bytes));
+      dict->SetString(kKeyModified,
+                      base::UTF16ToUTF8(base::TimeFormatFriendlyDateAndTime(
+                          usage_info.last_modified)));
 
       break;
     }
diff --git a/chrome/browser/ui/webui/downloads/downloads.mojom b/chrome/browser/ui/webui/downloads/downloads.mojom
index 4dec883..07926b9 100644
--- a/chrome/browser/ui/webui/downloads/downloads.mojom
+++ b/chrome/browser/ui/webui/downloads/downloads.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-module md_downloads.mojom;
+module downloads.mojom;
 
 struct Data {
   bool file_externally_removed;
diff --git a/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc b/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc
index ce9d238e..6031ead5 100644
--- a/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc
+++ b/chrome/browser/ui/webui/downloads/downloads_dom_handler.cc
@@ -7,6 +7,7 @@
 #include <algorithm>
 #include <functional>
 #include <memory>
+#include <string>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -76,9 +77,9 @@
 
 }  // namespace
 
-MdDownloadsDOMHandler::MdDownloadsDOMHandler(
-    md_downloads::mojom::PageHandlerRequest request,
-    md_downloads::mojom::PagePtr page,
+DownloadsDOMHandler::DownloadsDOMHandler(
+    downloads::mojom::PageHandlerRequest request,
+    downloads::mojom::PagePtr page,
     content::DownloadManager* download_manager,
     content::WebUI* web_ui)
     : list_tracker_(download_manager, std::move(page)),
@@ -91,7 +92,7 @@
   CheckForRemovedFiles();
 }
 
-MdDownloadsDOMHandler::~MdDownloadsDOMHandler() {
+DownloadsDOMHandler::~DownloadsDOMHandler() {
   list_tracker_.Stop();
   list_tracker_.Reset();
   if (!render_process_gone_)
@@ -99,13 +100,13 @@
   FinalizeRemovals();
 }
 
-void MdDownloadsDOMHandler::RenderProcessGone(base::TerminationStatus status) {
+void DownloadsDOMHandler::RenderProcessGone(base::TerminationStatus status) {
   // TODO(dbeam): WebUI + WebUIMessageHandler should do this automatically.
   // http://crbug.com/610450
   render_process_gone_ = true;
 }
 
-void MdDownloadsDOMHandler::GetDownloads(
+void DownloadsDOMHandler::GetDownloads(
     const std::vector<std::string>& search_terms) {
   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_GET_DOWNLOADS);
 
@@ -116,7 +117,7 @@
   list_tracker_.StartAndSendChunk();
 }
 
-void MdDownloadsDOMHandler::OpenFileRequiringGesture(const std::string& id) {
+void DownloadsDOMHandler::OpenFileRequiringGesture(const std::string& id) {
   if (!GetWebUIWebContents()->HasRecentInteractiveInputEvent()) {
     LOG(ERROR) << "OpenFileRequiringGesture received without recent "
                   "user interaction";
@@ -129,7 +130,7 @@
     file->OpenDownload();
 }
 
-void MdDownloadsDOMHandler::Drag(const std::string& id) {
+void DownloadsDOMHandler::Drag(const std::string& id) {
   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DRAG);
   download::DownloadItem* file = GetDownloadByStringId(id);
   if (!file)
@@ -153,8 +154,7 @@
   }
 }
 
-void MdDownloadsDOMHandler::SaveDangerousRequiringGesture(
-    const std::string& id) {
+void DownloadsDOMHandler::SaveDangerousRequiringGesture(const std::string& id) {
   if (!GetWebUIWebContents()->HasRecentInteractiveInputEvent()) {
     LOG(ERROR) << "SaveDangerousRequiringGesture received without recent "
                   "user interaction";
@@ -167,12 +167,12 @@
     ShowDangerPrompt(file);
 }
 
-void MdDownloadsDOMHandler::DiscardDangerous(const std::string& id) {
+void DownloadsDOMHandler::DiscardDangerous(const std::string& id) {
   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_DISCARD_DANGEROUS);
   RemoveDownloadInArgs(id);
 }
 
-void MdDownloadsDOMHandler::RetryDownload(const std::string& id) {
+void DownloadsDOMHandler::RetryDownload(const std::string& id) {
   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_RETRY_DOWNLOAD);
 
   download::DownloadItem* file = GetDownloadByStringId(id);
@@ -214,28 +214,28 @@
       ->DownloadUrl(std::move(dl_params));
 }
 
-void MdDownloadsDOMHandler::Show(const std::string& id) {
+void DownloadsDOMHandler::Show(const std::string& id) {
   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_SHOW);
   download::DownloadItem* file = GetDownloadByStringId(id);
   if (file)
     file->ShowDownloadInShell();
 }
 
-void MdDownloadsDOMHandler::Pause(const std::string& id) {
+void DownloadsDOMHandler::Pause(const std::string& id) {
   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_PAUSE);
   download::DownloadItem* file = GetDownloadByStringId(id);
   if (file)
     file->Pause();
 }
 
-void MdDownloadsDOMHandler::Resume(const std::string& id) {
+void DownloadsDOMHandler::Resume(const std::string& id) {
   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_RESUME);
   download::DownloadItem* file = GetDownloadByStringId(id);
   if (file)
     file->Resume(true);
 }
 
-void MdDownloadsDOMHandler::Remove(const std::string& id) {
+void DownloadsDOMHandler::Remove(const std::string& id) {
   if (!IsDeletingHistoryAllowed())
     return;
 
@@ -243,7 +243,7 @@
   RemoveDownloadInArgs(id);
 }
 
-void MdDownloadsDOMHandler::Undo() {
+void DownloadsDOMHandler::Undo() {
   // TODO(dbeam): handle more than removed downloads someday?
   if (removals_.empty())
     return;
@@ -275,14 +275,14 @@
     list_tracker_.StartAndSendChunk();
 }
 
-void MdDownloadsDOMHandler::Cancel(const std::string& id) {
+void DownloadsDOMHandler::Cancel(const std::string& id) {
   CountDownloadsDOMEvents(DOWNLOADS_DOM_EVENT_CANCEL);
   download::DownloadItem* file = GetDownloadByStringId(id);
   if (file)
     file->Cancel(true);
 }
 
-void MdDownloadsDOMHandler::ClearAll() {
+void DownloadsDOMHandler::ClearAll() {
   if (!IsDeletingHistoryAllowed()) {
     // This should only be reached during tests.
     return;
@@ -303,7 +303,7 @@
   list_tracker_.StartAndSendChunk();
 }
 
-void MdDownloadsDOMHandler::RemoveDownloads(const DownloadVector& to_remove) {
+void DownloadsDOMHandler::RemoveDownloads(const DownloadVector& to_remove) {
   IdSet ids;
 
   for (auto* download : to_remove) {
@@ -328,7 +328,7 @@
     removals_.push_back(ids);
 }
 
-void MdDownloadsDOMHandler::OpenDownloadsFolderRequiringGesture() {
+void DownloadsDOMHandler::OpenDownloadsFolderRequiringGesture() {
   if (!GetWebUIWebContents()->HasRecentInteractiveInputEvent()) {
     LOG(ERROR) << "OpenDownloadsFolderRequiringGesture received without recent "
                   "user interaction";
@@ -345,19 +345,18 @@
   }
 }
 
-// MdDownloadsDOMHandler, private: --------------------------------------------
+// DownloadsDOMHandler, private: --------------------------------------------
 
-content::DownloadManager* MdDownloadsDOMHandler::GetMainNotifierManager()
-    const {
+content::DownloadManager* DownloadsDOMHandler::GetMainNotifierManager() const {
   return list_tracker_.GetMainNotifierManager();
 }
 
-content::DownloadManager* MdDownloadsDOMHandler::GetOriginalNotifierManager()
+content::DownloadManager* DownloadsDOMHandler::GetOriginalNotifierManager()
     const {
   return list_tracker_.GetOriginalNotifierManager();
 }
 
-void MdDownloadsDOMHandler::FinalizeRemovals() {
+void DownloadsDOMHandler::FinalizeRemovals() {
   while (!removals_.empty()) {
     const IdSet remove = removals_.back();
     removals_.pop_back();
@@ -370,20 +369,19 @@
   }
 }
 
-void MdDownloadsDOMHandler::ShowDangerPrompt(
+void DownloadsDOMHandler::ShowDangerPrompt(
     download::DownloadItem* dangerous_item) {
   DownloadDangerPrompt* danger_prompt = DownloadDangerPrompt::Create(
-      dangerous_item,
-      GetWebUIWebContents(),
-      false,
-      base::Bind(&MdDownloadsDOMHandler::DangerPromptDone,
+      dangerous_item, GetWebUIWebContents(), false,
+      base::Bind(&DownloadsDOMHandler::DangerPromptDone,
                  weak_ptr_factory_.GetWeakPtr(), dangerous_item->GetId()));
   // danger_prompt will delete itself.
   DCHECK(danger_prompt);
 }
 
-void MdDownloadsDOMHandler::DangerPromptDone(
-    int download_id, DownloadDangerPrompt::Action action) {
+void DownloadsDOMHandler::DangerPromptDone(
+    int download_id,
+    DownloadDangerPrompt::Action action) {
   if (action != DownloadDangerPrompt::ACCEPT)
     return;
   download::DownloadItem* item = NULL;
@@ -397,14 +395,14 @@
   item->ValidateDangerousDownload();
 }
 
-bool MdDownloadsDOMHandler::IsDeletingHistoryAllowed() {
+bool DownloadsDOMHandler::IsDeletingHistoryAllowed() {
   content::DownloadManager* manager = GetMainNotifierManager();
   return manager &&
          Profile::FromBrowserContext(manager->GetBrowserContext())->
              GetPrefs()->GetBoolean(prefs::kAllowDeletingBrowserHistory);
 }
 
-download::DownloadItem* MdDownloadsDOMHandler::GetDownloadByStringId(
+download::DownloadItem* DownloadsDOMHandler::GetDownloadByStringId(
     const std::string& id) {
   uint64_t id_num;
   if (!base::StringToUint64(id, &id_num)) {
@@ -415,7 +413,7 @@
   return GetDownloadById(static_cast<uint32_t>(id_num));
 }
 
-download::DownloadItem* MdDownloadsDOMHandler::GetDownloadById(uint32_t id) {
+download::DownloadItem* DownloadsDOMHandler::GetDownloadById(uint32_t id) {
   download::DownloadItem* item = NULL;
   if (GetMainNotifierManager())
     item = GetMainNotifierManager()->GetDownload(id);
@@ -424,18 +422,18 @@
   return item;
 }
 
-content::WebContents* MdDownloadsDOMHandler::GetWebUIWebContents() {
+content::WebContents* DownloadsDOMHandler::GetWebUIWebContents() {
   return web_ui_->GetWebContents();
 }
 
-void MdDownloadsDOMHandler::CheckForRemovedFiles() {
+void DownloadsDOMHandler::CheckForRemovedFiles() {
   if (GetMainNotifierManager())
     GetMainNotifierManager()->CheckForHistoryFilesRemoval();
   if (GetOriginalNotifierManager())
     GetOriginalNotifierManager()->CheckForHistoryFilesRemoval();
 }
 
-void MdDownloadsDOMHandler::RemoveDownloadInArgs(const std::string& id) {
+void DownloadsDOMHandler::RemoveDownloadInArgs(const std::string& id) {
   download::DownloadItem* file = GetDownloadByStringId(id);
   if (!file)
     return;
diff --git a/chrome/browser/ui/webui/downloads/downloads_dom_handler.h b/chrome/browser/ui/webui/downloads/downloads_dom_handler.h
index a85079a..554d69b 100644
--- a/chrome/browser/ui/webui/downloads/downloads_dom_handler.h
+++ b/chrome/browser/ui/webui/downloads/downloads_dom_handler.h
@@ -31,19 +31,19 @@
 // The handler for Javascript messages related to the "downloads" view,
 // also observes changes to the download manager.
 // TODO(calamity): Remove WebUIMessageHandler.
-class MdDownloadsDOMHandler : public content::WebContentsObserver,
-                              public md_downloads::mojom::PageHandler {
+class DownloadsDOMHandler : public content::WebContentsObserver,
+                            public downloads::mojom::PageHandler {
  public:
-  MdDownloadsDOMHandler(md_downloads::mojom::PageHandlerRequest request,
-                        md_downloads::mojom::PagePtr page,
-                        content::DownloadManager* download_manager,
-                        content::WebUI* web_ui);
-  ~MdDownloadsDOMHandler() override;
+  DownloadsDOMHandler(downloads::mojom::PageHandlerRequest request,
+                      downloads::mojom::PagePtr page,
+                      content::DownloadManager* download_manager,
+                      content::WebUI* web_ui);
+  ~DownloadsDOMHandler() override;
 
   // WebContentsObserver implementation.
   void RenderProcessGone(base::TerminationStatus status) override;
 
-  // md_downloads::mojom::PageHandler:
+  // downloads::mojom::PageHandler:
   void GetDownloads(const std::vector<std::string>& search_terms) override;
   void OpenFileRequiringGesture(const std::string& id) override;
   void Drag(const std::string& id) override;
@@ -123,11 +123,11 @@
 
   content::WebUI* web_ui_;
 
-  mojo::Binding<md_downloads::mojom::PageHandler> binding_;
+  mojo::Binding<downloads::mojom::PageHandler> binding_;
 
-  base::WeakPtrFactory<MdDownloadsDOMHandler> weak_ptr_factory_{this};
+  base::WeakPtrFactory<DownloadsDOMHandler> weak_ptr_factory_{this};
 
-  DISALLOW_COPY_AND_ASSIGN(MdDownloadsDOMHandler);
+  DISALLOW_COPY_AND_ASSIGN(DownloadsDOMHandler);
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_DOWNLOADS_DOWNLOADS_DOM_HANDLER_H_
diff --git a/chrome/browser/ui/webui/downloads/downloads_dom_handler_unittest.cc b/chrome/browser/ui/webui/downloads/downloads_dom_handler_unittest.cc
index d6bf1da..31e1659b 100644
--- a/chrome/browser/ui/webui/downloads/downloads_dom_handler_unittest.cc
+++ b/chrome/browser/ui/webui/downloads/downloads_dom_handler_unittest.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/downloads/downloads_dom_handler.h"
 
 #include <vector>
+#include <utility>
 
 #include "chrome/browser/download/download_item_model.h"
 #include "chrome/browser/ui/webui/downloads/downloads.mojom.h"
@@ -20,26 +21,26 @@
 
 namespace {
 
-class TestMdDownloadsDOMHandler : public MdDownloadsDOMHandler {
+class TestDownloadsDOMHandler : public DownloadsDOMHandler {
  public:
-  TestMdDownloadsDOMHandler(md_downloads::mojom::PagePtr page,
-                            content::DownloadManager* download_manager,
-                            content::WebUI* web_ui)
-      : MdDownloadsDOMHandler(md_downloads::mojom::PageHandlerRequest(),
-                              std::move(page),
-                              download_manager,
-                              web_ui) {}
+  TestDownloadsDOMHandler(downloads::mojom::PagePtr page,
+                          content::DownloadManager* download_manager,
+                          content::WebUI* web_ui)
+      : DownloadsDOMHandler(downloads::mojom::PageHandlerRequest(),
+                            std::move(page),
+                            download_manager,
+                            web_ui) {}
 
-  using MdDownloadsDOMHandler::FinalizeRemovals;
-  using MdDownloadsDOMHandler::RemoveDownloads;
+  using DownloadsDOMHandler::FinalizeRemovals;
+  using DownloadsDOMHandler::RemoveDownloads;
 };
 
 }  // namespace
 
-// A fixture to test MdDownloadsDOMHandler.
-class MdDownloadsDOMHandlerTest : public testing::Test {
+// A fixture to test DownloadsDOMHandler.
+class DownloadsDOMHandlerTest : public testing::Test {
  public:
-  MdDownloadsDOMHandlerTest() {}
+  DownloadsDOMHandlerTest() {}
 
   // testing::Test:
   void SetUp() override {
@@ -63,24 +64,24 @@
   content::TestWebUI web_ui_;
 };
 
-TEST_F(MdDownloadsDOMHandlerTest, ChecksForRemovedFiles) {
+TEST_F(DownloadsDOMHandlerTest, ChecksForRemovedFiles) {
   EXPECT_CALL(*manager(), CheckForHistoryFilesRemoval());
-  TestMdDownloadsDOMHandler handler(page_.BindAndGetPtr(), manager(), web_ui());
+  TestDownloadsDOMHandler handler(page_.BindAndGetPtr(), manager(), web_ui());
 
   testing::Mock::VerifyAndClear(manager());
 
   EXPECT_CALL(*manager(), CheckForHistoryFilesRemoval());
 }
 
-TEST_F(MdDownloadsDOMHandlerTest, HandleGetDownloads) {
-  TestMdDownloadsDOMHandler handler(page_.BindAndGetPtr(), manager(), web_ui());
+TEST_F(DownloadsDOMHandlerTest, HandleGetDownloads) {
+  TestDownloadsDOMHandler handler(page_.BindAndGetPtr(), manager(), web_ui());
 
   handler.GetDownloads(std::vector<std::string>());
 
   EXPECT_CALL(page_, InsertItems(0, testing::_));
 }
 
-TEST_F(MdDownloadsDOMHandlerTest, ClearAll) {
+TEST_F(DownloadsDOMHandlerTest, ClearAll) {
   std::vector<download::DownloadItem*> downloads;
 
   // Safe, in-progress items should be passed over.
@@ -109,7 +110,7 @@
 
   ASSERT_TRUE(DownloadItemModel(&completed).ShouldShowInShelf());
 
-  TestMdDownloadsDOMHandler handler(page_.BindAndGetPtr(), manager(), web_ui());
+  TestDownloadsDOMHandler handler(page_.BindAndGetPtr(), manager(), web_ui());
   handler.RemoveDownloads(downloads);
 
   // Ensure |completed| has been "soft removed" (i.e. can be revived).
diff --git a/chrome/browser/ui/webui/downloads/downloads_list_tracker.cc b/chrome/browser/ui/webui/downloads/downloads_list_tracker.cc
index e65a2dc..dee48d78 100644
--- a/chrome/browser/ui/webui/downloads/downloads_list_tracker.cc
+++ b/chrome/browser/ui/webui/downloads/downloads_list_tracker.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/downloads/downloads_list_tracker.h"
 
 #include <iterator>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -82,7 +83,7 @@
 }  // namespace
 
 DownloadsListTracker::DownloadsListTracker(DownloadManager* download_manager,
-                                           md_downloads::mojom::PagePtr page)
+                                           downloads::mojom::PagePtr page)
     : main_notifier_(download_manager, this),
       page_(std::move(page)),
       should_show_(base::BindRepeating(&DownloadsListTracker::ShouldShow,
@@ -122,7 +123,7 @@
   auto it = sorted_items_.begin();
   std::advance(it, sent_to_page_);
 
-  std::vector<md_downloads::mojom::DataPtr> list;
+  std::vector<downloads::mojom::DataPtr> list;
   while (it != sorted_items_.end() && list.size() < chunk_size_) {
     list.push_back(CreateDownloadData(*it));
     ++it;
@@ -176,7 +177,7 @@
 
 DownloadsListTracker::DownloadsListTracker(
     DownloadManager* download_manager,
-    md_downloads::mojom::PagePtr page,
+    downloads::mojom::PagePtr page,
     base::Callback<bool(const DownloadItem&)> should_show)
     : main_notifier_(download_manager, this),
       page_(std::move(page)),
@@ -185,14 +186,14 @@
   Init();
 }
 
-md_downloads::mojom::DataPtr DownloadsListTracker::CreateDownloadData(
+downloads::mojom::DataPtr DownloadsListTracker::CreateDownloadData(
     download::DownloadItem* download_item) const {
   // TODO(asanka): Move towards using download_model here for getting status and
   // progress. The difference currently only matters to Drive downloads and
   // those don't show up on the downloads page, but should.
   DownloadItemModel download_model(download_item);
 
-  auto file_value = md_downloads::mojom::Data::New();
+  auto file_value = downloads::mojom::Data::New();
 
   file_value->started =
       static_cast<int>(download_item->GetStartTime().ToTimeT());
@@ -382,7 +383,7 @@
   if (index >= chunk_size_ && index >= sent_to_page_)
     return;
 
-  std::vector<md_downloads::mojom::DataPtr> list;
+  std::vector<downloads::mojom::DataPtr> list;
   list.push_back(CreateDownloadData(*insert));
 
   page_->InsertItems(static_cast<int>(index), std::move(list));
diff --git a/chrome/browser/ui/webui/downloads/downloads_list_tracker.h b/chrome/browser/ui/webui/downloads/downloads_list_tracker.h
index 7095a71b..14e515b 100644
--- a/chrome/browser/ui/webui/downloads/downloads_list_tracker.h
+++ b/chrome/browser/ui/webui/downloads/downloads_list_tracker.h
@@ -29,7 +29,7 @@
     : public download::AllDownloadItemNotifier::Observer {
  public:
   DownloadsListTracker(content::DownloadManager* download_manager,
-                       md_downloads::mojom::PagePtr page);
+                       downloads::mojom::PagePtr page);
   ~DownloadsListTracker() override;
 
   // Clears all downloads on the page if currently sending updates and resets
@@ -62,11 +62,11 @@
  protected:
   // Testing constructor.
   DownloadsListTracker(content::DownloadManager* download_manager,
-                       md_downloads::mojom::PagePtr page,
+                       downloads::mojom::PagePtr page,
                        base::Callback<bool(const download::DownloadItem&)>);
 
   // Creates a dictionary value that's sent to the page as JSON.
-  virtual md_downloads::mojom::DataPtr CreateDownloadData(
+  virtual downloads::mojom::DataPtr CreateDownloadData(
       download::DownloadItem* item) const;
 
   // Exposed for testing.
@@ -108,7 +108,7 @@
   download::AllDownloadItemNotifier main_notifier_;
   std::unique_ptr<download::AllDownloadItemNotifier> original_notifier_;
 
-  md_downloads::mojom::PagePtr page_;
+  downloads::mojom::PagePtr page_;
 
   // Callback used to determine if an item should show on the page. Set to
   // |ShouldShow()| in default constructor, passed in while testing.
diff --git a/chrome/browser/ui/webui/downloads/downloads_list_tracker_unittest.cc b/chrome/browser/ui/webui/downloads/downloads_list_tracker_unittest.cc
index 9520ad28..e56ac40 100644
--- a/chrome/browser/ui/webui/downloads/downloads_list_tracker_unittest.cc
+++ b/chrome/browser/ui/webui/downloads/downloads_list_tracker_unittest.cc
@@ -44,7 +44,7 @@
 class TestDownloadsListTracker : public DownloadsListTracker {
  public:
   TestDownloadsListTracker(content::DownloadManager* manager,
-                           md_downloads::mojom::PagePtr page)
+                           downloads::mojom::PagePtr page)
       : DownloadsListTracker(manager,
                              std::move(page),
                              base::BindRepeating(&ShouldShowItem)) {}
@@ -55,9 +55,9 @@
   using DownloadsListTracker::SetChunkSizeForTesting;
 
  protected:
-  md_downloads::mojom::DataPtr CreateDownloadData(
+  downloads::mojom::DataPtr CreateDownloadData(
       download::DownloadItem* download_item) const override {
-    auto file_value = md_downloads::mojom::Data::New();
+    auto file_value = downloads::mojom::Data::New();
     file_value->id = base::NumberToString(download_item->GetId());
     return file_value;
   }
diff --git a/chrome/browser/ui/webui/downloads/downloads_ui.cc b/chrome/browser/ui/webui/downloads/downloads_ui.cc
index a8c1a5e..2700464d 100644
--- a/chrome/browser/ui/webui/downloads/downloads_ui.cc
+++ b/chrome/browser/ui/webui/downloads/downloads_ui.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/downloads/downloads_ui.h"
 
 #include <memory>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/memory/ref_counted_memory.h"
@@ -59,10 +60,10 @@
   source->AddLocalizedString("openDownloadsFolder",
                              IDS_DOWNLOAD_LINK_OPEN_DOWNLOADS_FOLDER);
   source->AddLocalizedString("moreActions", IDS_DOWNLOAD_MORE_ACTIONS);
-  source->AddLocalizedString("search", IDS_MD_DOWNLOAD_SEARCH);
+  source->AddLocalizedString("search", IDS_DOWNLOAD_SEARCH);
 
   // No results message that shows instead of the downloads list.
-  source->AddLocalizedString("noDownloads", IDS_MD_DOWNLOAD_NO_DOWNLOADS);
+  source->AddLocalizedString("noDownloads", IDS_DOWNLOAD_NO_DOWNLOADS);
   source->AddLocalizedString("noSearchResults", IDS_SEARCH_NO_RESULTS);
 
   // Status.
@@ -91,7 +92,7 @@
   source->AddLocalizedString("controlRemoveFromList", IDS_DOWNLOAD_LINK_REMOVE);
   source->AddLocalizedString("controlRemoveFromListAriaLabel",
                              IDS_DOWNLOAD_LINK_REMOVE_ARIA_LABEL);
-  source->AddLocalizedString("controlRetry", IDS_MD_DOWNLOAD_LINK_RETRY);
+  source->AddLocalizedString("controlRetry", IDS_DOWNLOAD_LINK_RETRY);
   source->AddLocalizedString("controlledByUrl", IDS_DOWNLOAD_BY_EXTENSION_URL);
 
   PrefService* prefs = profile->GetPrefs();
@@ -102,15 +103,15 @@
   source->AddLocalizedString("inIncognito", IDS_DOWNLOAD_IN_INCOGNITO);
 
   source->AddResourcePath("1x/incognito_marker.png",
-                          IDR_MD_DOWNLOADS_1X_INCOGNITO_MARKER_PNG);
+                          IDR_DOWNLOADS_1X_INCOGNITO_MARKER_PNG);
   source->AddResourcePath("2x/incognito_marker.png",
-                          IDR_MD_DOWNLOADS_2X_INCOGNITO_MARKER_PNG);
+                          IDR_DOWNLOADS_2X_INCOGNITO_MARKER_PNG);
   source->AddResourcePath("1x/no_downloads.png",
-                          IDR_MD_DOWNLOADS_1X_NO_DOWNLOADS_PNG);
+                          IDR_DOWNLOADS_1X_NO_DOWNLOADS_PNG);
   source->AddResourcePath("2x/no_downloads.png",
-                          IDR_MD_DOWNLOADS_2X_NO_DOWNLOADS_PNG);
+                          IDR_DOWNLOADS_2X_NO_DOWNLOADS_PNG);
   source->AddResourcePath("downloads.mojom-lite.js",
-                          IDR_MD_DOWNLOADS_MOJO_LITE_JS);
+                          IDR_DOWNLOADS_MOJO_LITE_JS);
 
 #if BUILDFLAG(OPTIMIZE_WEBUI)
   source->UseGzip(base::BindRepeating([](const std::string& path) {
@@ -119,35 +120,32 @@
            path != "downloads.mojom-lite.js";
   }));
 
-  source->AddResourcePath("crisper.js", IDR_MD_DOWNLOADS_CRISPER_JS);
+  source->AddResourcePath("crisper.js", IDR_DOWNLOADS_CRISPER_JS);
   source->SetDefaultResource(
       base::FeatureList::IsEnabled(features::kWebUIPolymer2)
-          ? IDR_MD_DOWNLOADS_VULCANIZED_P2_HTML
-          : IDR_MD_DOWNLOADS_VULCANIZED_HTML);
+          ? IDR_DOWNLOADS_VULCANIZED_P2_HTML
+          : IDR_DOWNLOADS_VULCANIZED_HTML);
 #else
   source->AddResourcePath("browser_proxy.html",
-                          IDR_MD_DOWNLOADS_BROWSER_PROXY_HTML);
-  source->AddResourcePath("browser_proxy.js",
-                          IDR_MD_DOWNLOADS_BROWSER_PROXY_JS);
-  source->AddResourcePath("constants.html", IDR_MD_DOWNLOADS_CONSTANTS_HTML);
-  source->AddResourcePath("constants.js", IDR_MD_DOWNLOADS_CONSTANTS_JS);
-  source->AddResourcePath("downloads.js", IDR_MD_DOWNLOADS_DOWNLOADS_JS);
-  source->AddResourcePath("i18n_setup.html", IDR_MD_DOWNLOADS_I18N_SETUP_HTML);
-  source->AddResourcePath("icon_loader.html",
-                          IDR_MD_DOWNLOADS_ICON_LOADER_HTML);
-  source->AddResourcePath("icon_loader.js", IDR_MD_DOWNLOADS_ICON_LOADER_JS);
-  source->AddResourcePath("icons.html", IDR_MD_DOWNLOADS_ICONS_HTML);
-  source->AddResourcePath("item.html", IDR_MD_DOWNLOADS_ITEM_HTML);
-  source->AddResourcePath("item.js", IDR_MD_DOWNLOADS_ITEM_JS);
-  source->AddResourcePath("manager.html", IDR_MD_DOWNLOADS_MANAGER_HTML);
-  source->AddResourcePath("manager.js", IDR_MD_DOWNLOADS_MANAGER_JS);
+                          IDR_DOWNLOADS_BROWSER_PROXY_HTML);
+  source->AddResourcePath("browser_proxy.js", IDR_DOWNLOADS_BROWSER_PROXY_JS);
+  source->AddResourcePath("constants.html", IDR_DOWNLOADS_CONSTANTS_HTML);
+  source->AddResourcePath("constants.js", IDR_DOWNLOADS_CONSTANTS_JS);
+  source->AddResourcePath("downloads.js", IDR_DOWNLOADS_DOWNLOADS_JS);
+  source->AddResourcePath("i18n_setup.html", IDR_DOWNLOADS_I18N_SETUP_HTML);
+  source->AddResourcePath("icon_loader.html", IDR_DOWNLOADS_ICON_LOADER_HTML);
+  source->AddResourcePath("icon_loader.js", IDR_DOWNLOADS_ICON_LOADER_JS);
+  source->AddResourcePath("icons.html", IDR_DOWNLOADS_ICONS_HTML);
+  source->AddResourcePath("item.html", IDR_DOWNLOADS_ITEM_HTML);
+  source->AddResourcePath("item.js", IDR_DOWNLOADS_ITEM_JS);
+  source->AddResourcePath("manager.html", IDR_DOWNLOADS_MANAGER_HTML);
+  source->AddResourcePath("manager.js", IDR_DOWNLOADS_MANAGER_JS);
   source->AddResourcePath("search_service.html",
-                          IDR_MD_DOWNLOADS_SEARCH_SERVICE_HTML);
-  source->AddResourcePath("search_service.js",
-                          IDR_MD_DOWNLOADS_SEARCH_SERVICE_JS);
-  source->AddResourcePath("toolbar.html", IDR_MD_DOWNLOADS_TOOLBAR_HTML);
-  source->AddResourcePath("toolbar.js", IDR_MD_DOWNLOADS_TOOLBAR_JS);
-  source->SetDefaultResource(IDR_MD_DOWNLOADS_DOWNLOADS_HTML);
+                          IDR_DOWNLOADS_SEARCH_SERVICE_HTML);
+  source->AddResourcePath("search_service.js", IDR_DOWNLOADS_SEARCH_SERVICE_JS);
+  source->AddResourcePath("toolbar.html", IDR_DOWNLOADS_TOOLBAR_HTML);
+  source->AddResourcePath("toolbar.js", IDR_DOWNLOADS_TOOLBAR_JS);
+  source->SetDefaultResource(IDR_DOWNLOADS_DOWNLOADS_HTML);
 #endif
 
   source->SetJsonPath("strings.js");
@@ -159,11 +157,11 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 //
-// MdDownloadsUI
+// DownloadsUI
 //
 ///////////////////////////////////////////////////////////////////////////////
 
-MdDownloadsUI::MdDownloadsUI(content::WebUI* web_ui)
+DownloadsUI::DownloadsUI(content::WebUI* web_ui)
     : ui::MojoWebUIController(web_ui, true), page_factory_binding_(this) {
   Profile* profile = Profile::FromWebUI(web_ui);
   web_ui->AddMessageHandler(std::make_unique<MetricsHandler>());
@@ -175,34 +173,34 @@
   content::WebUIDataSource::Add(profile, source);
   content::URLDataSource::Add(profile, std::make_unique<ThemeSource>(profile));
 
-  AddHandlerToRegistry(base::BindRepeating(
-      &MdDownloadsUI::BindPageHandlerFactory, base::Unretained(this)));
+  AddHandlerToRegistry(base::BindRepeating(&DownloadsUI::BindPageHandlerFactory,
+                                           base::Unretained(this)));
 }
 
-MdDownloadsUI::~MdDownloadsUI() = default;
+DownloadsUI::~DownloadsUI() = default;
 
 // static
-base::RefCountedMemory* MdDownloadsUI::GetFaviconResourceBytes(
+base::RefCountedMemory* DownloadsUI::GetFaviconResourceBytes(
     ui::ScaleFactor scale_factor) {
   return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytesForScale(
       IDR_DOWNLOADS_FAVICON, scale_factor);
 }
 
-void MdDownloadsUI::BindPageHandlerFactory(
-    md_downloads::mojom::PageHandlerFactoryRequest request) {
+void DownloadsUI::BindPageHandlerFactory(
+    downloads::mojom::PageHandlerFactoryRequest request) {
   if (page_factory_binding_.is_bound())
     page_factory_binding_.Unbind();
 
   page_factory_binding_.Bind(std::move(request));
 }
 
-void MdDownloadsUI::CreatePageHandler(
-    md_downloads::mojom::PagePtr page,
-    md_downloads::mojom::PageHandlerRequest request) {
+void DownloadsUI::CreatePageHandler(
+    downloads::mojom::PagePtr page,
+    downloads::mojom::PageHandlerRequest request) {
   DCHECK(page);
   Profile* profile = Profile::FromWebUI(web_ui());
   DownloadManager* dlm = BrowserContext::GetDownloadManager(profile);
 
-  page_handler_.reset(new MdDownloadsDOMHandler(
-      std::move(request), std::move(page), dlm, web_ui()));
+  page_handler_.reset(new DownloadsDOMHandler(std::move(request),
+                                              std::move(page), dlm, web_ui()));
 }
diff --git a/chrome/browser/ui/webui/downloads/downloads_ui.h b/chrome/browser/ui/webui/downloads/downloads_ui.h
index 246edc96..82f7daf 100644
--- a/chrome/browser/ui/webui/downloads/downloads_ui.h
+++ b/chrome/browser/ui/webui/downloads/downloads_ui.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_DOWNLOADS_DOWNLOADS_UI_H_
 #define CHROME_BROWSER_UI_WEBUI_DOWNLOADS_DOWNLOADS_UI_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "chrome/browser/ui/webui/downloads/downloads.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -15,31 +17,30 @@
 class RefCountedMemory;
 }
 
-class MdDownloadsDOMHandler;
+class DownloadsDOMHandler;
 
-class MdDownloadsUI : public ui::MojoWebUIController,
-                      public md_downloads::mojom::PageHandlerFactory {
+class DownloadsUI : public ui::MojoWebUIController,
+                    public downloads::mojom::PageHandlerFactory {
  public:
-  explicit MdDownloadsUI(content::WebUI* web_ui);
-  ~MdDownloadsUI() override;
+  explicit DownloadsUI(content::WebUI* web_ui);
+  ~DownloadsUI() override;
 
   static base::RefCountedMemory* GetFaviconResourceBytes(
       ui::ScaleFactor scale_factor);
 
  private:
   void BindPageHandlerFactory(
-      md_downloads::mojom::PageHandlerFactoryRequest request);
+      downloads::mojom::PageHandlerFactoryRequest request);
 
-  // md_downloads::mojom::PageHandlerFactory:
-  void CreatePageHandler(
-      md_downloads::mojom::PagePtr page,
-      md_downloads::mojom::PageHandlerRequest request) override;
+  // downloads::mojom::PageHandlerFactory:
+  void CreatePageHandler(downloads::mojom::PagePtr page,
+                         downloads::mojom::PageHandlerRequest request) override;
 
-  std::unique_ptr<MdDownloadsDOMHandler> page_handler_;
+  std::unique_ptr<DownloadsDOMHandler> page_handler_;
 
-  mojo::Binding<md_downloads::mojom::PageHandlerFactory> page_factory_binding_;
+  mojo::Binding<downloads::mojom::PageHandlerFactory> page_factory_binding_;
 
-  DISALLOW_COPY_AND_ASSIGN(MdDownloadsUI);
+  DISALLOW_COPY_AND_ASSIGN(DownloadsUI);
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_DOWNLOADS_DOWNLOADS_UI_H_
diff --git a/chrome/browser/ui/webui/downloads/mock_downloads_page.cc b/chrome/browser/ui/webui/downloads/mock_downloads_page.cc
index 3fb8e61..e6f21212 100644
--- a/chrome/browser/ui/webui/downloads/mock_downloads_page.cc
+++ b/chrome/browser/ui/webui/downloads/mock_downloads_page.cc
@@ -7,9 +7,9 @@
 MockPage::MockPage() : binding_(this) {}
 MockPage::~MockPage() = default;
 
-md_downloads::mojom::PagePtr MockPage::BindAndGetPtr() {
+downloads::mojom::PagePtr MockPage::BindAndGetPtr() {
   DCHECK(!binding_.is_bound());
-  md_downloads::mojom::PagePtr page;
+  downloads::mojom::PagePtr page;
   binding_.Bind(mojo::MakeRequest(&page));
   return page;
 }
diff --git a/chrome/browser/ui/webui/downloads/mock_downloads_page.h b/chrome/browser/ui/webui/downloads/mock_downloads_page.h
index b0704626..0fdaccde 100644
--- a/chrome/browser/ui/webui/downloads/mock_downloads_page.h
+++ b/chrome/browser/ui/webui/downloads/mock_downloads_page.h
@@ -5,25 +5,26 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_DOWNLOADS_MOCK_DOWNLOADS_PAGE_H_
 #define CHROME_BROWSER_UI_WEBUI_DOWNLOADS_MOCK_DOWNLOADS_PAGE_H_
 
+#include <vector>
+
 #include "chrome/browser/ui/webui/downloads/downloads.mojom.h"
 
 #include "mojo/public/cpp/bindings/binding.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-class MockPage : public md_downloads::mojom::Page {
+class MockPage : public downloads::mojom::Page {
  public:
   MockPage();
   ~MockPage() override;
 
-  md_downloads::mojom::PagePtr BindAndGetPtr();
+  downloads::mojom::PagePtr BindAndGetPtr();
 
   MOCK_METHOD1(RemoveItem, void(int));
-  MOCK_METHOD2(UpdateItem, void(int, md_downloads::mojom::DataPtr));
-  MOCK_METHOD2(InsertItems,
-               void(int, std::vector<md_downloads::mojom::DataPtr>));
+  MOCK_METHOD2(UpdateItem, void(int, downloads::mojom::DataPtr));
+  MOCK_METHOD2(InsertItems, void(int, std::vector<downloads::mojom::DataPtr>));
   MOCK_METHOD0(ClearAll, void());
 
-  mojo::Binding<md_downloads::mojom::Page> binding_;
+  mojo::Binding<downloads::mojom::Page> binding_;
 };
 
 #endif  // CHROME_BROWSER_UI_WEBUI_DOWNLOADS_MOCK_DOWNLOADS_PAGE_H_
diff --git a/chrome/browser/ui/webui/identity_internals_ui_browsertest.cc b/chrome/browser/ui/webui/identity_internals_ui_browsertest.cc
index 82f47b9..ac4a25d 100644
--- a/chrome/browser/ui/webui/identity_internals_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/identity_internals_ui_browsertest.cc
@@ -23,7 +23,7 @@
 
 void IdentityInternalsUIBrowserTest::SetupTokenCache(int number_of_tokens) {
   for (int number = 0; number < number_of_tokens; ++number) {
-    const std::string token_number = base::IntToString(number);
+    const std::string token_number = base::NumberToString(number);
     std::string token_id("token");
     token_id += token_number;
     std::string extension_id("extension");
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
index b75431d..f2280cc 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui.cc
@@ -24,9 +24,11 @@
 #include "chrome/common/url_constants.h"
 #include "components/grit/components_resources.h"
 #include "components/safe_browsing/db/database_manager.h"
+#include "components/security_interstitials/content/origin_policy_ui.h"
 #include "components/security_interstitials/core/ssl_error_ui.h"
 #include "components/supervised_user_error_page/supervised_user_error_page.h"
 #include "content/public/browser/interstitial_page_delegate.h"
+#include "content/public/browser/origin_policy_error_reason.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/url_data_source.h"
@@ -404,6 +406,13 @@
 }
 #endif
 
+security_interstitials::SecurityInterstitialPage*
+CreateOriginPolicyInterstitialPage(content::WebContents* web_contents) {
+  return security_interstitials::OriginPolicyUI::GetBlockingPage(
+      content::OriginPolicyErrorReason::kCannotLoadPolicy, web_contents,
+      GURL("https://example.com/broken/origin/policy"));
+}
+
 }  //  namespace
 
 InterstitialUI::InterstitialUI(content::WebUI* web_ui)
@@ -468,6 +477,9 @@
   } else if (path_without_query == "/captiveportal") {
     interstitial_delegate.reset(CreateCaptivePortalBlockingPage(web_contents));
 #endif
+  } else if (path_without_query == "/origin_policy") {
+    interstitial_delegate.reset(
+        CreateOriginPolicyInterstitialPage(web_contents));
   }
 
   if (path_without_query == "/supervised_user") {
diff --git a/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc b/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
index 95d3d4a..de85f80 100644
--- a/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/interstitials/interstitial_ui_browsertest.cc
@@ -180,6 +180,12 @@
                    "Connect to Wi-Fi");
 }
 
+IN_PROC_BROWSER_TEST_F(InterstitialUITest, OriginPolicyErrorInterstitial) {
+  TestInterstitial(GURL("chrome://interstitials/origin_policy"),
+                   "Origin Policy Error",
+                   base::ASCIIToUTF16("has requested that a security policy"));
+}
+
 // Tests that back button works after opening an interstitial from
 // chrome://interstitials.
 IN_PROC_BROWSER_TEST_F(InterstitialUITest, InterstitialBackButton) {
diff --git a/chrome/browser/ui/webui/nacl_ui.cc b/chrome/browser/ui/webui/nacl_ui.cc
index 4545cb1..78babd6 100644
--- a/chrome/browser/ui/webui/nacl_ui.cc
+++ b/chrome/browser/ui/webui/nacl_ui.cc
@@ -194,9 +194,9 @@
     case base::win::VERSION_WIN8: os_label += " 8 or Server 2012"; break;
     default:  os_label += " UNKNOWN"; break;
   }
-  os_label += " SP" + base::IntToString(os->service_pack().major);
+  os_label += " SP" + base::NumberToString(os->service_pack().major);
   if (os->service_pack().minor > 0)
-    os_label += "." + base::IntToString(os->service_pack().minor);
+    os_label += "." + base::NumberToString(os->service_pack().minor);
   if (os->architecture() == base::win::OSInfo::X64_ARCHITECTURE)
     os_label += " 64 bit";
 #endif
diff --git a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
index 8d62019b..605023b 100644
--- a/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
+++ b/chrome/browser/ui/webui/ntp/ntp_resource_cache.cc
@@ -113,10 +113,10 @@
     int offset = GetLayoutConstant(BOOKMARK_BAR_NTP_HEIGHT);
 
     if (alignment & ThemeProperties::ALIGN_LEFT)
-      return "left " + base::IntToString(-offset) + "px";
+      return "left " + base::NumberToString(-offset) + "px";
     else if (alignment & ThemeProperties::ALIGN_RIGHT)
-      return "right " + base::IntToString(-offset) + "px";
-    return "center " + base::IntToString(-offset) + "px";
+      return "right " + base::NumberToString(-offset) + "px";
+    return "center " + base::NumberToString(-offset) + "px";
   }
 
   return ThemeProperties::AlignmentToString(alignment);
diff --git a/chrome/browser/ui/webui/policy_tool_ui_browsertest.cc b/chrome/browser/ui/webui/policy_tool_ui_browsertest.cc
index 46184041..6ad0ddf 100644
--- a/chrome/browser/ui/webui/policy_tool_ui_browsertest.cc
+++ b/chrome/browser/ui/webui/policy_tool_ui_browsertest.cc
@@ -281,9 +281,9 @@
   base::Time initial_time = base::Time::Now();
   for (int i = 0; i < count; ++i) {
     contents.SetPath({"chromePolicies", "SessionId", "value"},
-                     base::Value(base::IntToString(i)));
+                     base::Value(base::NumberToString(i)));
     base::FilePath::StringType session_name =
-        base::FilePath::FromUTF8Unsafe(base::IntToString(i)).value();
+        base::FilePath::FromUTF8Unsafe(base::NumberToString(i)).value();
     std::string stringified_contents;
     base::JSONWriter::Write(contents, &stringified_contents);
     base::WriteFile(GetSessionPath(session_name), stringified_contents.c_str(),
@@ -642,7 +642,7 @@
   std::unique_ptr<base::ListValue> sessions = ExtractSessionsList();
   base::ListValue expected;
   for (int i = 4; i >= 0; --i) {
-    expected.GetList().push_back(base::Value(base::IntToString(i)));
+    expected.GetList().push_back(base::Value(base::NumberToString(i)));
   }
   EXPECT_EQ(expected, *sessions);
 }
diff --git a/chrome/browser/ui/webui/print_preview/cloud_printer_handler.cc b/chrome/browser/ui/webui/print_preview/cloud_printer_handler.cc
index b2625bb6..3a54ee70 100644
--- a/chrome/browser/ui/webui/print_preview/cloud_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/cloud_printer_handler.cc
@@ -32,11 +32,8 @@
 }
 
 void CloudPrinterHandler::StartPrint(
-    const std::string& destination_id,
-    const std::string& capability,
     const base::string16& job_title,
-    base::Value ticket_json,
-    const gfx::Size& page_size,
+    base::Value settings,
     scoped_refptr<base::RefCountedMemory> print_data,
     PrintCallback callback) {
   // TODO(https://crbug.com/829414): Print to cloud print
diff --git a/chrome/browser/ui/webui/print_preview/cloud_printer_handler.h b/chrome/browser/ui/webui/print_preview/cloud_printer_handler.h
index 54fe7c9..f6c198f 100644
--- a/chrome/browser/ui/webui/print_preview/cloud_printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/cloud_printer_handler.h
@@ -26,11 +26,8 @@
                         GetPrintersDoneCallback done_callback) override;
   void StartGetCapability(const std::string& destination_id,
                           GetCapabilityCallback calback) override;
-  void StartPrint(const std::string& destination_id,
-                  const std::string& capability,
-                  const base::string16& job_title,
-                  base::Value ticket,
-                  const gfx::Size& page_size,
+  void StartPrint(const base::string16& job_title,
+                  base::Value settings,
                   scoped_refptr<base::RefCountedMemory> print_data,
                   PrintCallback callback) override;
 
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
index 60d291c..af3fba9e 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.cc
@@ -162,20 +162,22 @@
 }
 
 void ExtensionPrinterHandler::StartPrint(
-    const std::string& destination_id,
-    const std::string& capability,
     const base::string16& job_title,
-    base::Value ticket_value,
-    const gfx::Size& page_size,
+    base::Value settings,
     scoped_refptr<base::RefCountedMemory> print_data,
     PrintCallback callback) {
   auto print_job = std::make_unique<extensions::PrinterProviderPrintJob>();
-  print_job->printer_id = destination_id;
   print_job->job_title = job_title;
-  print_job->ticket = std::move(ticket_value);
+  std::string capabilities;
+  gfx::Size page_size;
+  if (!ParseSettings(settings, &print_job->printer_id, &capabilities,
+                     &page_size, &print_job->ticket)) {
+    std::move(callback).Run(base::Value("Invalid settings"));
+    return;
+  }
 
   cloud_devices::CloudDeviceDescription printer_description;
-  printer_description.InitFromString(capability);
+  printer_description.InitFromString(capabilities);
 
   cloud_devices::printer::ContentTypesCapability content_types;
   content_types.LoadFrom(printer_description);
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler.h b/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
index c707406..6cceb78 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler.h
@@ -56,11 +56,8 @@
                         GetPrintersDoneCallback done_callback) override;
   void StartGetCapability(const std::string& destination_id,
                           GetCapabilityCallback callback) override;
-  void StartPrint(const std::string& destination_id,
-                  const std::string& capability,
-                  const base::string16& job_title,
-                  base::Value ticket,
-                  const gfx::Size& page_size,
+  void StartPrint(const base::string16& job_title,
+                  base::Value settings,
                   scoped_refptr<base::RefCountedMemory> print_data,
                   PrintCallback callback) override;
   void StartGrantPrinterAccess(const std::string& printer_id,
diff --git a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
index bda0117d..d84c91a 100644
--- a/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/extension_printer_handler_unittest.cc
@@ -72,29 +72,6 @@
     "  \"description\": \"Test printer 2\""
     "}]";
 
-// Printer capability for printer that supports all content types.
-const char kAllContentTypesSupportedPrinter[] =
-    "{"
-    "  \"version\": \"1.0\","
-    "  \"printer\": {"
-    "    \"supported_content_type\": ["
-    "      {\"content_type\": \"*/*\"}"
-    "    ]"
-    "  }"
-    "}";
-
-// Printer capability for a printer that supports PDF.
-const char kPdfSupportedPrinter[] =
-    "{"
-    "  \"version\": \"1.0\","
-    "  \"printer\": {"
-    "    \"supported_content_type\": ["
-    "      {\"content_type\": \"application/pdf\"},"
-    "      {\"content_type\": \"image/pwg-raster\"}"
-    "    ]"
-    "  }"
-    "}";
-
 // Printer capability for a printer that supportd only PWG raster.
 const char kPWGRasterOnlyPrinterSimpleDescription[] =
     "{"
@@ -106,30 +83,6 @@
     "  }"
     "}";
 
-// Printer capability for a printer that supportd only PWG raster that has
-// options other that supported_content_type set.
-const char kPWGRasterOnlyPrinter[] =
-    "{"
-    "  \"version\": \"1.0\","
-    "  \"printer\": {"
-    "    \"supported_content_type\": ["
-    "      {\"content_type\": \"image/pwg-raster\"}"
-    "    ],"
-    "    \"pwg_raster_config\": {"
-    "      \"document_sheet_back\": \"FLIPPED\","
-    "      \"reverse_order_streaming\": true,"
-    "      \"rotate_all_pages\": true"
-    "    },"
-    "    \"dpi\": {"
-    "      \"option\": [{"
-    "        \"horizontal_dpi\": 100,"
-    "        \"vertical_dpi\": 200,"
-    "        \"is_default\": true"
-    "      }]"
-    "    }"
-    "  }"
-    "}";
-
 // Print ticket with no parameters set.
 const char kEmptyPrintTicket[] = "{\"version\": \"1.0\"}";
 
@@ -186,6 +139,99 @@
     "  }"
     "}";
 
+const char kPdfSettings[] = R"({
+  "deviceName": "printer_id",
+  "capabilities": "{
+      \"version\": \"1.0\",
+      \"printer\": {
+        \"supported_content_type\": [
+          {\"content_type\": \"application/pdf\"},
+          {\"content_type\": \"image/pwg-raster\"}
+        ]
+      }
+    }",
+  "ticket": "{\"version\": \"1.0\"}",
+  "pageWidth": 100,
+  "pageHeight": 50
+})";
+
+const char kAllTypesSettings[] = R"({
+  "deviceName": "printer_id",
+  "capabilities": "{
+      \"version\": \"1.0\",
+      \"printer\": {
+        \"supported_content_type\": [
+          {\"content_type\": \"*/*\"}
+        ]
+      }
+    }",
+  "ticket": "{\"version\": \"1.0\"}",
+  "pageWidth": 100,
+  "pageHeight": 50
+})";
+
+const char kSimpleRasterSettings[] = R"({
+  "deviceName": "printer_id",
+  "capabilities": "{
+      \"version\": \"1.0\",
+      \"printer\": {
+        \"supported_content_type\": [
+          {\"content_type\": \"image/pwg-raster\"}
+        ]
+      }
+    }",
+  "ticket": "{\"version\": \"1.0\"}",
+  "pageWidth": 100,
+  "pageHeight": 50
+})";
+
+const char kInvalidSettings[] = R"({
+  "deviceName": "printer_id",
+  "capabilities": "{
+      \"version\": \"1.0\",
+      \"printer\": {
+        \"supported_content_type\": [
+          {\"content_type\": \"image/pwg-raster\"}
+        ]
+      }
+    }",
+  "ticket": "{}",
+  "pageWidth": 100,
+  "pageHeight": 50
+})";
+
+const char kDuplexSettings[] = R"({
+  "deviceName": "printer_id",
+  "capabilities": "{
+      \"version\": \"1.0\",
+      \"printer\": {
+        \"supported_content_type\": [
+          {\"content_type\": \"image/pwg-raster\"}
+        ],
+        \"pwg_raster_config\": {
+          \"document_sheet_back\": \"FLIPPED\",
+          \"reverse_order_streaming\": true,
+          \"rotate_all_pages\": true
+        },
+        \"dpi\": {
+          \"option\": [{
+            \"horizontal_dpi\": 100,
+            \"vertical_dpi\": 200,
+            \"is_default\": true
+          }]
+        }
+      }
+    }",
+  "ticket": "{
+      \"version\": \"1.0\",
+      \"print\": {
+        \"duplex\": {\"type\": \"LONG_EDGE\"}
+      }
+    }",
+  "pageWidth": 100,
+  "pageHeight": 50
+})";
+
 const char kContentTypePDF[] = "application/pdf";
 const char kContentTypePWG[] = "image/pwg-raster";
 
@@ -674,8 +720,7 @@
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPdfSupportedPrinter, title,
-      GetJsonAsValue(kEmptyPrintTicket), gfx::Size(100, 100), print_data,
+      title, GetJsonAsValue(kPdfSettings), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -711,8 +756,7 @@
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPdfSupportedPrinter, title,
-      GetJsonAsValue(kEmptyPrintTicket), gfx::Size(100, 100), print_data,
+      title, GetJsonAsValue(kPdfSettings), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -737,8 +781,7 @@
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kAllContentTypesSupportedPrinter, title,
-      GetJsonAsValue(kEmptyPrintTicket), gfx::Size(100, 100), print_data,
+      title, GetJsonAsValue(kAllTypesSettings), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -775,8 +818,7 @@
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
-      GetJsonAsValue(kEmptyPrintTicket), gfx::Size(100, 50), print_data,
+      title, GetJsonAsValue(kSimpleRasterSettings), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -829,8 +871,7 @@
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinter, title,
-      GetJsonAsValue(kPrintTicketWithDuplex), gfx::Size(100, 50), print_data,
+      title, GetJsonAsValue(kDuplexSettings), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -883,8 +924,7 @@
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
-      GetJsonAsValue(kEmptyPrintTicket), gfx::Size(100, 50), print_data,
+      title, GetJsonAsValue(kSimpleRasterSettings), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(0u, call_count);
@@ -912,9 +952,7 @@
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
-      base::Value(base::Value::Type::DICTIONARY) /* ticket */,
-      gfx::Size(100, 100), print_data,
+      title, GetJsonAsValue(kInvalidSettings), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(1u, call_count);
@@ -935,8 +973,7 @@
   base::string16 title = base::ASCIIToUTF16("Title");
 
   extension_printer_handler_->StartPrint(
-      kPrinterId, kPWGRasterOnlyPrinterSimpleDescription, title,
-      GetJsonAsValue(kEmptyPrintTicket), gfx::Size(100, 100), print_data,
+      title, GetJsonAsValue(kSimpleRasterSettings), print_data,
       base::Bind(&RecordPrintResult, &call_count, &success, &status));
 
   EXPECT_EQ(1u, call_count);
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
index a8479bc..a7ced7c 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.cc
@@ -232,23 +232,21 @@
 }
 
 void LocalPrinterHandlerChromeos::StartPrint(
-    const std::string& destination_id,
-    const std::string& capability,
     const base::string16& job_title,
-    base::Value ticket,
-    const gfx::Size& page_size,
+    base::Value settings,
     scoped_refptr<base::RefCountedMemory> print_data,
     PrintCallback callback) {
   size_t size_in_kb = print_data->size() / 1024;
   UMA_HISTOGRAM_MEMORY_KB("Printing.CUPS.PrintDocumentSize", size_in_kb);
   if (profile_->GetPrefs()->GetBoolean(
           prefs::kPrintingSendUsernameAndFilenameEnabled)) {
-    ticket.SetKey(kSettingUsername, base::Value(chromeos::ProfileHelper::Get()
-                                                    ->GetUserByProfile(profile_)
-                                                    ->display_email()));
-    ticket.SetKey(kSettingSendUserInfo, base::Value(true));
+    settings.SetKey(kSettingUsername,
+                    base::Value(chromeos::ProfileHelper::Get()
+                                    ->GetUserByProfile(profile_)
+                                    ->display_email()));
+    settings.SetKey(kSettingSendUserInfo, base::Value(true));
   }
-  StartLocalPrint(std::move(ticket), std::move(print_data),
+  StartLocalPrint(std::move(settings), std::move(print_data),
                   preview_web_contents_, std::move(callback));
 }
 
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h
index 9d64bfc..de085a0d 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_chromeos.h
@@ -38,11 +38,8 @@
                         GetPrintersDoneCallback done_callback) override;
   void StartGetCapability(const std::string& printer_name,
                           GetCapabilityCallback cb) override;
-  void StartPrint(const std::string& destination_id,
-                  const std::string& capability,
-                  const base::string16& job_title,
-                  base::Value ticket,
-                  const gfx::Size& page_size,
+  void StartPrint(const base::string16& job_title,
+                  base::Value settings,
                   scoped_refptr<base::RefCountedMemory> print_data,
                   PrintCallback callback) override;
 
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc b/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc
index b6d3202..68b425f 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc
@@ -119,14 +119,11 @@
 }
 
 void LocalPrinterHandlerDefault::StartPrint(
-    const std::string& destination_id,
-    const std::string& capability,
     const base::string16& job_title,
-    base::Value ticket,
-    const gfx::Size& page_size,
+    base::Value settings,
     scoped_refptr<base::RefCountedMemory> print_data,
     PrintCallback callback) {
-  StartLocalPrint(std::move(ticket), std::move(print_data),
+  StartLocalPrint(std::move(settings), std::move(print_data),
                   preview_web_contents_, std::move(callback));
 }
 
diff --git a/chrome/browser/ui/webui/print_preview/local_printer_handler_default.h b/chrome/browser/ui/webui/print_preview/local_printer_handler_default.h
index 41099509c..085c5ad 100644
--- a/chrome/browser/ui/webui/print_preview/local_printer_handler_default.h
+++ b/chrome/browser/ui/webui/print_preview/local_printer_handler_default.h
@@ -33,11 +33,8 @@
                         GetPrintersDoneCallback done_callback) override;
   void StartGetCapability(const std::string& destination_id,
                           GetCapabilityCallback callback) override;
-  void StartPrint(const std::string& destination_id,
-                  const std::string& capability,
-                  const base::string16& job_title,
-                  base::Value ticket,
-                  const gfx::Size& page_size,
+  void StartPrint(const base::string16& job_title,
+                  base::Value settings,
                   scoped_refptr<base::RefCountedMemory> print_data,
                   PrintCallback callback) override;
 
diff --git a/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc b/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
index 4566be07..ab7ba485 100644
--- a/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/pdf_printer_handler.cc
@@ -84,7 +84,7 @@
   ColorCapability color;
   {
     Color standard_color(STANDARD_COLOR);
-    standard_color.vendor_id = base::IntToString(COLOR);
+    standard_color.vendor_id = base::NumberToString(COLOR);
     color.AddDefaultOption(standard_color, true);
   }
   color.SaveTo(&description);
@@ -179,11 +179,8 @@
 }
 
 void PdfPrinterHandler::StartPrint(
-    const std::string& destination_id,
-    const std::string& capability,
     const base::string16& job_title,
-    base::Value ticket,
-    const gfx::Size& page_size,
+    base::Value settings,
     scoped_refptr<base::RefCountedMemory> print_data,
     PrintCallback callback) {
   print_data_ = print_data;
diff --git a/chrome/browser/ui/webui/print_preview/pdf_printer_handler.h b/chrome/browser/ui/webui/print_preview/pdf_printer_handler.h
index 9c72ab4..eae9098 100644
--- a/chrome/browser/ui/webui/print_preview/pdf_printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/pdf_printer_handler.h
@@ -23,10 +23,6 @@
 class WebContents;
 }
 
-namespace gfx {
-class Size;
-}
-
 namespace printing {
 class StickySettings;
 }
@@ -52,11 +48,8 @@
                         GetPrintersDoneCallback done_callback) override;
   void StartGetCapability(const std::string& destination_id,
                           GetCapabilityCallback callback) override;
-  void StartPrint(const std::string& destination_id,
-                  const std::string& capability,
-                  const base::string16& job_title,
-                  base::Value ticket_json,
-                  const gfx::Size& page_size,
+  void StartPrint(const base::string16& job_title,
+                  base::Value settings,
                   scoped_refptr<base::RefCountedMemory> print_data,
                   PrintCallback callback) override;
 
diff --git a/chrome/browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc b/chrome/browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc
index aea4d04..a43f6b7c 100644
--- a/chrome/browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/pdf_printer_handler_win_unittest.cc
@@ -61,8 +61,7 @@
   }
 
   void StartPrintToPdf(const base::string16& job_title) {
-    StartPrint("", "", job_title, base::Value(), gfx::Size(), nullptr,
-               base::DoNothing());
+    StartPrint(job_title, base::Value(), nullptr, base::DoNothing());
     run_loop_.Run();
   }
 
diff --git a/chrome/browser/ui/webui/print_preview/policy_settings.cc b/chrome/browser/ui/webui/print_preview/policy_settings.cc
index 9f979900..5d1912733 100644
--- a/chrome/browser/ui/webui/print_preview/policy_settings.cc
+++ b/chrome/browser/ui/webui/print_preview/policy_settings.cc
@@ -20,8 +20,6 @@
   registry->RegisterIntegerPref(prefs::kPrintingColorDefault, 0);
   registry->RegisterIntegerPref(prefs::kPrintingDuplexDefault, 0);
   registry->RegisterDictionaryPref(prefs::kPrintingSizeDefault);
-  registry->RegisterBooleanPref(prefs::kPrintingSendUsernameAndFilenameEnabled,
-                                false);
 #endif
 }
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 77b0194..042fed5 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -279,32 +279,26 @@
 // managed by an enterprise policy.
 const char kIsHeaderFooterManaged[] = "isHeaderFooterManaged";
 
-// Get the print job settings dictionary from |json_str|. Returns NULL on
-// failure.
-std::unique_ptr<base::DictionaryValue> GetSettingsDictionary(
-    const std::string& json_str) {
-  if (json_str.empty()) {
-    NOTREACHED() << "Empty print job settings";
-    return NULL;
-  }
-  std::unique_ptr<base::DictionaryValue> settings =
-      base::DictionaryValue::From(base::JSONReader::Read(json_str));
-  if (!settings) {
+// Get the print job settings dictionary from |json_str|.
+// Returns |base::Value()| on failure.
+base::Value GetSettingsDictionary(const std::string& json_str) {
+  std::unique_ptr<base::Value> settings = base::JSONReader::Read(json_str);
+  if (!settings->is_dict()) {
     NOTREACHED() << "Print job settings must be a dictionary.";
-    return NULL;
+    return base::Value();
   }
 
-  if (settings->empty()) {
+  if (settings->DictEmpty()) {
     NOTREACHED() << "Print job settings dictionary is empty";
-    return NULL;
+    return base::Value();
   }
 
-  return settings;
+  return base::Value::FromUniquePtrValue(std::move(settings));
 }
 
 // Track the popularity of print settings and report the stats.
-void ReportPrintSettingsStats(const base::DictionaryValue& print_settings,
-                              const base::DictionaryValue& preview_settings,
+void ReportPrintSettingsStats(const base::Value& print_settings,
+                              const base::Value& preview_settings,
                               bool is_pdf) {
   ReportPrintSettingHistogram(TOTAL);
 
@@ -317,134 +311,106 @@
   // print ticket. Similarly, settings applied at the printer should be pulled
   // from the print ticket, as they may have dummy values in the preview
   // request.
-  const base::ListValue* page_range_array = NULL;
-  if (preview_settings.GetList(kSettingPageRange, &page_range_array) &&
-      !page_range_array->empty()) {
+  const base::Value* page_range_array =
+      preview_settings.FindKey(kSettingPageRange);
+  if (page_range_array && page_range_array->is_list() &&
+      !page_range_array->GetList().empty()) {
     ReportPrintSettingHistogram(PAGE_RANGE);
   }
 
-  const base::DictionaryValue* media_size_value = NULL;
-  if (preview_settings.GetDictionary(kSettingMediaSize, &media_size_value) &&
-      !media_size_value->empty()) {
-    bool is_default = false;
-    if (media_size_value->GetBoolean(kSettingMediaSizeIsDefault, &is_default) &&
-        is_default) {
+  const base::Value* media_size_value =
+      preview_settings.FindKey(kSettingMediaSize);
+  if (media_size_value && media_size_value->is_dict() &&
+      !media_size_value->DictEmpty()) {
+    if (media_size_value->FindBoolKey(kSettingMediaSizeIsDefault)
+            .value_or(false)) {
       ReportPrintSettingHistogram(DEFAULT_MEDIA);
     } else {
       ReportPrintSettingHistogram(NON_DEFAULT_MEDIA);
     }
   }
 
-  bool landscape = false;
-  if (preview_settings.GetBoolean(kSettingLandscape, &landscape))
-    ReportPrintSettingHistogram(landscape ? LANDSCAPE : PORTRAIT);
+  base::Optional<bool> landscape_opt =
+      preview_settings.FindBoolKey(kSettingLandscape);
+  if (landscape_opt)
+    ReportPrintSettingHistogram(landscape_opt.value() ? LANDSCAPE : PORTRAIT);
 
-  int copies = 1;
-  if (print_settings.GetInteger(kSettingCopies, &copies) && copies > 1) {
+  if (print_settings.FindIntKey(kSettingCopies).value_or(1) > 1)
     ReportPrintSettingHistogram(COPIES);
-  }
 
-  int scaling = 100;
-  if (preview_settings.GetInteger(kSettingScaleFactor, &scaling) &&
-      scaling != 100) {
+  if (preview_settings.FindIntKey(kSettingScaleFactor).value_or(100) != 100)
     ReportPrintSettingHistogram(SCALING);
-  }
 
-  int pages_per_sheet = 1;
-  if (preview_settings.GetInteger(kSettingPagesPerSheet, &pages_per_sheet) &&
-      pages_per_sheet != 1) {
+  if (preview_settings.FindIntKey(kSettingPagesPerSheet).value_or(1) != 1)
     ReportPrintSettingHistogram(PAGES_PER_SHEET);
-  }
 
-  bool collate = false;
-  if (print_settings.GetBoolean(kSettingCollate, &collate) && collate)
+  if (print_settings.FindBoolKey(kSettingCollate).value_or(false))
     ReportPrintSettingHistogram(COLLATE);
 
-  int duplex_mode = 0;
-  if (print_settings.GetInteger(kSettingDuplexMode, &duplex_mode))
-    ReportPrintSettingHistogram(duplex_mode ? DUPLEX : SIMPLEX);
+  base::Optional<int> duplex_mode_opt =
+      print_settings.FindIntKey(kSettingDuplexMode);
+  if (duplex_mode_opt)
+    ReportPrintSettingHistogram(duplex_mode_opt.value() ? DUPLEX : SIMPLEX);
 
-  int color_mode = 0;
-  if (print_settings.GetInteger(kSettingColor, &color_mode)) {
+  base::Optional<int> color_mode_opt = print_settings.FindIntKey(kSettingColor);
+  if (color_mode_opt) {
     ReportPrintSettingHistogram(
-        IsColorModelSelected(color_mode) ? COLOR : BLACK_AND_WHITE);
+        IsColorModelSelected(color_mode_opt.value()) ? COLOR : BLACK_AND_WHITE);
   }
 
-  int margins_type = 0;
-  if (preview_settings.GetInteger(kSettingMarginsType, &margins_type) &&
-      margins_type != 0) {
+  if (preview_settings.FindIntKey(kSettingMarginsType).value_or(0) != 0)
     ReportPrintSettingHistogram(NON_DEFAULT_MARGINS);
-  }
 
-  bool headers = false;
-  if (preview_settings.GetBoolean(kSettingHeaderFooterEnabled, &headers) &&
-      headers) {
+  if (preview_settings.FindBoolKey(kSettingHeaderFooterEnabled).value_or(false))
     ReportPrintSettingHistogram(HEADERS_AND_FOOTERS);
-  }
 
-  bool css_background = false;
-  if (preview_settings.GetBoolean(kSettingShouldPrintBackgrounds,
-                                  &css_background) &&
-      css_background) {
+  if (preview_settings.FindBoolKey(kSettingShouldPrintBackgrounds)
+          .value_or(false)) {
     ReportPrintSettingHistogram(CSS_BACKGROUND);
   }
 
-  bool selection_only = false;
-  if (preview_settings.GetBoolean(kSettingShouldPrintSelectionOnly,
-                                  &selection_only) &&
-      selection_only) {
+  if (preview_settings.FindBoolKey(kSettingShouldPrintSelectionOnly)
+          .value_or(false)) {
     ReportPrintSettingHistogram(SELECTION_ONLY);
   }
 
-  bool rasterize = false;
-  if (preview_settings.GetBoolean(kSettingRasterizePdf, &rasterize) &&
-      rasterize) {
+  if (preview_settings.FindBoolKey(kSettingRasterizePdf).value_or(false))
     ReportPrintSettingHistogram(PRINT_AS_IMAGE);
-  }
 
-  bool fit_to_page = false;
   if (is_pdf &&
-      preview_settings.GetBoolean(kSettingFitToPageEnabled, &fit_to_page) &&
-      fit_to_page) {
+      preview_settings.FindBoolKey(kSettingFitToPageEnabled).value_or(false)) {
     ReportPrintSettingHistogram(FIT_TO_PAGE);
   }
 
-  int dpi_horizontal = 0;
-  int dpi_vertical = 0;
-  if (print_settings.GetInteger(kSettingDpiHorizontal, &dpi_horizontal) &&
-      print_settings.GetInteger(kSettingDpiVertical, &dpi_vertical) &&
-      dpi_horizontal > 0 && dpi_vertical > 0) {
-    bool is_default = false;
-    if (print_settings.GetBoolean(kSettingDpiDefault, &is_default))
-      ReportPrintSettingHistogram(is_default ? DEFAULT_DPI : NON_DEFAULT_DPI);
+  if (print_settings.FindIntKey(kSettingDpiHorizontal).value_or(0) > 0 &&
+      print_settings.FindIntKey(kSettingDpiVertical).value_or(0) > 0) {
+    base::Optional<bool> is_default_opt =
+        print_settings.FindBoolKey(kSettingDpiDefault);
+    if (is_default_opt) {
+      ReportPrintSettingHistogram(is_default_opt.value() ? DEFAULT_DPI
+                                                         : NON_DEFAULT_DPI);
+    }
   }
 }
 
-UserActionBuckets DetermineUserAction(const base::DictionaryValue& settings) {
-  bool value = false;
+UserActionBuckets DetermineUserAction(const base::Value& settings) {
 #if defined(OS_MACOSX)
-  value = settings.HasKey(kSettingOpenPDFInPreview);
-#endif
-  if (value)
+  if (settings.FindKey(kSettingOpenPDFInPreview) != nullptr)
     return OPEN_IN_MAC_PREVIEW;
+#endif
   // This needs to be checked before checking for a cloud print ID, since a
   // print ticket for printing to Drive will also contain a cloud print ID.
-  settings.GetBoolean(kSettingPrintToGoogleDrive, &value);
-  if (value)
+  if (settings.FindBoolKey(kSettingPrintToGoogleDrive).value_or(false))
     return PRINT_TO_GOOGLE_DRIVE;
-  if (settings.HasKey(kSettingCloudPrintId))
+  if (settings.FindKey(kSettingCloudPrintId) != nullptr)
     return PRINT_WITH_CLOUD_PRINT;
-  settings.GetBoolean(kSettingPrintWithPrivet, &value);
-  if (value)
+  if (settings.FindBoolKey(kSettingPrintWithPrivet).value_or(false))
     return PRINT_WITH_PRIVET;
-  settings.GetBoolean(kSettingPrintWithExtension, &value);
-  if (value)
+  if (settings.FindBoolKey(kSettingPrintWithExtension).value_or(false))
     return PRINT_WITH_EXTENSION;
-  settings.GetBoolean(kSettingPrintToPDF, &value);
-  if (value)
+  if (settings.FindBoolKey(kSettingPrintToPDF).value_or(false))
     return PRINT_TO_PDF;
-  settings.GetBoolean(kSettingShowSystemDialog, &value);
-  if (value)
+  if (settings.FindBoolKey(kSettingShowSystemDialog).value_or(false))
     return FALLBACK_TO_ADVANCED_SETTINGS_DIALOG;
   return PRINT_TO_PRINTER;
 }
@@ -716,11 +682,9 @@
   args->GetString(0, &callback_id);
   CHECK(!callback_id.empty());
   args->GetString(1, &json_str);
-  std::unique_ptr<base::DictionaryValue> settings =
-      GetSettingsDictionary(json_str);
-  CHECK(settings);
-  int request_id = -1;
-  settings->GetInteger(kPreviewRequestID, &request_id);
+  base::Value settings = GetSettingsDictionary(json_str);
+  CHECK(settings.is_dict());
+  int request_id = settings.FindIntKey(kPreviewRequestID).value();
   CHECK_GT(request_id, -1);
 
   CHECK(!base::ContainsKey(preview_callbacks_, request_id));
@@ -728,8 +692,9 @@
   print_preview_ui()->OnPrintPreviewRequest(request_id);
   // Add an additional key in order to identify |print_preview_ui| later on
   // when calling PrintPreviewUI::ShouldCancelRequest() on the IO thread.
-  settings->SetInteger(kPreviewUIID,
-                       print_preview_ui()->GetIDForPrintPreviewUI().value());
+  settings.SetKey(
+      kPreviewUIID,
+      base::Value(print_preview_ui()->GetIDForPrintPreviewUI().value()));
 
   // Increment request count.
   ++regenerate_preview_request_count_;
@@ -747,25 +712,26 @@
 
   // Retrieve the page title and url and send it to the renderer process if
   // headers and footers are to be displayed.
-  bool display_header_footer = false;
-  bool success =
-      settings->GetBoolean(kSettingHeaderFooterEnabled, &display_header_footer);
-  DCHECK(success);
-  if (display_header_footer) {
-    settings->SetString(kSettingHeaderFooterTitle, initiator->GetTitle());
+  base::Optional<bool> display_header_footer_opt =
+      settings.FindBoolKey(kSettingHeaderFooterEnabled);
+  DCHECK(display_header_footer_opt);
+  if (display_header_footer_opt.value_or(false)) {
+    settings.SetKey(kSettingHeaderFooterTitle,
+                    base::Value(initiator->GetTitle()));
 
     url::Replacements<char> url_sanitizer;
     url_sanitizer.ClearUsername();
     url_sanitizer.ClearPassword();
     const GURL& initiator_url = initiator->GetLastCommittedURL();
-    settings->SetString(kSettingHeaderFooterURL,
-                        url_formatter::FormatUrl(
-                            initiator_url.ReplaceComponents(url_sanitizer)));
+    settings.SetKey(kSettingHeaderFooterURL,
+                    base::Value(url_formatter::FormatUrl(
+                        initiator_url.ReplaceComponents(url_sanitizer))));
   }
 
   VLOG(1) << "Print preview request start";
 
-  rfh->Send(new PrintMsg_PrintPreview(rfh->GetRoutingID(), *settings));
+  rfh->Send(new PrintMsg_PrintPreview(
+      rfh->GetRoutingID(), static_cast<base::DictionaryValue&>(settings)));
   last_preview_settings_ = std::move(settings);
 }
 
@@ -780,18 +746,16 @@
   std::string json_str;
   CHECK(args->GetString(1, &json_str));
 
-  std::unique_ptr<base::DictionaryValue> settings =
-      GetSettingsDictionary(json_str);
-  if (!settings) {
+  base::Value settings = GetSettingsDictionary(json_str);
+  if (!settings.is_dict()) {
     RejectJavascriptCallback(base::Value(callback_id), base::Value(-1));
     return;
   }
 
-  const UserActionBuckets user_action = DetermineUserAction(*settings);
+  const UserActionBuckets user_action = DetermineUserAction(settings);
 
-  int page_count = 0;
-  if (!settings->GetInteger(kSettingPreviewPageCount, &page_count) ||
-      page_count <= 0) {
+  int page_count = settings.FindIntKey(kSettingPreviewPageCount).value_or(-1);
+  if (page_count <= 0) {
     RejectJavascriptCallback(base::Value(callback_id),
                              GetErrorValue(user_action, "NO_PAGE_COUNT"));
     return;
@@ -809,29 +773,10 @@
   DCHECK(data->size());
   DCHECK(data->front());
 
-  std::string destination_id;
-  std::string print_ticket;
-  std::string capabilities;
-  int width = 0;
-  int height = 0;
-  if (user_action == PRINT_WITH_PRIVET || user_action == PRINT_WITH_EXTENSION) {
-    if (!settings->GetString(kSettingDeviceName, &destination_id) ||
-        !settings->GetString(kSettingTicket, &print_ticket) ||
-        !settings->GetString(kSettingCapabilities, &capabilities) ||
-        !settings->GetInteger(kSettingPageWidth, &width) ||
-        !settings->GetInteger(kSettingPageHeight, &height) || width <= 0 ||
-        height <= 0) {
-      NOTREACHED();
-      RejectJavascriptCallback(base::Value(callback_id),
-                               GetErrorValue(user_action, "FAILED"));
-      return;
-    }
-  }
-
   // After validating |settings|, record metrics.
   bool is_pdf = !print_preview_ui()->source_is_modifiable();
-  if (last_preview_settings_)
-    ReportPrintSettingsStats(*settings, *last_preview_settings_, is_pdf);
+  if (last_preview_settings_.is_dict())
+    ReportPrintSettingsStats(settings, last_preview_settings_, is_pdf);
   {
     PrintDocumentTypeBuckets doc_type = is_pdf ? PDF_DOCUMENT : HTML_DOCUMENT;
     size_t average_page_size_in_kb = data->size() / page_count;
@@ -852,28 +797,10 @@
     return;
   }
 
-  PrinterType type = GetPrinterTypeForUserAction(user_action);
-  base::Value job_settings;
-  switch (type) {
-    case PrinterType::kPdfPrinter:
-      // Do nothing.
-      break;
-    case PrinterType::kLocalPrinter:
-      job_settings = base::Value::FromUniquePtrValue(std::move(settings));
-      break;
-    default:
-      std::unique_ptr<base::Value> ticket_value =
-          base::JSONReader::Read(print_ticket);
-      if (!ticket_value) {
-        OnPrintResult(callback_id, base::Value("Invalid settings"));
-        return;
-      }
-      job_settings = base::Value::FromUniquePtrValue(std::move(ticket_value));
-  }
-  PrinterHandler* handler = GetPrinterHandler(type);
-  handler->StartPrint(destination_id, capabilities,
-                      print_preview_ui()->initiator_title(),
-                      std::move(job_settings), gfx::Size(width, height), data,
+  PrinterHandler* handler =
+      GetPrinterHandler(GetPrinterTypeForUserAction(user_action));
+  handler->StartPrint(print_preview_ui()->initiator_title(),
+                      std::move(settings), data,
                       base::BindOnce(&PrintPreviewHandler::OnPrintResult,
                                      weak_factory_.GetWeakPtr(), callback_id));
 }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index 344b05af..cee6ae8 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -318,7 +318,7 @@
   bool has_logged_printers_count_;
 
   // The settings used for the most recent preview request.
-  std::unique_ptr<base::DictionaryValue> last_preview_settings_;
+  base::Value last_preview_settings_;
 
 #if defined(OS_CHROMEOS)
   // Holds token service to get OAuth2 access tokens.
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
index 17c9919..9fa7dab 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler_unittest.cc
@@ -143,11 +143,8 @@
   void StartGrantPrinterAccess(const std::string& printer_id,
                                GetPrinterInfoCallback callback) override {}
 
-  void StartPrint(const std::string& destination_id,
-                  const std::string& capability,
-                  const base::string16& job_title,
-                  base::Value ticket,
-                  const gfx::Size& page_size,
+  void StartPrint(const base::string16& job_title,
+                  base::Value settings,
                   scoped_refptr<base::RefCountedMemory> print_data,
                   PrintCallback callback) override {
     std::move(callback).Run(base::Value());
@@ -459,7 +456,7 @@
     handler()->reset_calls();
     base::Value args(base::Value::Type::LIST);
     std::string callback_id_in =
-        "test-callback-id-" + base::UintToString(i + 1);
+        "test-callback-id-" + base::NumberToString(i + 1);
     args.GetList().emplace_back(callback_id_in);
     args.GetList().emplace_back(type);
     std::unique_ptr<base::ListValue> list_args =
@@ -505,7 +502,7 @@
     handler()->reset_calls();
     base::Value args(base::Value::Type::LIST);
     std::string callback_id_in =
-        "test-callback-id-" + base::UintToString(i + 1);
+        "test-callback-id-" + base::NumberToString(i + 1);
     args.GetList().emplace_back(callback_id_in);
     args.GetList().emplace_back(kDummyPrinterName);
     args.GetList().emplace_back(type);
@@ -534,7 +531,8 @@
     handler()->reset_calls();
     base::Value args(base::Value::Type::LIST);
     std::string callback_id_in =
-        "test-callback-id-" + base::UintToString(i + base::size(kAllTypes) + 1);
+        "test-callback-id-" +
+        base::NumberToString(i + base::size(kAllTypes) + 1);
     args.GetList().emplace_back(callback_id_in);
     args.GetList().emplace_back("EmptyPrinter");
     args.GetList().emplace_back(type);
@@ -565,7 +563,7 @@
     handler()->reset_calls();
     base::Value args(base::Value::Type::LIST);
     std::string callback_id_in =
-        "test-callback-id-" + base::UintToString(i + 1);
+        "test-callback-id-" + base::NumberToString(i + 1);
     args.GetList().emplace_back(callback_id_in);
     base::Value print_ticket = GetPrintTicket(type, cloud);
     std::string json;
@@ -740,7 +738,7 @@
     handler()->reset_calls();
     base::Value args(base::Value::Type::LIST);
     std::string callback_id_in =
-        "test-callback-id-" + base::UintToString(i + 1);
+        "test-callback-id-" + base::NumberToString(i + 1);
     args.GetList().emplace_back(callback_id_in);
     args.GetList().emplace_back(kDummyPrinterName);
     args.GetList().emplace_back(type);
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 85aa19d..b32db77a 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -310,7 +310,7 @@
 #elif defined(OS_WIN)
   enterprise_managed = base::IsMachineExternallyManaged();
 #endif
-  source->AddBoolean("IsEnterpriseManaged", enterprise_managed);
+  source->AddBoolean("isEnterpriseManaged", enterprise_managed);
 
   bool nup_printing_enabled =
       base::FeatureList::IsEnabled(features::kNupPrinting);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_utils.cc b/chrome/browser/ui/webui/print_preview/print_preview_utils.cc
index 7191e3c..c89f4a7 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_utils.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_utils.cc
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "base/bind.h"
+#include "base/json/json_reader.h"
 #include "base/logging.h"
 #include "base/memory/ref_counted_memory.h"
 #include "base/stl_util.h"
@@ -208,4 +209,32 @@
       preview_web_contents->GetMainFrame(), std::move(callback));
 }
 
+bool ParseSettings(const base::Value& settings,
+                   std::string* out_destination_id,
+                   std::string* out_capabilities,
+                   gfx::Size* out_page_size,
+                   base::Value* out_ticket) {
+  const std::string* destination_id_opt =
+      settings.FindStringKey(kSettingDeviceName);
+  const std::string* ticket_opt = settings.FindStringKey(kSettingTicket);
+  const std::string* capabilities_opt =
+      settings.FindStringKey(kSettingCapabilities);
+  out_page_size->SetSize(settings.FindIntKey(kSettingPageWidth).value_or(0),
+                         settings.FindIntKey(kSettingPageHeight).value_or(0));
+  if (!destination_id_opt || !ticket_opt || !capabilities_opt ||
+      out_page_size->IsEmpty()) {
+    NOTREACHED();
+    return false;
+  }
+  std::unique_ptr<base::Value> ticket_value =
+      base::JSONReader::Read(*ticket_opt);
+  if (!ticket_value)
+    return false;
+
+  *out_destination_id = *destination_id_opt;
+  *out_capabilities = *capabilities_opt;
+  *out_ticket = base::Value::FromUniquePtrValue(std::move(ticket_value));
+  return true;
+}
+
 }  // namespace printing
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_utils.h b/chrome/browser/ui/webui/print_preview/print_preview_utils.h
index 4ebe961..b7c00de 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_utils.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_utils.h
@@ -46,6 +46,14 @@
                      content::WebContents* preview_web_contents,
                      PrinterHandler::PrintCallback callback);
 
+// Parses print job settings. Returns |true| on success.
+// This is used by extension and privet printers.
+bool ParseSettings(const base::Value& settings,
+                   std::string* out_destination_id,
+                   std::string* out_capabilities,
+                   gfx::Size* out_page_size,
+                   base::Value* out_ticket);
+
 }  // namespace printing
 
 #endif  // CHROME_BROWSER_UI_WEBUI_PRINT_PREVIEW_PRINT_PREVIEW_UTILS_H_
diff --git a/chrome/browser/ui/webui/print_preview/printer_handler.h b/chrome/browser/ui/webui/print_preview/printer_handler.h
index 8d63ab3..4d6a476 100644
--- a/chrome/browser/ui/webui/print_preview/printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/printer_handler.h
@@ -19,10 +19,6 @@
 class WebContents;
 }
 
-namespace gfx {
-class Size;
-}
-
 class Profile;
 
 namespace printing {
@@ -111,18 +107,12 @@
                                        GetPrinterInfoCallback callback);
 
   // Starts a print request.
-  // |destination_id|: The printer to which print job should be sent.
-  // |capability|: Capability reported by the printer.
-  // |job_title|: The  title used for print job.
-  // |ticket|: The print job ticket.
-  // |page_size|: The document page size.
+  // |job_title|: The title used for print job.
+  // |settings|: The print job settings.
   // |print_data|: The document bytes to print.
   // |callback| should be called in the response to the request.
-  virtual void StartPrint(const std::string& destination_id,
-                          const std::string& capability,
-                          const base::string16& job_title,
-                          base::Value ticket,
-                          const gfx::Size& page_size,
+  virtual void StartPrint(const base::string16& job_title,
+                          base::Value settings,
                           scoped_refptr<base::RefCountedMemory> print_data,
                           PrintCallback callback) = 0;
 };
diff --git a/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc b/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
index 87ad1eb..0d695cf 100644
--- a/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/privet_printer_handler.cc
@@ -76,20 +76,27 @@
 }
 
 void PrivetPrinterHandler::StartPrint(
-    const std::string& destination_id,
-    const std::string& capability,
     const base::string16& job_title,
-    base::Value ticket,
-    const gfx::Size& page_size,
+    base::Value settings,
     scoped_refptr<base::RefCountedMemory> print_data,
     PrintCallback callback) {
+  std::string destination_id;
+  std::string capabilities;
+  gfx::Size page_size;
+  base::Value ticket;
+  if (!ParseSettings(settings, &destination_id, &capabilities, &page_size,
+                     &ticket)) {
+    std::move(callback).Run(base::Value(-1));
+    return;
+  }
+
   DCHECK(!print_callback_);
   print_callback_ = std::move(callback);
   CreateHTTP(
       destination_id,
       base::BindOnce(&PrivetPrinterHandler::PrintUpdateClient,
                      weak_ptr_factory_.GetWeakPtr(), job_title, print_data,
-                     std::move(ticket), capability, page_size));
+                     std::move(ticket), capabilities, page_size));
 }
 
 void PrivetPrinterHandler::LocalPrinterChanged(
diff --git a/chrome/browser/ui/webui/print_preview/privet_printer_handler.h b/chrome/browser/ui/webui/print_preview/privet_printer_handler.h
index 8029948..5d0e7a5 100644
--- a/chrome/browser/ui/webui/print_preview/privet_printer_handler.h
+++ b/chrome/browser/ui/webui/print_preview/privet_printer_handler.h
@@ -44,12 +44,8 @@
                         GetPrintersDoneCallback done_callback) override;
   void StartGetCapability(const std::string& destination_id,
                           GetCapabilityCallback calback) override;
-  // TODO(tbarzic): It might make sense to have the strings in a single struct.
-  void StartPrint(const std::string& destination_id,
-                  const std::string& capability,
-                  const base::string16& job_title,
+  void StartPrint(const base::string16& job_title,
                   base::Value ticket,
-                  const gfx::Size& page_size,
                   scoped_refptr<base::RefCountedMemory> print_data,
                   PrintCallback callback) override;
 
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 5a4b72f..46f45b57 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -679,7 +679,7 @@
   event->SetBoolean("rollback", rollback);
   event->SetString("version", version);
   // DictionaryValue does not support int64_t, so convert to string.
-  event->SetString("size", base::Int64ToString(size));
+  event->SetString("size", base::NumberToString(size));
 #if defined(OS_CHROMEOS)
   if (status == VersionUpdater::FAILED_OFFLINE ||
       status == VersionUpdater::FAILED_CONNECTION_TYPE_DISALLOWED) {
diff --git a/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc b/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc
index 4f0138f..dc4e23c 100644
--- a/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/device_power_handler.cc
@@ -302,10 +302,10 @@
     status_text = l10n_util::GetStringFUTF16(
         charging ? IDS_SETTINGS_BATTERY_STATUS_CHARGING
                  : IDS_SETTINGS_BATTERY_STATUS,
-        base::IntToString16(percent), GetBatteryTimeText(time_left));
+        base::NumberToString16(percent), GetBatteryTimeText(time_left));
   } else {
     status_text = l10n_util::GetStringFUTF16(IDS_SETTINGS_BATTERY_STATUS_SHORT,
-                                             base::IntToString16(percent));
+                                             base::NumberToString16(percent));
   }
 
   base::DictionaryValue battery_dict;
diff --git a/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc b/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
index 7ddc716..9b7cf3f1 100644
--- a/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
+++ b/chrome/browser/ui/webui/settings/chromeos/fingerprint_handler.cc
@@ -232,7 +232,7 @@
   for (int i = 1; i <= kMaxAllowedFingerprints; ++i) {
     std::string fingerprint_name = l10n_util::GetStringFUTF8(
         IDS_SETTINGS_PEOPLE_LOCK_SCREEN_NEW_FINGERPRINT_DEFAULT_NAME,
-        base::IntToString16(i));
+        base::NumberToString16(i));
     if (!base::ContainsValue(fingerprints_labels_, fingerprint_name)) {
       fp_service_->StartEnrollSession(user_id_, fingerprint_name);
       break;
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 a6791dbe..39386f28 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
@@ -59,6 +59,7 @@
 #include "ash/public/interfaces/voice_interaction_controller.mojom.h"
 #include "base/system/sys_info.h"
 #include "chrome/browser/chromeos/arc/arc_util.h"
+#include "chrome/browser/chromeos/crostini/crostini_util.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos.h"
 #include "chrome/browser/chromeos/ownership/owner_settings_service_chromeos_factory.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
@@ -466,10 +467,14 @@
       {"crostiniEnable", IDS_SETTINGS_TURN_ON},
       {"crostiniRemove", IDS_SETTINGS_CROSTINI_REMOVE},
       {"crostiniSharedPaths", IDS_SETTINGS_CROSTINI_SHARED_PATHS},
+      {"crostiniSharedPathsListHeading",
+       IDS_SETTINGS_CROSTINI_SHARED_PATHS_LIST_HEADING},
       {"crostiniSharedPathsInstructionsAdd",
        IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_ADD},
       {"crostiniSharedPathsInstructionsRemove",
        IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_REMOVE},
+      {"crostiniSharedPathsRemoveSharing",
+       IDS_SETTINGS_CROSTINI_SHARED_PATHS_REMOVE_SHARING},
       {"crostiniSharedUsbDevicesLabel",
        IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LABEL},
       {"crostiniSharedUsbDevicesDescription",
@@ -482,7 +487,12 @@
       l10n_util::GetStringFUTF16(
           IDS_SETTINGS_CROSTINI_SUBTEXT,
           GetHelpUrlWithBoard(chrome::kLinuxAppsLearnMoreURL)));
-
+  html_source->AddString(
+      "crostiniSharedPathsInstructionsLocate",
+      l10n_util::GetStringFUTF16(
+          IDS_SETTINGS_CROSTINI_SHARED_PATHS_INSTRUCTIONS_LOCATE,
+          base::ASCIIToUTF16(
+              crostini::ContainerChromeOSBaseDirectory().value())));
   html_source->AddBoolean(
       "enableCrostiniUsbDeviceSupport",
       base::FeatureList::IsEnabled(chromeos::features::kCrostiniUsbSupport));
@@ -1808,7 +1818,7 @@
 
   // Format numbers to be used on the pin keyboard.
   for (int j = 0; j <= 9; j++) {
-    html_source->AddString("pinKeyboard" + base::IntToString(j),
+    html_source->AddString("pinKeyboard" + base::NumberToString(j),
                            base::FormatNumber(int64_t{j}));
   }
 
@@ -2310,6 +2320,7 @@
     {"cookieName", IDS_SETTINGS_COOKIES_COOKIE_NAME_LABEL},
     {"cookiePath", IDS_SETTINGS_COOKIES_COOKIE_PATH_LABEL},
     {"cookieSendFor", IDS_SETTINGS_COOKIES_COOKIE_SENDFOR_LABEL},
+    {"databaseOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
     {"fileSystemOrigin", IDS_SETTINGS_COOKIES_LOCAL_STORAGE_ORIGIN_LABEL},
     {"fileSystemPersistentUsage",
      IDS_SETTINGS_COOKIES_FILE_SYSTEM_PERSISTENT_USAGE_LABEL},
@@ -2333,7 +2344,6 @@
      IDS_SETTINGS_COOKIES_LOCAL_STORAGE_SIZE_ON_DISK_LABEL},
     {"sharedWorkerWorker", IDS_SETTINGS_COOKIES_SHARED_WORKER_WORKER_LABEL},
     {"sharedWorkerName", IDS_SETTINGS_COOKIES_COOKIE_NAME_LABEL},
-    {"webdbDesc", IDS_SETTINGS_COOKIES_WEB_DATABASE_DESCRIPTION_LABEL},
     {"siteSettingsCategoryPageTitle", IDS_SETTINGS_SITE_SETTINGS_CATEGORY},
     {"siteSettingsCategoryCamera", IDS_SETTINGS_SITE_SETTINGS_CAMERA},
     {"siteSettingsCameraLabel", IDS_SETTINGS_SITE_SETTINGS_CAMERA_LABEL},
diff --git a/chrome/browser/ui/webui/settings/people_handler.cc b/chrome/browser/ui/webui/settings/people_handler.cc
index c8b09336..a48662a8 100644
--- a/chrome/browser/ui/webui/settings/people_handler.cc
+++ b/chrome/browser/ui/webui/settings/people_handler.cc
@@ -1078,9 +1078,11 @@
   if (!service || service->GetUserSettings()->IsFirstSetupComplete())
     return;
 
+  unified_consent::metrics::RecordSyncSetupDataTypesHistrogam(
+      service->GetUserSettings(), profile_->GetPrefs());
+
   // We're done configuring, so notify SyncService that it is OK to start
   // syncing.
-  sync_blocker_.reset();
   service->GetUserSettings()->SetFirstSetupComplete();
   FireWebUIListener("sync-settings-saved");
 }
diff --git a/chrome/browser/ui/webui/snippets_internals/snippets_internals_page_handler.cc b/chrome/browser/ui/webui/snippets_internals/snippets_internals_page_handler.cc
index b43444e..79ae30b 100644
--- a/chrome/browser/ui/webui/snippets_internals/snippets_internals_page_handler.cc
+++ b/chrome/browser/ui/webui/snippets_internals/snippets_internals_page_handler.cc
@@ -102,7 +102,7 @@
   auto item = snippets_internals::mojom::SuggestionItem::New();
   item->suggestionTitle = base::UTF16ToUTF8(suggestion.title());
   item->suggestionIdWithinCategory = suggestion.id().id_within_category();
-  item->suggestionId = "content-suggestion-" + base::IntToString(index);
+  item->suggestionId = "content-suggestion-" + base::NumberToString(index);
   item->url = suggestion.url().spec();
   item->faviconUrl = suggestion.url_with_favicon().spec();
   item->snippet = base::UTF16ToUTF8(suggestion.snippet_text());
@@ -186,8 +186,9 @@
 
   std::set<variations::VariationID> ids = GetSnippetsExperiments();
   std::vector<std::string> string_ids;
-  std::transform(ids.begin(), ids.end(), std::back_inserter(string_ids),
-                 &base::IntToString);
+  std::transform(
+      ids.begin(), ids.end(), std::back_inserter(string_ids),
+      [](variations::VariationID id) { return base::NumberToString(id); });
 
   properties["experiment-ids"] = base::JoinString(string_ids, ", ");
   std::move(callback).Run(properties);
diff --git a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
index d1ce163..bf522660 100644
--- a/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
+++ b/chrome/browser/ui/zoom/chrome_zoom_level_prefs.cc
@@ -144,8 +144,9 @@
   } else {
     base::DictionaryValue dict;
     dict.SetDouble(kZoomLevelPath, level);
-    dict.SetString(kLastModifiedPath,
-                   base::Int64ToString(change.last_modified.ToInternalValue()));
+    dict.SetString(
+        kLastModifiedPath,
+        base::NumberToString(change.last_modified.ToInternalValue()));
     host_zoom_dictionary_weak->SetKey(change.host, std::move(dict));
   }
 }
diff --git a/chrome/browser/unified_consent/unified_consent_service_factory.cc b/chrome/browser/unified_consent/unified_consent_service_factory.cc
index 2d52141..38a1076 100644
--- a/chrome/browser/unified_consent/unified_consent_service_factory.cc
+++ b/chrome/browser/unified_consent/unified_consent_service_factory.cc
@@ -6,7 +6,6 @@
 
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/identity_manager_factory.h"
-#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/sync/profile_sync_service_factory.h"
 #include "chrome/browser/unified_consent/chrome_unified_consent_service_client.h"
 #include "components/keyed_service/content/browser_context_dependency_manager.h"
diff --git a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
index c8fbfc9..4517b1b1 100644
--- a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_browsertest.cc
@@ -50,6 +50,15 @@
       "theme_color": "#00FF00"
     })";
 
+constexpr char kPwaHtml[] =
+    R"(
+<html>
+<head>
+  <link rel="manifest" href="manifest.json">
+</head>
+</html>
+)";
+
 // WebUIController that serves a System PWA.
 class TestWebUIController : public content::WebUIController {
  public:
@@ -58,17 +67,18 @@
     content::WebUIDataSource* data_source =
         content::WebUIDataSource::Create("test-system-app");
     data_source->AddResourcePath("icon-256.png", IDR_PRODUCT_LOGO_256);
-    data_source->AddResourcePath("pwa.html", IDR_PWA_HTML);
     data_source->SetRequestFilter(base::BindRepeating(
         [](const std::string& id,
            const content::WebUIDataSource::GotDataCallback& callback) {
           scoped_refptr<base::RefCountedString> ref_contents(
               new base::RefCountedString);
-          if (id != "manifest.json")
+          if (id == "manifest.json")
+            ref_contents->data() = kSystemAppManifestText;
+          else if (id == "pwa.html")
+            ref_contents->data() = kPwaHtml;
+          else
             return false;
 
-          ref_contents->data() = kSystemAppManifestText;
-
           callback.Run(ref_contents);
           return true;
         }));
diff --git a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc
index 7794c61..13f60b4 100644
--- a/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc
+++ b/chrome/browser/web_applications/bookmark_apps/system_web_app_manager_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
 #include "chrome/browser/prefs/browser_prefs.h"
+#include "chrome/browser/web_applications/bookmark_apps/test_web_app_provider.h"
 #include "chrome/browser/web_applications/components/pending_app_manager.h"
 #include "chrome/browser/web_applications/components/test_pending_app_manager.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
@@ -50,20 +51,31 @@
 
 class SystemWebAppManagerTest : public ChromeRenderViewHostTestHarness {
  public:
-  SystemWebAppManagerTest() = default;
+  SystemWebAppManagerTest()
+      : test_web_app_provider_creator_(
+            base::BindOnce(&SystemWebAppManagerTest::CreateWebAppProvider,
+                           base::Unretained(this))) {
+    scoped_feature_list_.InitWithFeatures({features::kSystemWebApps}, {});
+  }
+
   ~SystemWebAppManagerTest() override = default;
 
-  void SetUp() override {
-    ChromeRenderViewHostTestHarness::SetUp();
-    scoped_feature_list_.InitWithFeatures({features::kSystemWebApps}, {});
+  std::unique_ptr<KeyedService> CreateWebAppProvider(Profile* profile) {
+    auto provider = std::make_unique<TestWebAppProvider>(profile);
 
-    // Reset WebAppProvider so that its SystemWebAppManager doesn't interfere
-    // with tests.
-    WebAppProvider::Get(profile())->Reset();
+    auto test_pending_app_manager = std::make_unique<TestPendingAppManager>();
+    test_pending_app_manager_ = test_pending_app_manager.get();
+    provider->SetPendingAppManager(std::move(test_pending_app_manager));
+
+    auto system_web_app_manager = std::make_unique<TestSystemWebAppManager>(
+        profile, test_pending_app_manager_);
+    system_web_app_manager_ = system_web_app_manager.get();
+    provider->SetSystemWebAppManager(std::move(system_web_app_manager));
+
+    return provider;
   }
 
   void SimulatePreviouslyInstalledApp(
-      TestPendingAppManager* pending_app_manager,
       GURL url,
       InstallSource install_source) {
     std::string id =
@@ -74,11 +86,23 @@
     ExtensionIdsMap extension_ids_map(profile()->GetPrefs());
     extension_ids_map.Insert(url, id, install_source);
 
-    pending_app_manager->SimulatePreviouslyInstalledApp(url, install_source);
+    pending_app_manager()->SimulatePreviouslyInstalledApp(url, install_source);
+  }
+
+ protected:
+  TestPendingAppManager* pending_app_manager() {
+    return test_pending_app_manager_;
+  }
+
+  TestSystemWebAppManager* system_web_app_manager() {
+    return system_web_app_manager_;
   }
 
  private:
   base::test::ScopedFeatureList scoped_feature_list_;
+  TestWebAppProviderCreator test_web_app_provider_creator_;
+  TestPendingAppManager* test_pending_app_manager_ = nullptr;
+  TestSystemWebAppManager* system_web_app_manager_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(SystemWebAppManagerTest);
 };
@@ -88,79 +112,66 @@
   base::test::ScopedFeatureList disable_feature_list;
   disable_feature_list.InitWithFeatures({}, {features::kSystemWebApps});
 
-  auto pending_app_manager = std::make_unique<TestPendingAppManager>();
-
-  SimulatePreviouslyInstalledApp(pending_app_manager.get(), GURL(kAppUrl1),
+  SimulatePreviouslyInstalledApp(GURL(kAppUrl1),
                                  InstallSource::kSystemInstalled);
 
   std::vector<GURL> system_apps;
   system_apps.push_back(GURL(kAppUrl1));
 
-  TestSystemWebAppManager system_web_app_manager(profile(),
-                                                 pending_app_manager.get());
-  system_web_app_manager.SetSystemApps(std::move(system_apps));
-  system_web_app_manager.Start();
+  system_web_app_manager()->SetSystemApps(std::move(system_apps));
+  system_web_app_manager()->Start();
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_TRUE(pending_app_manager->install_requests().empty());
+  EXPECT_TRUE(pending_app_manager()->install_requests().empty());
 
   // We should try to uninstall the app that is no longer in the System App
   // list.
   EXPECT_EQ(std::vector<GURL>({GURL(kAppUrl1)}),
-            pending_app_manager->uninstall_requests());
+            pending_app_manager()->uninstall_requests());
 }
 
 // Test that System Apps do install with the feature enabled.
 TEST_F(SystemWebAppManagerTest, Enabled) {
-  auto pending_app_manager = std::make_unique<TestPendingAppManager>();
-
   std::vector<GURL> system_apps;
   system_apps.push_back(GURL(kAppUrl1));
   system_apps.push_back(GURL(kAppUrl2));
 
-  TestSystemWebAppManager system_web_app_manager(profile(),
-                                                 pending_app_manager.get());
-  system_web_app_manager.SetSystemApps(std::move(system_apps));
-  system_web_app_manager.Start();
-
+  system_web_app_manager()->SetSystemApps(std::move(system_apps));
+  system_web_app_manager()->Start();
   base::RunLoop().RunUntilIdle();
 
-  const auto& apps_to_install = pending_app_manager->install_requests();
+  const auto& apps_to_install = pending_app_manager()->install_requests();
   EXPECT_FALSE(apps_to_install.empty());
 }
 
 // Test that changing the set of System Apps uninstalls apps.
 TEST_F(SystemWebAppManagerTest, UninstallAppInstalledInPreviousSession) {
-  auto pending_app_manager = std::make_unique<TestPendingAppManager>();
-
   // Simulate System Apps and a regular app that were installed in the
   // previous session.
-  SimulatePreviouslyInstalledApp(pending_app_manager.get(), GURL(kAppUrl1),
+  SimulatePreviouslyInstalledApp(GURL(kAppUrl1),
                                  InstallSource::kSystemInstalled);
-  SimulatePreviouslyInstalledApp(pending_app_manager.get(), GURL(kAppUrl2),
+  SimulatePreviouslyInstalledApp(GURL(kAppUrl2),
                                  InstallSource::kSystemInstalled);
-  SimulatePreviouslyInstalledApp(pending_app_manager.get(), GURL(kAppUrl3),
-                                 InstallSource::kInternal);
+  SimulatePreviouslyInstalledApp(GURL(kAppUrl3), InstallSource::kInternal);
   std::vector<GURL> system_apps;
   system_apps.push_back(GURL(kAppUrl1));
 
-  TestSystemWebAppManager system_web_app_manager(profile(),
-                                                 pending_app_manager.get());
-  system_web_app_manager.SetSystemApps(std::move(system_apps));
-  system_web_app_manager.Start();
+  system_web_app_manager()->SetSystemApps(std::move(system_apps));
+  system_web_app_manager()->Start();
 
   base::RunLoop().RunUntilIdle();
 
   // We should only try to install the app in the System App list.
   std::vector<PendingAppManager::AppInfo> expected_apps_to_install;
   expected_apps_to_install.push_back(GetWindowedAppInfo());
-  EXPECT_EQ(pending_app_manager->install_requests(), expected_apps_to_install);
+  EXPECT_EQ(pending_app_manager()->install_requests(),
+            expected_apps_to_install);
 
   // We should try to uninstall the app that is no longer in the System App
   // list.
   EXPECT_EQ(std::vector<GURL>({GURL(kAppUrl2)}),
-            pending_app_manager->uninstall_requests());
+            pending_app_manager()->uninstall_requests());
 }
 
 }  // namespace web_app
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_linux.cc b/chrome/browser/web_applications/components/web_app_shortcut_linux.cc
index fc09135..a2d48d968 100644
--- a/chrome/browser/web_applications/components/web_app_shortcut_linux.cc
+++ b/chrome/browser/web_applications/components/web_app_shortcut_linux.cc
@@ -75,7 +75,7 @@
     argv.push_back("user");
 
     argv.push_back("--size");
-    argv.push_back(base::IntToString(width));
+    argv.push_back(base::NumberToString(width));
 
     argv.push_back(temp_file_path.value());
     argv.push_back(icon_name);
diff --git a/chrome/browser/web_applications/components/web_app_shortcut_mac.mm b/chrome/browser/web_applications/components/web_app_shortcut_mac.mm
index 3748bca..029c4a1 100644
--- a/chrome/browser/web_applications/components/web_app_shortcut_mac.mm
+++ b/chrome/browser/web_applications/components/web_app_shortcut_mac.mm
@@ -267,8 +267,9 @@
       continue;
 
     base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
-    command_line.AppendSwitchASCII(app_mode::kLaunchedByChromeProcessId,
-                                   base::IntToString(base::GetCurrentProcId()));
+    command_line.AppendSwitchASCII(
+        app_mode::kLaunchedByChromeProcessId,
+        base::NumberToString(base::GetCurrentProcId()));
     if (launched_after_rebuild)
       command_line.AppendSwitch(app_mode::kLaunchedAfterRebuild);
 
diff --git a/chrome/browser/web_applications/extensions/BUILD.gn b/chrome/browser/web_applications/extensions/BUILD.gn
index fc0ed916..d6955db 100644
--- a/chrome/browser/web_applications/extensions/BUILD.gn
+++ b/chrome/browser/web_applications/extensions/BUILD.gn
@@ -8,10 +8,10 @@
 
 source_set("extensions") {
   sources = [
+    "bookmark_app_install_finalizer.cc",
+    "bookmark_app_install_finalizer.h",
     "bookmark_app_installation_task.cc",
     "bookmark_app_installation_task.h",
-    "bookmark_app_installer.cc",
-    "bookmark_app_installer.h",
     "bookmark_app_tab_helper.cc",
     "bookmark_app_tab_helper.h",
     "bookmark_app_util.cc",
@@ -42,8 +42,8 @@
   testonly = true
 
   sources = [
+    "bookmark_app_install_finalizer_unittest.cc",
     "bookmark_app_installation_task_unittest.cc",
-    "bookmark_app_installer_unittest.cc",
     "bookmark_app_util_unittest.cc",
     "pending_bookmark_app_manager_unittest.cc",
     "web_app_extension_ids_map_unittest.cc",
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
new file mode 100644
index 0000000..bb2b4b2
--- /dev/null
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.cc
@@ -0,0 +1,56 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
+#include "chrome/common/web_application_info.h"
+#include "extensions/common/extension.h"
+#include "url/gurl.h"
+
+namespace extensions {
+
+BookmarkAppInstallFinalizer::BookmarkAppInstallFinalizer(Profile* profile)
+    : crx_installer_(CrxInstaller::CreateSilent(
+          ExtensionSystem::Get(profile)->extension_service())) {}
+
+BookmarkAppInstallFinalizer::~BookmarkAppInstallFinalizer() = default;
+
+void BookmarkAppInstallFinalizer::Install(
+    const WebApplicationInfo& web_app_info,
+    ResultCallback callback) {
+  crx_installer_->set_installer_callback(base::BindOnce(
+      &BookmarkAppInstallFinalizer::OnInstall, weak_ptr_factory_.GetWeakPtr(),
+      std::move(callback), web_app_info.app_url));
+  crx_installer_->InstallWebApp(web_app_info);
+}
+
+void BookmarkAppInstallFinalizer::SetCrxInstallerForTesting(
+    scoped_refptr<CrxInstaller> crx_installer) {
+  crx_installer_ = crx_installer;
+}
+
+void BookmarkAppInstallFinalizer::OnInstall(
+    ResultCallback callback,
+    const GURL& app_url,
+    const base::Optional<CrxInstallError>& error) {
+  if (error) {
+    std::move(callback).Run(std::string());
+    return;
+  }
+
+  auto* installed_extension = crx_installer_->extension();
+  DCHECK(installed_extension);
+  DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(installed_extension), app_url);
+
+  std::move(callback).Run(installed_extension->id());
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
new file mode 100644
index 0000000..ea9d659
--- /dev/null
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h
@@ -0,0 +1,58 @@
+// 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_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_INSTALL_FINALIZER_H_
+#define CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_INSTALL_FINALIZER_H_
+
+#include "base/callback_forward.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "extensions/common/extension_id.h"
+
+class GURL;
+class Profile;
+
+struct WebApplicationInfo;
+
+namespace extensions {
+
+class CrxInstaller;
+class CrxInstallError;
+
+// Class used to actually install the Bookmark App in the system.
+class BookmarkAppInstallFinalizer {
+ public:
+  using ResultCallback = base::OnceCallback<void(const ExtensionId&)>;
+
+  // Constructs a BookmarkAppInstallFinalizer that will install the Bookmark App
+  // in |profile|.
+  explicit BookmarkAppInstallFinalizer(Profile* profile);
+  virtual ~BookmarkAppInstallFinalizer();
+
+  // TODO(crbug.com/864904): This should take more options e.g. what container
+  // to launch the app in, should the app sync, etc.
+  virtual void Install(const WebApplicationInfo& web_app_info,
+                       ResultCallback callback);
+
+  void SetCrxInstallerForTesting(scoped_refptr<CrxInstaller> crx_installer);
+
+ private:
+  void OnInstall(ResultCallback callback,
+                 const GURL& app_url,
+                 const base::Optional<CrxInstallError>& error);
+
+  scoped_refptr<CrxInstaller> crx_installer_;
+
+  // 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);
+};
+
+}  // namespace extensions
+
+#endif  // CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_INSTALL_FINALIZER_H_
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
new file mode 100644
index 0000000..297d5c50
--- /dev/null
+++ b/chrome/browser/web_applications/extensions/bookmark_app_install_finalizer_unittest.cc
@@ -0,0 +1,138 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/web_applications/extensions/bookmark_app_install_finalizer.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/run_loop.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/crx_installer.h"
+#include "chrome/browser/extensions/test_extension_system.h"
+#include "chrome/common/web_application_info.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "chrome/test/base/testing_profile.h"
+#include "extensions/browser/install/crx_install_error.h"
+#include "extensions/common/extension_id.h"
+
+namespace extensions {
+
+namespace {
+
+const char kWebAppUrl[] = "https://foo.example";
+const char kWebAppTitle[] = "Foo Title";
+
+}  // namespace
+
+class BookmarkAppInstallFinalizerTest : public ChromeRenderViewHostTestHarness {
+ public:
+  // Subclass that runs a closure when an extension is unpacked successfully.
+  // Useful for tests that want to trigger their own succeess/failure events.
+  class FakeCrxInstaller : public CrxInstaller {
+   public:
+    explicit FakeCrxInstaller(Profile* profile)
+        : CrxInstaller(
+              ExtensionSystem::Get(profile)->extension_service()->AsWeakPtr(),
+              nullptr,
+              nullptr) {}
+
+    void OnUnpackSuccess(
+        const base::FilePath& temp_dir,
+        const base::FilePath& extension_dir,
+        std::unique_ptr<base::DictionaryValue> original_manifest,
+        const Extension* extension,
+        const SkBitmap& install_icon,
+        const base::Optional<int>& dnr_ruleset_checksum) override {
+      run_loop_.QuitClosure().Run();
+    }
+
+    void WaitForInstallToTrigger() { run_loop_.Run(); }
+
+    void SimulateInstallFailed() {
+      CrxInstallError error(CrxInstallErrorType::DECLINED,
+                            CrxInstallErrorDetail::INSTALL_NOT_ENABLED,
+                            base::ASCIIToUTF16(""));
+      NotifyCrxInstallComplete(error);
+    }
+
+   private:
+    ~FakeCrxInstaller() override = default;
+
+    base::RunLoop run_loop_;
+
+    DISALLOW_COPY_AND_ASSIGN(FakeCrxInstaller);
+  };
+
+  BookmarkAppInstallFinalizerTest() = default;
+  ~BookmarkAppInstallFinalizerTest() override = default;
+
+  void SetUp() override {
+    ChromeRenderViewHostTestHarness::SetUp();
+    // CrxInstaller in BookmarkAppInstallFinalizer needs an ExtensionService, so
+    // create one for the profile.
+    TestExtensionSystem* test_system =
+        static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()));
+    test_system->CreateExtensionService(base::CommandLine::ForCurrentProcess(),
+                                        profile()->GetPath(),
+                                        false /* autoupdate_enabled */);
+  }
+
+  void InstallCallback(base::OnceClosure quit_closure,
+                       const ExtensionId& extension_id) {
+    app_installed_ = !extension_id.empty();
+    std::move(quit_closure).Run();
+  }
+
+  bool app_installed() { return app_installed_.value(); }
+  bool install_callback_called() { return app_installed_.has_value(); }
+
+ private:
+  base::Optional<bool> app_installed_;
+
+  DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallFinalizerTest);
+};
+
+TEST_F(BookmarkAppInstallFinalizerTest, BasicInstallSucceeds) {
+  BookmarkAppInstallFinalizer installer(profile());
+
+  WebApplicationInfo info;
+  info.app_url = GURL(kWebAppUrl);
+  info.title = base::ASCIIToUTF16(kWebAppTitle);
+
+  base::RunLoop run_loop;
+  installer.Install(
+      info, base::BindOnce(&BookmarkAppInstallFinalizerTest::InstallCallback,
+                           base::Unretained(this), run_loop.QuitClosure()));
+  run_loop.Run();
+  EXPECT_TRUE(app_installed());
+}
+
+TEST_F(BookmarkAppInstallFinalizerTest, BasicInstallFails) {
+  BookmarkAppInstallFinalizer installer(profile());
+  auto fake_crx_installer =
+      base::MakeRefCounted<BookmarkAppInstallFinalizerTest::FakeCrxInstaller>(
+          profile());
+  installer.SetCrxInstallerForTesting(fake_crx_installer);
+
+  base::RunLoop run_loop;
+
+  WebApplicationInfo info;
+  info.app_url = GURL(kWebAppUrl);
+  info.title = base::ASCIIToUTF16(kWebAppTitle);
+  installer.Install(
+      info, base::BindOnce(&BookmarkAppInstallFinalizerTest::InstallCallback,
+                           base::Unretained(this), run_loop.QuitClosure()));
+
+  fake_crx_installer->WaitForInstallToTrigger();
+  fake_crx_installer->SimulateInstallFailed();
+  run_loop.Run();
+
+  EXPECT_FALSE(app_installed());
+}
+
+}  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc
index c3f9e0f6..89ccdef 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.cc
@@ -15,7 +15,6 @@
 #include "chrome/browser/ssl/security_state_tab_helper.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_data_retriever.h"
-#include "chrome/browser/web_applications/extensions/bookmark_app_installer.h"
 #include "chrome/common/web_application_info.h"
 #include "content/public/browser/browser_thread.h"
 #include "extensions/common/constants.h"
@@ -60,8 +59,7 @@
     : profile_(profile),
       app_info_(std::move(app_info)),
       helper_factory_(base::BindRepeating(&BookmarkAppHelperCreateWrapper)),
-      data_retriever_(std::make_unique<web_app::WebAppDataRetriever>()),
-      installer_(std::make_unique<BookmarkAppInstaller>(profile)) {}
+      data_retriever_(std::make_unique<web_app::WebAppDataRetriever>()) {}
 
 BookmarkAppInstallationTask::~BookmarkAppInstallationTask() = default;
 
@@ -70,7 +68,7 @@
     ResultCallback callback) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
 
-  data_retriever().GetWebApplicationInfo(
+  data_retriever_->GetWebApplicationInfo(
       web_contents,
       base::BindOnce(&BookmarkAppInstallationTask::OnGetWebApplicationInfo,
                      weak_ptr_factory_.GetWeakPtr(), std::move(callback),
@@ -87,11 +85,6 @@
   data_retriever_ = std::move(data_retriever);
 }
 
-void BookmarkAppInstallationTask::SetInstallerForTesting(
-    std::unique_ptr<BookmarkAppInstaller> installer) {
-  installer_ = std::move(installer);
-}
-
 void BookmarkAppInstallationTask::OnGetWebApplicationInfo(
     ResultCallback result_callback,
     content::WebContents* web_contents,
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.h b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.h
index 983e2b2..978f569 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_installation_task.h
+++ b/chrome/browser/web_applications/extensions/bookmark_app_installation_task.h
@@ -30,7 +30,6 @@
 namespace extensions {
 
 class BookmarkAppHelper;
-class BookmarkAppInstaller;
 class Extension;
 
 // Class to install a BookmarkApp-based Shortcut or WebApp from a WebContents
@@ -79,11 +78,6 @@
       BookmarkAppHelperFactory helper_factory);
   void SetDataRetrieverForTesting(
       std::unique_ptr<web_app::WebAppDataRetriever> data_retriever);
-  void SetInstallerForTesting(std::unique_ptr<BookmarkAppInstaller> installer);
-
- protected:
-  web_app::WebAppDataRetriever& data_retriever() { return *data_retriever_; }
-  BookmarkAppInstaller& installer() { return *installer_; }
 
  private:
   void OnGetWebApplicationInfo(
@@ -104,7 +98,6 @@
   BookmarkAppHelperFactory helper_factory_;
 
   std::unique_ptr<web_app::WebAppDataRetriever> data_retriever_;
-  std::unique_ptr<BookmarkAppInstaller> installer_;
 
   base::WeakPtrFactory<BookmarkAppInstallationTask> weak_ptr_factory_{this};
 
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc
index 2580959..5aa3bbe 100644
--- a/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc
+++ b/chrome/browser/web_applications/extensions/bookmark_app_installation_task_unittest.cc
@@ -23,7 +23,6 @@
 #include "chrome/browser/installable/installable_data.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
 #include "chrome/browser/web_applications/components/web_app_data_retriever.h"
-#include "chrome/browser/web_applications/extensions/bookmark_app_installer.h"
 #include "chrome/browser/web_applications/test/test_data_retriever.h"
 #include "chrome/common/web_application_info.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
@@ -155,30 +154,6 @@
   DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallationTaskTest);
 };
 
-class TestInstaller : public BookmarkAppInstaller {
- public:
-  explicit TestInstaller(Profile* profile, bool succeeds)
-      : BookmarkAppInstaller(profile), succeeds_(succeeds) {}
-
-  ~TestInstaller() override = default;
-
-  void Install(const WebApplicationInfo& web_app_info,
-               ResultCallback callback) override {
-    web_app_info_ = web_app_info;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(
-        FROM_HERE, base::BindOnce(std::move(callback),
-                                  succeeds_ ? "12345" : std::string()));
-  }
-
-  const WebApplicationInfo& web_app_info() { return web_app_info_.value(); }
-
- private:
-  const bool succeeds_;
-  base::Optional<WebApplicationInfo> web_app_info_;
-
-  DISALLOW_COPY_AND_ASSIGN(TestInstaller);
-};
-
 TEST_F(BookmarkAppInstallationTaskTest,
        WebAppOrShortcutFromContents_InstallationSucceeds) {
   const GURL app_url(kWebAppUrl);
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installer.cc b/chrome/browser/web_applications/extensions/bookmark_app_installer.cc
deleted file mode 100644
index 7f911b4..0000000
--- a/chrome/browser/web_applications/extensions/bookmark_app_installer.cc
+++ /dev/null
@@ -1,55 +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/web_applications/extensions/bookmark_app_installer.h"
-
-#include <utility>
-
-#include "base/bind.h"
-#include "base/callback.h"
-#include "chrome/browser/extensions/crx_installer.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
-#include "chrome/common/web_application_info.h"
-#include "extensions/common/extension.h"
-#include "url/gurl.h"
-
-namespace extensions {
-
-BookmarkAppInstaller::BookmarkAppInstaller(Profile* profile)
-    : crx_installer_(CrxInstaller::CreateSilent(
-          ExtensionSystem::Get(profile)->extension_service())) {}
-
-BookmarkAppInstaller::~BookmarkAppInstaller() = default;
-
-void BookmarkAppInstaller::Install(const WebApplicationInfo& web_app_info,
-                                   ResultCallback callback) {
-  crx_installer_->set_installer_callback(base::BindOnce(
-      &BookmarkAppInstaller::OnInstall, weak_ptr_factory_.GetWeakPtr(),
-      std::move(callback), web_app_info.app_url));
-  crx_installer_->InstallWebApp(web_app_info);
-}
-
-void BookmarkAppInstaller::SetCrxInstallerForTesting(
-    scoped_refptr<CrxInstaller> crx_installer) {
-  crx_installer_ = crx_installer;
-}
-
-void BookmarkAppInstaller::OnInstall(
-    ResultCallback callback,
-    const GURL& app_url,
-    const base::Optional<CrxInstallError>& error) {
-  if (error) {
-    std::move(callback).Run(std::string());
-    return;
-  }
-
-  auto* installed_extension = crx_installer_->extension();
-  DCHECK(installed_extension);
-  DCHECK_EQ(AppLaunchInfo::GetLaunchWebURL(installed_extension), app_url);
-
-  std::move(callback).Run(installed_extension->id());
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installer.h b/chrome/browser/web_applications/extensions/bookmark_app_installer.h
deleted file mode 100644
index a9334c4..0000000
--- a/chrome/browser/web_applications/extensions/bookmark_app_installer.h
+++ /dev/null
@@ -1,59 +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_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_INSTALLER_H_
-#define CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_INSTALLER_H_
-
-#include "base/callback_forward.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/optional.h"
-#include "extensions/common/extension_id.h"
-
-class GURL;
-class Profile;
-
-struct WebApplicationInfo;
-
-namespace extensions {
-
-class CrxInstaller;
-class CrxInstallError;
-
-// Class used by BookmarkAppInstallationTask to actually install the Bookmark
-// App in the system.
-class BookmarkAppInstaller {
- public:
-  using ResultCallback = base::OnceCallback<void(const ExtensionId&)>;
-
-  // Constructs a BookmarkAppInstaller that will install the Bookmark App in
-  // |profile|.
-  explicit BookmarkAppInstaller(Profile* profile);
-  virtual ~BookmarkAppInstaller();
-
-  // TODO(crbug.com/864904): This should take more options e.g. what container
-  // to launch the app in, should the app sync, etc.
-  virtual void Install(const WebApplicationInfo& web_app_info,
-                       ResultCallback callback);
-
-  void SetCrxInstallerForTesting(scoped_refptr<CrxInstaller> crx_installer);
-
- private:
-  void OnInstall(ResultCallback callback,
-                 const GURL& app_url,
-                 const base::Optional<CrxInstallError>& error);
-
-  scoped_refptr<CrxInstaller> crx_installer_;
-
-  // We need a WeakPtr because CrxInstaller is refcounted and it can run its
-  // callback after this class has been destroyed.
-  base::WeakPtrFactory<BookmarkAppInstaller> weak_ptr_factory_{this};
-
-  DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstaller);
-};
-
-}  // namespace extensions
-
-#endif  // CHROME_BROWSER_WEB_APPLICATIONS_EXTENSIONS_BOOKMARK_APP_INSTALLER_H_
diff --git a/chrome/browser/web_applications/extensions/bookmark_app_installer_unittest.cc b/chrome/browser/web_applications/extensions/bookmark_app_installer_unittest.cc
deleted file mode 100644
index 915b6b2f..0000000
--- a/chrome/browser/web_applications/extensions/bookmark_app_installer_unittest.cc
+++ /dev/null
@@ -1,139 +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/web_applications/extensions/bookmark_app_installer.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/run_loop.h"
-#include "base/strings/utf_string_conversions.h"
-#include "chrome/browser/extensions/crx_installer.h"
-#include "chrome/browser/extensions/test_extension_system.h"
-#include "chrome/common/web_application_info.h"
-#include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "chrome/test/base/testing_profile.h"
-#include "extensions/browser/install/crx_install_error.h"
-#include "extensions/common/extension_id.h"
-
-namespace extensions {
-
-namespace {
-
-const char kWebAppUrl[] = "https://foo.example";
-const char kWebAppTitle[] = "Foo Title";
-
-}  // namespace
-
-class BookmarkAppInstallerTest : public ChromeRenderViewHostTestHarness {
- public:
-  // Subclass that runs a closure when an extension is unpacked successfully.
-  // Useful for tests that want to trigger their own succeess/failure events.
-  class FakeCrxInstaller : public CrxInstaller {
-   public:
-    explicit FakeCrxInstaller(Profile* profile)
-        : CrxInstaller(
-              ExtensionSystem::Get(profile)->extension_service()->AsWeakPtr(),
-              nullptr,
-              nullptr) {
-    }
-
-    void OnUnpackSuccess(
-        const base::FilePath& temp_dir,
-        const base::FilePath& extension_dir,
-        std::unique_ptr<base::DictionaryValue> original_manifest,
-        const Extension* extension,
-        const SkBitmap& install_icon,
-        const base::Optional<int>& dnr_ruleset_checksum) override {
-      run_loop_.QuitClosure().Run();
-    }
-
-    void WaitForInstallToTrigger() { run_loop_.Run(); }
-
-    void SimulateInstallFailed() {
-      CrxInstallError error(CrxInstallErrorType::DECLINED,
-                            CrxInstallErrorDetail::INSTALL_NOT_ENABLED,
-                            base::ASCIIToUTF16(""));
-      NotifyCrxInstallComplete(error);
-    }
-
-   private:
-    ~FakeCrxInstaller() override = default;
-
-    base::RunLoop run_loop_;
-
-    DISALLOW_COPY_AND_ASSIGN(FakeCrxInstaller);
-  };
-
-  BookmarkAppInstallerTest() = default;
-  ~BookmarkAppInstallerTest() override = default;
-
-  void SetUp() override {
-    ChromeRenderViewHostTestHarness::SetUp();
-    // CrxInstaller in BookmarkAppInstaller needs an ExtensionService, so
-    // create one for the profile.
-    TestExtensionSystem* test_system =
-        static_cast<TestExtensionSystem*>(ExtensionSystem::Get(profile()));
-    test_system->CreateExtensionService(base::CommandLine::ForCurrentProcess(),
-                                        profile()->GetPath(),
-                                        false /* autoupdate_enabled */);
-  }
-
-  void InstallCallback(base::OnceClosure quit_closure,
-                       const ExtensionId& extension_id) {
-    app_installed_ = !extension_id.empty();
-    std::move(quit_closure).Run();
-  }
-
-  bool app_installed() { return app_installed_.value(); }
-  bool install_callback_called() { return app_installed_.has_value(); }
-
- private:
-  base::Optional<bool> app_installed_;
-
-  DISALLOW_COPY_AND_ASSIGN(BookmarkAppInstallerTest);
-};
-
-TEST_F(BookmarkAppInstallerTest, BasicInstallSucceeds) {
-  BookmarkAppInstaller installer(profile());
-
-  WebApplicationInfo info;
-  info.app_url = GURL(kWebAppUrl);
-  info.title = base::ASCIIToUTF16(kWebAppTitle);
-
-  base::RunLoop run_loop;
-  installer.Install(
-      info, base::BindOnce(&BookmarkAppInstallerTest::InstallCallback,
-                           base::Unretained(this), run_loop.QuitClosure()));
-  run_loop.Run();
-  EXPECT_TRUE(app_installed());
-}
-
-TEST_F(BookmarkAppInstallerTest, BasicInstallFails) {
-  BookmarkAppInstaller installer(profile());
-  auto fake_crx_installer =
-      base::MakeRefCounted<BookmarkAppInstallerTest::FakeCrxInstaller>(
-          profile());
-  installer.SetCrxInstallerForTesting(fake_crx_installer);
-
-  base::RunLoop run_loop;
-
-  WebApplicationInfo info;
-  info.app_url = GURL(kWebAppUrl);
-  info.title = base::ASCIIToUTF16(kWebAppTitle);
-  installer.Install(
-      info, base::BindOnce(&BookmarkAppInstallerTest::InstallCallback,
-                           base::Unretained(this), run_loop.QuitClosure()));
-
-  fake_crx_installer->WaitForInstallToTrigger();
-  fake_crx_installer->SimulateInstallFailed();
-  run_loop.Run();
-
-  EXPECT_FALSE(app_installed());
-}
-
-}  // namespace extensions
diff --git a/chrome/browser/web_applications/system_web_app_manager.cc b/chrome/browser/web_applications/system_web_app_manager.cc
index 52532435..17d3957 100644
--- a/chrome/browser/web_applications/system_web_app_manager.cc
+++ b/chrome/browser/web_applications/system_web_app_manager.cc
@@ -12,7 +12,6 @@
 #include "base/task/post_task.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/web_applications/components/web_app_constants.h"
-#include "chrome/browser/web_applications/web_app_utils.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/webui_url_constants.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -37,7 +36,7 @@
 
 SystemWebAppManager::SystemWebAppManager(Profile* profile,
                                          PendingAppManager* pending_app_manager)
-    : profile_(profile), pending_app_manager_(pending_app_manager) {}
+    : pending_app_manager_(pending_app_manager) {}
 
 SystemWebAppManager::~SystemWebAppManager() = default;
 
@@ -57,7 +56,7 @@
 
 void SystemWebAppManager::StartAppInstallation() {
   std::vector<GURL> urls_to_install;
-  if (AreWebAppsEnabled(profile_) && IsEnabled()) {
+  if (IsEnabled()) {
     // Skipping this will uninstall all System Apps currently installed.
     urls_to_install = CreateSystemWebApps();
   }
diff --git a/chrome/browser/web_applications/system_web_app_manager.h b/chrome/browser/web_applications/system_web_app_manager.h
index 9da5b83..f7d4b7f 100644
--- a/chrome/browser/web_applications/system_web_app_manager.h
+++ b/chrome/browser/web_applications/system_web_app_manager.h
@@ -36,8 +36,6 @@
  private:
   void StartAppInstallation();
 
-  Profile* profile_;
-
   // Used to install, uninstall, and update apps. Should outlive this class.
   PendingAppManager* pending_app_manager_;
 
diff --git a/chrome/browser/web_applications/web_app_database_unittest.cc b/chrome/browser/web_applications/web_app_database_unittest.cc
index 0672a79..01fac60 100644
--- a/chrome/browser/web_applications/web_app_database_unittest.cc
+++ b/chrome/browser/web_applications/web_app_database_unittest.cc
@@ -78,11 +78,13 @@
 
   static std::unique_ptr<WebApp> CreateWebApp(const std::string& base_url,
                                               int suffix) {
-    const auto launch_url = base_url + base::IntToString(suffix);
+    const auto launch_url = base_url + base::NumberToString(suffix);
     const AppId app_id = GenerateAppIdFromURL(GURL(launch_url));
-    const std::string name = "Name" + base::IntToString(suffix);
-    const std::string description = "Description" + base::IntToString(suffix);
-    const std::string scope = base_url + "/scope" + base::IntToString(suffix);
+    const std::string name = "Name" + base::NumberToString(suffix);
+    const std::string description =
+        "Description" + base::NumberToString(suffix);
+    const std::string scope =
+        base_url + "/scope" + base::NumberToString(suffix);
     const base::Optional<SkColor> theme_color = suffix;
 
     auto app = std::make_unique<WebApp>(app_id);
@@ -93,7 +95,8 @@
     app->SetScope(GURL(scope));
     app->SetThemeColor(theme_color);
 
-    const std::string icon_url = base_url + "/icon" + base::IntToString(suffix);
+    const std::string icon_url =
+        base_url + "/icon" + base::NumberToString(suffix);
     const int icon_size_in_px = 256;
 
     WebApp::Icons icons;
@@ -247,7 +250,7 @@
   WebApp::Icons icons;
   for (int i = 1; i <= num_icons; ++i) {
     const std::string icon_url =
-        base_url + "/icon" + base::IntToString(num_icons);
+        base_url + "/icon" + base::NumberToString(num_icons);
     // Let size equals the icon's number squared.
     const int icon_size_in_px = i * i;
     icons.push_back({GURL(icon_url), icon_size_in_px});
diff --git a/chrome/browser/web_applications/web_app_provider.cc b/chrome/browser/web_applications/web_app_provider.cc
index 2f85901..b8c20171e 100644
--- a/chrome/browser/web_applications/web_app_provider.cc
+++ b/chrome/browser/web_applications/web_app_provider.cc
@@ -178,26 +178,6 @@
                                             base::DoNothing());
 }
 
-void WebAppProvider::Reset() {
-  // TODO(loyso): Make it independent to the order of destruction via using two
-  // end-to-end passes:
-  // 1) Do Reset() for each subsystem to nullify pointers (detach subsystems).
-  // 2) Destroy subsystems.
-
-  // PendingAppManager is used by WebAppPolicyManager and therefore should be
-  // deleted after it.
-  web_app_policy_manager_.reset();
-  system_web_app_manager_.reset();
-  pending_app_manager_.reset();
-
-  install_manager_.reset();
-  icon_manager_.reset();
-  registrar_.reset();
-  database_.reset();
-  database_factory_.reset();
-  audio_focus_id_map_.reset();
-}
-
 void WebAppProvider::Observe(int type,
                              const content::NotificationSource& source,
                              const content::NotificationDetails& detals) {
@@ -228,6 +208,26 @@
   return extensions::CountUserInstalledBookmarkApps(profile_);
 }
 
+void WebAppProvider::Reset() {
+  // TODO(loyso): Make it independent to the order of destruction via using two
+  // end-to-end passes:
+  // 1) Do Reset() for each subsystem to nullify pointers (detach subsystems).
+  // 2) Destroy subsystems.
+
+  // PendingAppManager is used by WebAppPolicyManager and therefore should be
+  // deleted after it.
+  web_app_policy_manager_.reset();
+  system_web_app_manager_.reset();
+  pending_app_manager_.reset();
+
+  install_manager_.reset();
+  icon_manager_.reset();
+  registrar_.reset();
+  database_.reset();
+  database_factory_.reset();
+  audio_focus_id_map_.reset();
+}
+
 void WebAppProvider::OnScanForExternalWebApps(
     std::vector<web_app::PendingAppManager::AppInfo> app_infos) {
   pending_app_manager_->SynchronizeInstalledApps(
diff --git a/chrome/browser/web_applications/web_app_provider.h b/chrome/browser/web_applications/web_app_provider.h
index ce13117..0898332 100644
--- a/chrome/browser/web_applications/web_app_provider.h
+++ b/chrome/browser/web_applications/web_app_provider.h
@@ -77,8 +77,6 @@
   static void InstallWebApp(content::WebContents* web_contents,
                             bool force_shortcut_app);
 
-  void Reset();
-
   // content::NotificationObserver
   void Observe(int type,
                const content::NotificationSource& source,
@@ -101,6 +99,8 @@
 
   void OnRegistryReady();
 
+  void Reset();
+
   void OnScanForExternalWebApps(
       std::vector<web_app::PendingAppManager::AppInfo>);
 
diff --git a/chrome/browser/web_applications/web_app_registrar_unittest.cc b/chrome/browser/web_applications/web_app_registrar_unittest.cc
index bcce3810..dceda363 100644
--- a/chrome/browser/web_applications/web_app_registrar_unittest.cc
+++ b/chrome/browser/web_applications/web_app_registrar_unittest.cc
@@ -22,7 +22,7 @@
   Registry registry;
 
   for (int i = 0; i < num_apps; ++i) {
-    const auto url = base_url + base::IntToString(i);
+    const auto url = base_url + base::NumberToString(i);
     const AppId app_id = GenerateAppIdFromURL(GURL(url));
     auto web_app = std::make_unique<WebApp>(app_id);
     registry.emplace(app_id, std::move(web_app));
diff --git a/chrome/browser/win/chrome_process_finder.cc b/chrome/browser/win/chrome_process_finder.cc
index 81f2394c..a6892db 100644
--- a/chrome/browser/win/chrome_process_finder.cc
+++ b/chrome/browser/win/chrome_process_finder.cc
@@ -46,7 +46,7 @@
   base::CommandLine command_line(*base::CommandLine::ForCurrentProcess());
   command_line.AppendSwitchASCII(
       switches::kOriginalProcessStartTime,
-      base::Int64ToString(
+      base::NumberToString(
           base::Process::Current().CreationTime().ToInternalValue()));
 
   if (fast_start)
diff --git a/chrome/browser/win/ui_automation_util.cc b/chrome/browser/win/ui_automation_util.cc
index 28c3037..5a15738 100644
--- a/chrome/browser/win/ui_automation_util.cc
+++ b/chrome/browser/win/ui_automation_util.cc
@@ -111,7 +111,8 @@
 #if DCHECK_IS_ON()
   std::vector<std::string> value_strings;
   std::transform(values.begin(), values.end(),
-                 std::back_inserter(value_strings), &base::IntToString);
+                 std::back_inserter(value_strings),
+                 [](int32_t value) { return base::NumberToString(value); });
   return base::JoinString(value_strings, ", ");
 #else   // DCHECK_IS_ON()
   return std::string();
diff --git a/chrome/chrome_cleaner/http/internet_helpers.cc b/chrome/chrome_cleaner/http/internet_helpers.cc
index 8e73bc6..edd7ba7 100644
--- a/chrome/chrome_cleaner/http/internet_helpers.cc
+++ b/chrome/chrome_cleaner/http/internet_helpers.cc
@@ -194,11 +194,11 @@
   if (secure) {
     if (port == 443)
       return L"https://" + host + path;
-    return L"https://" + host + L':' + base::UintToString16(port) + path;
+    return L"https://" + host + L':' + base::NumberToString16(port) + path;
   }
   if (port == 80)
     return L"http://" + host + path;
-  return L"http://" + host + L':' + base::UintToString16(port) + path;
+  return L"http://" + host + L':' + base::NumberToString16(port) + path;
 }
 
 base::string16 GenerateMultipartHttpRequestBoundary() {
diff --git a/chrome/chrome_cleaner/http/user_agent.cc b/chrome/chrome_cleaner/http/user_agent.cc
index 4884e8f..48df348 100644
--- a/chrome/chrome_cleaner/http/user_agent.cc
+++ b/chrome/chrome_cleaner/http/user_agent.cc
@@ -41,8 +41,8 @@
 
 base::string16 UserAgent::AsString() {
   return product_name_ + L"/" + product_version_ + L" (Windows NT " +
-         base::IntToString16(os_major_version_) + L"." +
-         base::IntToString16(os_minor_version_) +
+         base::NumberToString16(os_major_version_) + L"." +
+         base::NumberToString16(os_minor_version_) +
          ArchitectureToString(architecture_) + L") WinHTTP/" + winhttp_version_;
 }
 
diff --git a/chrome/chrome_cleaner/ipc/chrome_prompt_ipc_unittest.cc b/chrome/chrome_cleaner/ipc/chrome_prompt_ipc_unittest.cc
index c07bd67..8fb7cdfd 100644
--- a/chrome/chrome_cleaner/ipc/chrome_prompt_ipc_unittest.cc
+++ b/chrome/chrome_cleaner/ipc/chrome_prompt_ipc_unittest.cc
@@ -152,10 +152,10 @@
     if (test_config.with_registry_keys)
       AppendSwitch(kIncludeRegistryKeysSwitch);
     AppendSwitch(kExpectedPromptResultSwitch,
-                 base::IntToString(
+                 base::NumberToString(
                      static_cast<int>(test_config.expected_prompt_acceptance)));
     AppendSwitch(kExpectedParentDisconnectedSwitch,
-                 base::IntToString(static_cast<int>(
+                 base::NumberToString(static_cast<int>(
                      test_config.expected_parent_disconnected)));
   }
 
diff --git a/chrome/chrome_cleaner/ipc/sandbox.cc b/chrome/chrome_cleaner/ipc/sandbox.cc
index 87265db..3776dee 100644
--- a/chrome/chrome_cleaner/ipc/sandbox.cc
+++ b/chrome/chrome_cleaner/ipc/sandbox.cc
@@ -207,7 +207,7 @@
                                           GetCrashReporterIPCPipeName());
 
   sandbox_command_line.AppendSwitchASCII(
-      kSandboxedProcessIdSwitch, base::IntToString(static_cast<int>(type)));
+      kSandboxedProcessIdSwitch, base::NumberToString(static_cast<int>(type)));
 
   return StartSandboxTarget(sandbox_command_line, setup_hooks, type);
 }
@@ -256,7 +256,7 @@
           base::WaitableEvent::InitialState::NOT_SIGNALED);
   command_line.AppendSwitchNative(
       chrome_cleaner::kInitDoneNotifierSwitch,
-      base::UintToString16(
+      base::NumberToString16(
           base::win::HandleToUint32(init_done_event->handle())));
   policy->AddHandleToShare(init_done_event->handle());
 
diff --git a/chrome/chrome_cleaner/logging/cleaner_logging_service_unittest.cc b/chrome/chrome_cleaner/logging/cleaner_logging_service_unittest.cc
index 31b990c..52db7308 100644
--- a/chrome/chrome_cleaner/logging/cleaner_logging_service_unittest.cc
+++ b/chrome/chrome_cleaner/logging/cleaner_logging_service_unittest.cc
@@ -389,7 +389,7 @@
        flag_value <= ChromeCleanerReport::CleanerStartup_MAX; ++flag_value) {
     command_line.InitFromArgv(command_line.argv());
     command_line.AppendSwitchASCII(kChromePromptSwitch,
-                                   base::IntToString(flag_value));
+                                   base::NumberToString(flag_value));
     if (flag_value == ChromeCleanerReport::CLEANER_STARTUP_UNSPECIFIED ||
         flag_value == ChromeCleanerReport::CLEANER_STARTUP_NOT_PROMPTED) {
       EXPECT_EQ(ChromeCleanerReport::CLEANER_STARTUP_UNKNOWN,
diff --git a/chrome/chrome_cleaner/logging/interface_log_service_unittest.cc b/chrome/chrome_cleaner/logging/interface_log_service_unittest.cc
index e8edb64..ed37d4b0 100644
--- a/chrome/chrome_cleaner/logging/interface_log_service_unittest.cc
+++ b/chrome/chrome_cleaner/logging/interface_log_service_unittest.cc
@@ -95,7 +95,7 @@
   void function1(std::string parameter1, int32_t parameter2) {
     std::map<std::string, std::string> params;
     params["parameter1"] = parameter1;
-    std::string s_parameter2 = base::IntToString(parameter2);
+    std::string s_parameter2 = base::NumberToString(parameter2);
     params["parameter2"] = s_parameter2;
     log_service_->AddCall(CURRENT_FILE_AND_METHOD, params);
   }
@@ -239,7 +239,7 @@
   EXPECT_EQ(call_record[0].file_name(), kFileName);
   EXPECT_EQ(2U, call_record[0].parameters().size());
   EXPECT_EQ(kString1, call_record[0].parameters().at("parameter1"));
-  EXPECT_EQ(base::IntToString(kInt1),
+  EXPECT_EQ(base::NumberToString(kInt1),
             call_record[0].parameters().at("parameter2"));
 
   EXPECT_EQ(call_record[1].function_name(), "function3");
@@ -248,7 +248,7 @@
   EXPECT_EQ(call_record[2].function_name(), "function1");
   EXPECT_EQ(call_record[2].file_name(), kFileName);
   EXPECT_EQ(kString2, call_record[2].parameters().at("parameter1"));
-  EXPECT_EQ(base::IntToString(kInt2),
+  EXPECT_EQ(base::NumberToString(kInt2),
             call_record[2].parameters().at("parameter2"));
 
   EXPECT_EQ(call_record[3].function_name(), "function2");
@@ -274,13 +274,13 @@
   parameters1.insert(call_record[0].parameters().begin(),
                      call_record[0].parameters().end());
   EXPECT_EQ(parameters1["parameter1"], kString1);
-  EXPECT_EQ(parameters1["parameter2"], base::IntToString(kInt1));
+  EXPECT_EQ(parameters1["parameter2"], base::NumberToString(kInt1));
 
   std::map<std::string, std::string> parameters2;
   parameters2.insert(call_record[2].parameters().begin(),
                      call_record[2].parameters().end());
   EXPECT_EQ(parameters2["parameter1"], kString2);
-  EXPECT_EQ(parameters2["parameter2"], base::IntToString(kInt2));
+  EXPECT_EQ(parameters2["parameter2"], base::NumberToString(kInt2));
 }
 
 TEST_F(InterfaceLogServiceTest, EmptyLogFileTest) {
diff --git a/chrome/chrome_cleaner/logging/message_builder.cc b/chrome/chrome_cleaner/logging/message_builder.cc
index cb70ae5..398641e4 100644
--- a/chrome/chrome_cleaner/logging/message_builder.cc
+++ b/chrome/chrome_cleaner/logging/message_builder.cc
@@ -18,7 +18,7 @@
     : value_(base::UTF8ToUTF16(value.as_string())) {}
 
 MessageBuilder::MessageItem::MessageItem(int value)
-    : value_(base::IntToString16(value)) {}
+    : value_(base::NumberToString16(value)) {}
 
 MessageBuilder::ScopedIndent::ScopedIndent(MessageBuilder* builder)
     : builder_(builder) {
diff --git a/chrome/chrome_cleaner/logging/registry_logger.cc b/chrome/chrome_cleaner/logging/registry_logger.cc
index b57acbc..0764588c 100644
--- a/chrome/chrome_cleaner/logging/registry_logger.cc
+++ b/chrome/chrome_cleaner/logging/registry_logger.cc
@@ -158,7 +158,7 @@
     return;
 
   int64_t scan_time_serialized = scan_time.InMicroseconds();
-  scan_times_key_.WriteValue(base::UintToString16(pup_id).c_str(),
+  scan_times_key_.WriteValue(base::NumberToString16(pup_id).c_str(),
                              &scan_time_serialized,
                              sizeof(scan_time_serialized), REG_QWORD);
 }
@@ -316,7 +316,7 @@
 bool RegistryLogger::RecordFoundPUPs(const std::vector<UwSId>& pups_to_store) {
   base::string16 multi_sz_value;
   for (UwSId pup_to_store : pups_to_store) {
-    multi_sz_value += base::UintToString16(pup_to_store);
+    multi_sz_value += base::NumberToString16(pup_to_store);
     multi_sz_value += kMultiSzSeparator;
   }
   multi_sz_value += kMultiSzSeparator;
diff --git a/chrome/chrome_cleaner/os/disk_util.cc b/chrome/chrome_cleaner/os/disk_util.cc
index 6461a78..5c9a543 100644
--- a/chrome/chrome_cleaner/os/disk_util.cc
+++ b/chrome/chrome_cleaner/os/disk_util.cc
@@ -477,7 +477,7 @@
   AppendFileInformationField(
       L"digest", base::UTF8ToUTF16(file_information.sha256), &content);
   AppendFileInformationField(
-      L"size", base::Int64ToString16(file_information.size), &content);
+      L"size", base::NumberToString16(file_information.size), &content);
   AppendFileInformationField(L"company_name", file_information.company_name,
                              &content);
   AppendFileInformationField(L"company_short_name",
diff --git a/chrome/chrome_cleaner/settings/cleaner_settings_unittest.cc b/chrome/chrome_cleaner/settings/cleaner_settings_unittest.cc
index 9219991..224c6fa 100644
--- a/chrome/chrome_cleaner/settings/cleaner_settings_unittest.cc
+++ b/chrome/chrome_cleaner/settings/cleaner_settings_unittest.cc
@@ -47,7 +47,7 @@
   if (execution_mode_ != ExecutionMode::kNone) {
     command_line.AppendSwitchASCII(
         kExecutionModeSwitch,
-        base::IntToString(static_cast<int>(execution_mode_)));
+        base::NumberToString(static_cast<int>(execution_mode_)));
   }
   if (with_scanning_mode_logs_)
     command_line.AppendSwitch(kWithScanningModeLogsSwitch);
diff --git a/chrome/chrome_cleaner/settings/settings_unittest.cc b/chrome/chrome_cleaner/settings/settings_unittest.cc
index ebc2c02..e749606 100644
--- a/chrome/chrome_cleaner/settings/settings_unittest.cc
+++ b/chrome/chrome_cleaner/settings/settings_unittest.cc
@@ -48,7 +48,7 @@
   for (int index = Engine::Name_MIN; index < Engine::Name_MAX; ++index) {
     base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
     command_line.AppendSwitchASCII(chrome_cleaner::kEngineSwitch,
-                                   base::IntToString(index));
+                                   base::NumberToString(index));
     Settings* settings = ReinitializeSettings(command_line);
     if (index != Engine::UNKNOWN)
       EXPECT_EQ(static_cast<Engine::Name>(index), settings->engine());
@@ -60,7 +60,7 @@
 TEST_F(SettingsTest, EngineInvalidNumericValue) {
   base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
   command_line.AppendSwitchASCII(chrome_cleaner::kEngineSwitch,
-                                 base::IntToString(kInvalidEngineValue));
+                                 base::NumberToString(kInvalidEngineValue));
   Settings* settings = ReinitializeSettings(command_line);
   EXPECT_EQ(Engine::URZA, settings->engine());
 }
@@ -113,16 +113,16 @@
       // Switch not present.
       {"", ExecutionMode::kNone},
       // Valid values for execution mode.
-      {base::IntToString(static_cast<int>(ExecutionMode::kScanning)),
+      {base::NumberToString(static_cast<int>(ExecutionMode::kScanning)),
        ExecutionMode::kScanning},
-      {base::IntToString(static_cast<int>(ExecutionMode::kCleanup)),
+      {base::NumberToString(static_cast<int>(ExecutionMode::kCleanup)),
        ExecutionMode::kCleanup},
       // Unknown values for execution mode.
-      {base::IntToString(static_cast<int>(ExecutionMode::kNumValues)),
+      {base::NumberToString(static_cast<int>(ExecutionMode::kNumValues)),
        ExecutionMode::kNone},
-      {base::IntToString(static_cast<int>(ExecutionMode::kNumValues) + 1),
+      {base::NumberToString(static_cast<int>(ExecutionMode::kNumValues) + 1),
        ExecutionMode::kNone},
-      {base::IntToString(static_cast<int>(ExecutionMode::kNone) - 1),
+      {base::NumberToString(static_cast<int>(ExecutionMode::kNone) - 1),
        ExecutionMode::kNone},
       {"invalid-value", ExecutionMode::kNone},
   };
diff --git a/chrome/chrome_cleaner/test/test_executables.cc b/chrome/chrome_cleaner/test/test_executables.cc
index 4524356..1c56c1d7 100644
--- a/chrome/chrome_cleaner/test/test_executables.cc
+++ b/chrome/chrome_cleaner/test/test_executables.cc
@@ -44,7 +44,7 @@
           base::WaitableEvent::InitialState::NOT_SIGNALED);
   command_line.AppendSwitchNative(
       chrome_cleaner::kInitDoneNotifierSwitch,
-      base::UintToString16(
+      base::NumberToString16(
           base::win::HandleToUint32(init_done_event->handle())));
 
   if (cmd)
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index e0eefd2..0c8d732 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -493,6 +493,10 @@
 const base::Feature kPushMessagingBackgroundMode{
     "PushMessagingBackgroundMode", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enables Load image context menu item for broken images.
+const base::Feature kLoadBrokenImagesFromContextMenu{
+    "LoadBrokenImagesFromContextMenu", base::FEATURE_DISABLED_BY_DEFAULT};
+
 const base::Feature kSafeSearchUrlReporting{"SafeSearchUrlReporting",
                                             base::FEATURE_DISABLED_BY_DEFAULT};
 
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index f434cbe..8ba9f12 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -329,6 +329,9 @@
 extern const base::Feature kPushMessagingBackgroundMode;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kLoadBrokenImagesFromContextMenu;
+
+COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kSafeSearchUrlReporting;
 
 COMPONENT_EXPORT(CHROME_FEATURES)
diff --git a/chrome/common/extensions/api/commands/commands_handler.cc b/chrome/common/extensions/api/commands/commands_handler.cc
index 6de8029..d8d5b680 100644
--- a/chrome/common/extensions/api/commands/commands_handler.cc
+++ b/chrome/common/extensions/api/commands/commands_handler.cc
@@ -86,7 +86,7 @@
     if (!iter.value().GetAsDictionary(&command)) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
           manifest_errors::kInvalidKeyBindingDictionary,
-          base::IntToString(command_index));
+          base::NumberToString(command_index));
       return false;
     }
 
@@ -106,7 +106,7 @@
               extension, APIPermission::kCommandsAccessibility)) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             manifest_errors::kInvalidKeyBindingTooMany,
-            base::IntToString(kMaxCommandsWithKeybindingPerExtension));
+            base::NumberToString(kMaxCommandsWithKeybindingPerExtension));
         return false;
       }
     }
diff --git a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
index 6a1c71c..78e0c322 100644
--- a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
+++ b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler.cc
@@ -177,7 +177,7 @@
       if (!access_list_value->GetString(i, &access) ||
           result->AddFileAccessPermission(access)) {
         *error = extensions::ErrorUtils::FormatErrorMessageUTF16(
-            errors::kInvalidFileAccessValue, base::IntToString(i));
+            errors::kInvalidFileAccessValue, base::NumberToString(i));
         return nullptr;
       }
     }
@@ -200,7 +200,7 @@
       std::string filter;
       if (!file_filters->GetString(i, &filter)) {
         *error = extensions::ErrorUtils::FormatErrorMessageUTF16(
-            errors::kInvalidFileFilterValue, base::IntToString(i));
+            errors::kInvalidFileFilterValue, base::NumberToString(i));
         return nullptr;
       }
       filter = base::ToLowerASCII(filter);
diff --git a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc
index 9145bb5..bc0f6fe 100644
--- a/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc
+++ b/chrome/common/extensions/api/file_browser_handlers/file_browser_handler_manifest_unittest.cc
@@ -73,7 +73,7 @@
   Testcase testcases[] = {
       Testcase("filebrowser_invalid_access_permission.json",
                extensions::ErrorUtils::FormatErrorMessage(
-                   errors::kInvalidFileAccessValue, base::IntToString(1))),
+                   errors::kInvalidFileAccessValue, base::NumberToString(1))),
       Testcase("filebrowser_invalid_access_permission_list.json",
                errors::kInvalidFileAccessList),
       Testcase("filebrowser_invalid_empty_access_permission_list.json",
@@ -92,7 +92,7 @@
                errors::kInvalidFileFiltersList),
       Testcase("filebrowser_invalid_file_filters_2.json",
                extensions::ErrorUtils::FormatErrorMessage(
-                   errors::kInvalidFileFilterValue, base::IntToString(0))),
+                   errors::kInvalidFileFilterValue, base::NumberToString(0))),
       Testcase("filebrowser_invalid_file_filters_url.json",
                extensions::ErrorUtils::FormatErrorMessage(
                    errors::kInvalidURLPatternError, "http:*.html"))};
diff --git a/chrome/common/extensions/api/input_ime/input_components_handler.cc b/chrome/common/extensions/api/input_ime/input_components_handler.cc
index 177e8eb..c8e6c7f 100644
--- a/chrome/common/extensions/api/input_ime/input_components_handler.cc
+++ b/chrome/common/extensions/api/input_ime/input_components_handler.cc
@@ -83,8 +83,7 @@
     // Get input_components[i].name.
     if (!module_value->GetString(keys::kName, &name_str)) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
-          errors::kInvalidInputComponentName,
-          base::IntToString(i));
+          errors::kInvalidInputComponentName, base::NumberToString(i));
       return false;
     }
 
@@ -95,14 +94,12 @@
         type = INPUT_COMPONENT_TYPE_IME;
       } else {
         *error = ErrorUtils::FormatErrorMessageUTF16(
-            errors::kInvalidInputComponentType,
-            base::IntToString(i));
+            errors::kInvalidInputComponentType, base::NumberToString(i));
         return false;
       }
     } else {
       *error = ErrorUtils::FormatErrorMessageUTF16(
-          errors::kInvalidInputComponentType,
-          base::IntToString(i));
+          errors::kInvalidInputComponentType, base::NumberToString(i));
       return false;
     }
 
@@ -114,8 +111,7 @@
     // Get input_components[i].description.
     if (!module_value->GetString(keys::kDescription, &description_str)) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
-          errors::kInvalidInputComponentDescription,
-          base::IntToString(i));
+          errors::kInvalidInputComponentDescription, base::NumberToString(i));
       return false;
     }
 
@@ -146,8 +142,8 @@
         std::string layout_name_str;
         if (!layouts_value->GetString(j, &layout_name_str)) {
           *error = ErrorUtils::FormatErrorMessageUTF16(
-              errors::kInvalidInputComponentLayoutName,
-              base::IntToString(i), base::IntToString(j));
+              errors::kInvalidInputComponentLayoutName, base::NumberToString(i),
+              base::NumberToString(j));
           return false;
         }
         layouts.insert(layout_name_str);
@@ -159,8 +155,7 @@
       if (!module_value->GetDictionary(keys::kShortcutKey,
           &shortcut_value)) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
-            errors::kInvalidInputComponentShortcutKey,
-            base::IntToString(i));
+            errors::kInvalidInputComponentShortcutKey, base::NumberToString(i));
         return false;
       }
 
@@ -168,7 +163,7 @@
       if (!shortcut_value->GetString(keys::kKeycode, &shortcut_keycode_str)) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
             errors::kInvalidInputComponentShortcutKeycode,
-            base::IntToString(i));
+            base::NumberToString(i));
         return false;
       }
 
@@ -194,9 +189,8 @@
     if (module_value->GetString(keys::kInputView, &input_view_str)) {
       input_view_url = extension->GetResourceURL(input_view_str);
       if (!input_view_url.is_valid()) {
-        *error = ErrorUtils::FormatErrorMessageUTF16(
-            errors::kInvalidInputView,
-            base::IntToString(i));
+        *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidInputView,
+                                                     base::NumberToString(i));
         return false;
       }
     }
@@ -208,8 +202,7 @@
       options_page_url = extension->GetResourceURL(options_page_str);
       if (!options_page_url.is_valid()) {
         *error = ErrorUtils::FormatErrorMessageUTF16(
-            errors::kInvalidOptionsPage,
-            base::IntToString(i));
+            errors::kInvalidOptionsPage, base::NumberToString(i));
         return false;
       }
     } else {
diff --git a/chrome/common/extensions/command.cc b/chrome/common/extensions/command.cc
index 901e8e0..aba488f 100644
--- a/chrome/common/extensions/command.cc
+++ b/chrome/common/extensions/command.cc
@@ -78,8 +78,7 @@
       platform_key != values::kKeybindingPlatformLinux &&
       platform_key != values::kKeybindingPlatformDefault) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
-        errors::kInvalidKeyBindingUnknownPlatform,
-        base::IntToString(index),
+        errors::kInvalidKeyBindingUnknownPlatform, base::NumberToString(index),
         platform_key);
     return ui::Accelerator();
   }
@@ -89,11 +88,9 @@
   if (tokens.size() == 0 ||
       (tokens.size() == 1 && DoesRequireModifier(accelerator)) ||
       tokens.size() > kMaxTokenSize) {
-    *error = ErrorUtils::FormatErrorMessageUTF16(
-        errors::kInvalidKeyBinding,
-        base::IntToString(index),
-        platform_key,
-        accelerator);
+    *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidKeyBinding,
+                                                 base::NumberToString(index),
+                                                 platform_key, accelerator);
     return ui::Accelerator();
   }
 
@@ -210,11 +207,9 @@
         break;
       }
     } else {
-      *error = ErrorUtils::FormatErrorMessageUTF16(
-          errors::kInvalidKeyBinding,
-          base::IntToString(index),
-          platform_key,
-          accelerator);
+      *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidKeyBinding,
+                                                   base::NumberToString(index),
+                                                   platform_key, accelerator);
       return ui::Accelerator();
     }
   }
@@ -232,11 +227,9 @@
   // as a modifier.
   if (key == ui::VKEY_UNKNOWN || (ctrl && alt) || (command && alt) ||
       (shift && !ctrl && !alt && !command)) {
-    *error = ErrorUtils::FormatErrorMessageUTF16(
-        errors::kInvalidKeyBinding,
-        base::IntToString(index),
-        platform_key,
-        accelerator);
+    *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidKeyBinding,
+                                                 base::NumberToString(index),
+                                                 platform_key, accelerator);
     return ui::Accelerator();
   }
 
@@ -244,9 +237,7 @@
       (shift || ctrl || alt || command)) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
         errors::kInvalidKeyBindingMediaKeyWithModifier,
-        base::IntToString(index),
-        platform_key,
-        accelerator);
+        base::NumberToString(index), platform_key, accelerator);
     return ui::Accelerator();
   }
 
@@ -441,8 +432,7 @@
     if (!command->GetString(keys::kDescription, &description) ||
         description.empty()) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
-          errors::kInvalidKeyBindingDescription,
-          base::IntToString(index));
+          errors::kInvalidKeyBindingDescription, base::NumberToString(index));
       return false;
     }
   }
@@ -464,10 +454,8 @@
         suggestions[iter.key()] = suggested_key_string;
       } else {
         *error = ErrorUtils::FormatErrorMessageUTF16(
-            errors::kInvalidKeyBinding,
-            base::IntToString(index),
-            keys::kSuggestedKey,
-            kMissing);
+            errors::kInvalidKeyBinding, base::NumberToString(index),
+            keys::kSuggestedKey, kMissing);
         return false;
       }
     }
@@ -498,10 +486,8 @@
     if (iter->first == values::kKeybindingPlatformDefault &&
         iter->second.find("Command+") != std::string::npos) {
       *error = ErrorUtils::FormatErrorMessageUTF16(
-          errors::kInvalidKeyBinding,
-          base::IntToString(index),
-          keys::kSuggestedKey,
-          kCommandKeyNotSupported);
+          errors::kInvalidKeyBinding, base::NumberToString(index),
+          keys::kSuggestedKey, kCommandKeyNotSupported);
       return false;
     }
 
@@ -515,10 +501,8 @@
     key = values::kKeybindingPlatformDefault;
   if (suggestions.find(key) == suggestions.end()) {
     *error = ErrorUtils::FormatErrorMessageUTF16(
-        errors::kInvalidKeyBindingMissingPlatform,
-        base::IntToString(index),
-        keys::kSuggestedKey,
-        platform);
+        errors::kInvalidKeyBindingMissingPlatform, base::NumberToString(index),
+        keys::kSuggestedKey, platform);
     return false;  // No platform specified and no fallback. Bail.
   }
 
@@ -536,10 +520,8 @@
       if (accelerator.key_code() == ui::VKEY_UNKNOWN) {
         if (error->empty()) {
           *error = ErrorUtils::FormatErrorMessageUTF16(
-              errors::kInvalidKeyBinding,
-              base::IntToString(index),
-              iter->first,
-              iter->second);
+              errors::kInvalidKeyBinding, base::NumberToString(index),
+              iter->first, iter->second);
         }
         return false;
       }
diff --git a/chrome/common/extensions/command_unittest.cc b/chrome/common/extensions/command_unittest.cc
index be69b6649..cc5fa72 100644
--- a/chrome/common/extensions/command_unittest.cc
+++ b/chrome/common/extensions/command_unittest.cc
@@ -41,8 +41,8 @@
                 bool platform_specific_only,
                 std::vector<std::string>& platforms) {
   SCOPED_TRACE(std::string("Command name: |") + data.command_name + "| key: |" +
-               data.key + "| description: |" + data.description + "| index: " +
-               base::IntToString(i));
+               data.key + "| description: |" + data.description +
+               "| index: " + base::NumberToString(i));
 
   extensions::Command command;
   std::unique_ptr<base::DictionaryValue> input(new base::DictionaryValue);
diff --git a/chrome/common/extensions/docs/server2/app.yaml b/chrome/common/extensions/docs/server2/app.yaml
index 2e8b666..153a0e1 100644
--- a/chrome/common/extensions/docs/server2/app.yaml
+++ b/chrome/common/extensions/docs/server2/app.yaml
@@ -1,5 +1,5 @@
 application: chrome-apps-doc
-version: 3-63-0
+version: 3-64-0
 runtime: python27
 api_version: 1
 threadsafe: false
diff --git a/chrome/common/extensions/docs/templates/intros/declarativeNetRequest.html b/chrome/common/extensions/docs/templates/intros/declarativeNetRequest.html
index 208e1ff5..c01e073 100644
--- a/chrome/common/extensions/docs/templates/intros/declarativeNetRequest.html
+++ b/chrome/common/extensions/docs/templates/intros/declarativeNetRequest.html
@@ -66,7 +66,7 @@
   "condition" : {
     "urlFilter" : "abc",
     "domains" : ["foo.com"],
-    "resource_types" : ["script"]
+    "resourceTypes" : ["script"]
   }
 }
 </pre>
@@ -143,9 +143,10 @@
 To disable the extension ruleset on a particular page, extensions can use the
 <a href="#method-addAllowedPages">
 chrome.declarativeNetRequest.addAllowedPages</a> API. This allows
-extensions to specify match patterns that are compared against the tab URL. Any
-requests originating from these pages are not evaluated against the extension
-ruleset. The number of such patterns an extension can add is bound by the
+extensions to specify <a href="/extensions/match_patterns">match patterns</a>
+that are compared against the tab URL. Any requests originating from these pages
+are not evaluated against the extension ruleset. The number of such patterns an
+extension can add is bound by the
 <a href="#property-MAX_NUMBER_OF_ALLOWED_PAGES">chrome.declarativeNetRequest.MAX_NUMBER_OF_ALLOWED_PAGES</a> constant.
 </p>
 
diff --git a/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc b/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc
index 6293f37..a0e40f1 100644
--- a/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc
+++ b/chrome/common/extensions/manifest_handlers/content_scripts_manifest_unittest.cc
@@ -27,18 +27,18 @@
 TEST_F(ContentScriptsManifestTest, MatchPattern) {
   Testcase testcases[] = {
       // chrome:// urls are not allowed.
-      Testcase(
-          "content_script_chrome_url_invalid.json",
-          ErrorUtils::FormatErrorMessage(
-              errors::kInvalidMatch, base::IntToString(0), base::IntToString(0),
-              URLPattern::GetParseResultString(
-                  URLPattern::ParseResult::kInvalidScheme))),
+      Testcase("content_script_chrome_url_invalid.json",
+               ErrorUtils::FormatErrorMessage(
+                   errors::kInvalidMatch, base::NumberToString(0),
+                   base::NumberToString(0),
+                   URLPattern::GetParseResultString(
+                       URLPattern::ParseResult::kInvalidScheme))),
 
       // Match paterns must be strings.
       Testcase("content_script_match_pattern_not_string.json",
                ErrorUtils::FormatErrorMessage(
-                   errors::kInvalidMatch, base::IntToString(0),
-                   base::IntToString(0), errors::kExpectString))};
+                   errors::kInvalidMatch, base::NumberToString(0),
+                   base::NumberToString(0), errors::kExpectString))};
   RunTestcases(testcases, base::size(testcases), EXPECT_TYPE_ERROR);
 
   LoadAndExpectSuccess("ports_in_content_scripts.json");
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_web_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_web_unittest.cc
index eb9398bb..b491b978 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_web_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_web_unittest.cc
@@ -21,24 +21,24 @@
       Testcase("web_urls_wrong_type.json", errors::kInvalidWebURLs),
       Testcase("web_urls_invalid_1.json",
                ErrorUtils::FormatErrorMessage(errors::kInvalidWebURL,
-                                              base::IntToString(0),
+                                              base::NumberToString(0),
                                               errors::kExpectString)),
       Testcase("web_urls_invalid_2.json",
                ErrorUtils::FormatErrorMessage(
-                   errors::kInvalidWebURL, base::IntToString(0),
+                   errors::kInvalidWebURL, base::NumberToString(0),
                    URLPattern::GetParseResultString(
                        URLPattern::ParseResult::kMissingSchemeSeparator))),
       Testcase("web_urls_invalid_3.json",
                ErrorUtils::FormatErrorMessage(errors::kInvalidWebURL,
-                                              base::IntToString(0),
+                                              base::NumberToString(0),
                                               errors::kNoWildCardsInPaths)),
       Testcase("web_urls_invalid_4.json",
                ErrorUtils::FormatErrorMessage(
-                   errors::kInvalidWebURL, base::IntToString(0),
+                   errors::kInvalidWebURL, base::NumberToString(0),
                    errors::kCannotClaimAllURLsInExtent)),
       Testcase("web_urls_invalid_5.json",
                ErrorUtils::FormatErrorMessage(
-                   errors::kInvalidWebURL, base::IntToString(1),
+                   errors::kInvalidWebURL, base::NumberToString(1),
                    errors::kCannotClaimAllHostsInExtent))};
   RunTestcases(testcases, base::size(testcases), EXPECT_TYPE_ERROR);
 
diff --git a/chrome/common/extensions/manifest_tests/extension_manifests_webview_accessible_resources_unittest.cc b/chrome/common/extensions/manifest_tests/extension_manifests_webview_accessible_resources_unittest.cc
index 47bd9758..8d182681 100644
--- a/chrome/common/extensions/manifest_tests/extension_manifests_webview_accessible_resources_unittest.cc
+++ b/chrome/common/extensions/manifest_tests/extension_manifests_webview_accessible_resources_unittest.cc
@@ -68,16 +68,18 @@
                       errors::kInvalidWebviewPartitionsList);
   LoadAndExpectError("webview_accessible_resources_invalid3.json",
                       errors::kInvalidWebviewPartitionsList);
-  LoadAndExpectError("webview_accessible_resources_invalid4.json",
-      ErrorUtils::FormatErrorMessage(
-          errors::kInvalidWebviewPartition, base::IntToString(0)));
+  LoadAndExpectError(
+      "webview_accessible_resources_invalid4.json",
+      ErrorUtils::FormatErrorMessage(errors::kInvalidWebviewPartition,
+                                     base::NumberToString(0)));
   LoadAndExpectError("webview_accessible_resources_invalid5.json",
                      errors::kInvalidWebviewPartitionName);
   LoadAndExpectError("webview_accessible_resources_invalid6.json",
                      errors::kInvalidWebviewAccessibleResourcesList);
   LoadAndExpectError("webview_accessible_resources_invalid7.json",
                      errors::kInvalidWebviewAccessibleResourcesList);
-  LoadAndExpectError("webview_accessible_resources_invalid8.json",
-      ErrorUtils::FormatErrorMessage(
-          errors::kInvalidWebviewAccessibleResource, base::IntToString(0)));
+  LoadAndExpectError(
+      "webview_accessible_resources_invalid8.json",
+      ErrorUtils::FormatErrorMessage(errors::kInvalidWebviewAccessibleResource,
+                                     base::NumberToString(0)));
 }
diff --git a/chrome/common/extensions/permissions/chrome_permission_message_rules_unittest.cc b/chrome/common/extensions/permissions/chrome_permission_message_rules_unittest.cc
index 1bad3e8..9448be3 100644
--- a/chrome/common/extensions/permissions/chrome_permission_message_rules_unittest.cc
+++ b/chrome/common/extensions/permissions/chrome_permission_message_rules_unittest.cc
@@ -21,7 +21,7 @@
 std::string PermissionIDsToString(const std::set<APIPermission::ID>& ids) {
   std::vector<std::string> strs;
   for (auto id : ids)
-    strs.push_back(base::IntToString(id));
+    strs.push_back(base::NumberToString(id));
   return base::JoinString(strs, " ");
 }
 
diff --git a/chrome/common/extensions/permissions/permission_set_unittest.cc b/chrome/common/extensions/permissions/permission_set_unittest.cc
index 8dbb2e3..64e828c7 100644
--- a/chrome/common/extensions/permissions/permission_set_unittest.cc
+++ b/chrome/common/extensions/permissions/permission_set_unittest.cc
@@ -83,7 +83,7 @@
 std::string PermissionIDsToString(const PermissionIDSet& ids) {
   std::vector<std::string> strs;
   for (const PermissionID& id : ids)
-    strs.push_back(base::IntToString(id.id()));
+    strs.push_back(base::NumberToString(id.id()));
   return base::StringPrintf("[ %s ]", base::JoinString(strs, ", ").c_str());
 }
 
diff --git a/chrome/common/google_url_loader_throttle.cc b/chrome/common/google_url_loader_throttle.cc
index 0378855..556f3394 100644
--- a/chrome/common/google_url_loader_throttle.cc
+++ b/chrome/common/google_url_loader_throttle.cc
@@ -25,11 +25,11 @@
 void GoogleURLLoaderThrottle::WillStartRequest(
     network::ResourceRequest* request,
     bool* defer) {
-  if (!is_off_the_record_ &&
-      variations::ShouldAppendVariationHeaders(request->url) &&
-      !dynamic_params_.variation_ids_header.empty()) {
-    request->client_data_header = dynamic_params_.variation_ids_header;
-  }
+  variations::AppendVariationsHeaderWithCustomValue(
+      request->url,
+      is_off_the_record_ ? variations::InIncognito::kYes
+                         : variations::InIncognito::kNo,
+      dynamic_params_.variation_ids_header, request);
 
   if (dynamic_params_.force_safe_search) {
     GURL new_url;
@@ -59,12 +59,12 @@
 
 void GoogleURLLoaderThrottle::WillRedirectRequest(
     net::RedirectInfo* redirect_info,
-    const network::ResourceResponseHead& /* response_head */,
+    const network::ResourceResponseHead& response_head,
     bool* /* defer */,
     std::vector<std::string>* to_be_removed_headers,
     net::HttpRequestHeaders* modified_headers) {
-  if (!variations::ShouldAppendVariationHeaders(redirect_info->new_url))
-    to_be_removed_headers->push_back(variations::kClientDataHeader);
+  variations::RemoveVariationsHeaderIfNeeded(*redirect_info, response_head,
+                                             to_be_removed_headers);
 
   // URLLoaderThrottles can only change the redirect URL when the network
   // service is enabled. The non-network service path handles this in
diff --git a/chrome/common/media_router/mojo/media_router_struct_traits_unittest.cc b/chrome/common/media_router/mojo/media_router_struct_traits_unittest.cc
index 83d90dc..b6acc67 100644
--- a/chrome/common/media_router/mojo/media_router_struct_traits_unittest.cc
+++ b/chrome/common/media_router/mojo/media_router_struct_traits_unittest.cc
@@ -6,7 +6,7 @@
 
 #include <utility>
 
-#include "base/message_loop/message_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "chrome/common/media_router/discovery/media_sink_internal.h"
 #include "chrome/common/media_router/mojo/media_router.mojom.h"
 #include "chrome/common/media_router/mojo/media_router_traits_test_service.mojom.h"
@@ -35,7 +35,7 @@
     std::move(callback).Run(sink);
   }
 
-  base::MessageLoop loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   mojo::BindingSet<MediaRouterTraitsTestService> traits_test_bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(MediaRouterStructTraitsTest);
diff --git a/chrome/common/net/safe_search_util_unittest.cc b/chrome/common/net/safe_search_util_unittest.cc
index fdfaccb..0dcf946 100644
--- a/chrome/common/net/safe_search_util_unittest.cc
+++ b/chrome/common/net/safe_search_util_unittest.cc
@@ -4,7 +4,6 @@
 
 #include "chrome/common/net/safe_search_util.h"
 
-#include "base/message_loop/message_loop.h"
 #include "base/strings/string_piece.h"
 #include "chrome/common/url_constants.h"
 #include "net/http/http_request_headers.h"
@@ -12,29 +11,22 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-class SafeSearchUtilTest : public ::testing::Test {
- protected:
-  SafeSearchUtilTest() {}
-  ~SafeSearchUtilTest() override {}
+namespace {
+// Does a request using the |url_string| URL and verifies that the expected
+// string is equal to the query part (between ? and #) of the final url of
+// that request.
+void CheckAddedParameters(const std::string& url_string,
+                          const std::string& expected_query_parameters) {
+  // Show the URL in the trace so we know where we failed.
+  SCOPED_TRACE(url_string);
 
-  // Does a request using the |url_string| URL and verifies that the expected
-  // string is equal to the query part (between ? and #) of the final url of
-  // that request.
-  void CheckAddedParameters(const std::string& url_string,
-                            const std::string& expected_query_parameters) {
-    // Show the URL in the trace so we know where we failed.
-    SCOPED_TRACE(url_string);
+  GURL result(url_string);
+  safe_search_util::ForceGoogleSafeSearch(GURL(url_string), &result);
 
-    GURL result(url_string);
-    safe_search_util::ForceGoogleSafeSearch(GURL(url_string), &result);
+  EXPECT_EQ(expected_query_parameters, result.query());
+}
 
-    EXPECT_EQ(expected_query_parameters, result.query());
-  }
-
-  base::MessageLoop message_loop_;
-};
-
-TEST_F(SafeSearchUtilTest, AddGoogleSafeSearchParams) {
+TEST(SafeSearchUtilTest, AddGoogleSafeSearchParams) {
   const std::string kSafeParameter = safe_search_util::kSafeSearchSafeParameter;
   const std::string kSsuiParameter = safe_search_util::kSafeSearchSsuiParameter;
   const std::string kBothParameters = kSafeParameter + "&" + kSsuiParameter;
@@ -128,7 +120,7 @@
                        "q=%26%26%26&param=%26%26%26&" + kBothParameters);
 }
 
-TEST_F(SafeSearchUtilTest, SetYoutubeHeader) {
+TEST(SafeSearchUtilTest, SetYoutubeHeader) {
   net::HttpRequestHeaders headers;
   safe_search_util::ForceYouTubeRestrict(
       GURL("http://www.youtube.com"), &headers,
@@ -138,7 +130,7 @@
   EXPECT_EQ("Moderate", value);
 }
 
-TEST_F(SafeSearchUtilTest, OverrideYoutubeHeader) {
+TEST(SafeSearchUtilTest, OverrideYoutubeHeader) {
   net::HttpRequestHeaders headers;
   headers.SetHeader("Youtube-Restrict", "Off");
   safe_search_util::ForceYouTubeRestrict(
@@ -149,7 +141,7 @@
   EXPECT_EQ("Moderate", value);
 }
 
-TEST_F(SafeSearchUtilTest, DoesntTouchNonYoutubeURL) {
+TEST(SafeSearchUtilTest, DoesntTouchNonYoutubeURL) {
   net::HttpRequestHeaders headers;
   headers.SetHeader("Youtube-Restrict", "Off");
   safe_search_util::ForceYouTubeRestrict(
@@ -159,3 +151,5 @@
   EXPECT_TRUE(headers.GetHeader("Youtube-Restrict", &value));
   EXPECT_EQ("Off", value);
 }
+
+}  // namespace
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index 5a30b61e..39da56b 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -2574,4 +2574,14 @@
 const char kEnterpriseHardwarePlatformAPIEnabled[] =
     "enterprise_hardware_platform_api.enabled";
 
+#if defined(OS_CHROMEOS)
+// Enum that specifies certificate management permissions for user. It can have
+// one of the following values.
+// 0: Users can manage all certificates.
+// 1: Users can manage user certificates, but not device certificates.
+// 2: Disallow users from managing certificates
+// Controlled by CertificateManagementAllowed policy.
+const char kCertificateManagementAllowed[] = "certificate_management_allowed";
+#endif
+
 }  // namespace prefs
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index ea42e89a..8a2674b8 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -914,6 +914,10 @@
 extern const char kUsageStatsEnabled[];
 #endif
 
+#if defined(OS_CHROMEOS)
+extern const char kCertificateManagementAllowed[];
+#endif
+
 }  // namespace prefs
 
 #endif  // CHROME_COMMON_PREF_NAMES_H_
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h
index fed196b..d829113 100644
--- a/chrome/common/render_messages.h
+++ b/chrome/common/render_messages.h
@@ -77,12 +77,10 @@
 
 // Sent by the renderer process to check whether access to web databases is
 // granted by content settings.
-IPC_SYNC_MESSAGE_CONTROL5_1(ChromeViewHostMsg_AllowDatabase,
+IPC_SYNC_MESSAGE_CONTROL3_1(ChromeViewHostMsg_AllowDatabase,
                             int /* render_frame_id */,
                             GURL /* origin_url */,
                             GURL /* top origin url */,
-                            base::string16 /* database name */,
-                            base::string16 /* database display name */,
                             bool /* allowed */)
 
 // Sent by the renderer process to check whether access to DOM Storage is
diff --git a/chrome/common/service_process_util_unittest.cc b/chrome/common/service_process_util_unittest.cc
index bba6148..da5d78c 100644
--- a/chrome/common/service_process_util_unittest.cc
+++ b/chrome/common/service_process_util_unittest.cc
@@ -10,13 +10,14 @@
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/location.h"
-#include "base/message_loop/message_loop.h"
 #include "base/process/kill.h"
 #include "base/process/launch.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/string_split.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/platform_thread.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 
 #if !defined(OS_MACOSX)
@@ -230,7 +231,7 @@
 
 MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestShutdown) {
   base::PlatformThread::SetName("ServiceProcessStateTestShutdownMainThread");
-  base::MessageLoop message_loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   base::RunLoop run_loop;
   base::Thread io_thread_("ServiceProcessStateTestShutdownIOThread");
   base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
@@ -239,7 +240,7 @@
   EXPECT_TRUE(state.Initialize());
   EXPECT_TRUE(state.SignalReady(io_thread_.task_runner().get(),
                                 base::Bind(&ShutdownTask, &run_loop)));
-  message_loop.task_runner()->PostDelayedTask(
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
       FROM_HERE, run_loop.QuitWhenIdleClosure(),
       TestTimeouts::action_max_timeout());
   EXPECT_FALSE(g_good_shutdown);
diff --git a/chrome/installer/gcapi/gcapi_last_run_test.cc b/chrome/installer/gcapi/gcapi_last_run_test.cc
index 309a8c0..d069b8908 100644
--- a/chrome/installer/gcapi/gcapi_last_run_test.cc
+++ b/chrome/installer/gcapi/gcapi_last_run_test.cc
@@ -51,7 +51,7 @@
   }
 
   bool SetLastRunTime(int64_t last_run_time) {
-    return SetLastRunTimeString(base::Int64ToString16(last_run_time));
+    return SetLastRunTimeString(base::NumberToString16(last_run_time));
   }
 
   bool SetLastRunTimeString(const base::string16& last_run_time_string) {
diff --git a/chrome/installer/gcapi/gcapi_reactivation_test.cc b/chrome/installer/gcapi/gcapi_reactivation_test.cc
index c4d93992..503e6c3 100644
--- a/chrome/installer/gcapi/gcapi_reactivation_test.cc
+++ b/chrome/installer/gcapi/gcapi_reactivation_test.cc
@@ -45,7 +45,7 @@
   }
 
   bool SetLastRunTime(HKEY hive, int64_t last_run_time) {
-    return SetLastRunTimeString(hive, base::Int64ToString16(last_run_time));
+    return SetLastRunTimeString(hive, base::NumberToString16(last_run_time));
   }
 
   bool SetLastRunTimeString(HKEY hive,
diff --git a/chrome/installer/setup/update_active_setup_version_work_item.cc b/chrome/installer/setup/update_active_setup_version_work_item.cc
index 3b308b6..f2463d9 100644
--- a/chrome/installer/setup/update_active_setup_version_work_item.cc
+++ b/chrome/installer/setup/update_active_setup_version_work_item.cc
@@ -91,7 +91,7 @@
       previous_value = 0;
     }
     version_components[SELECTIVE_TRIGGER] =
-        base::UintToString16(previous_value + 1);
+        base::NumberToString16(previous_value + 1);
   }
 
   return base::JoinString(version_components, L",");
diff --git a/chrome/installer/setup/user_experiment.cc b/chrome/installer/setup/user_experiment.cc
index eafc3b16..26faf62 100644
--- a/chrome/installer/setup/user_experiment.cc
+++ b/chrome/installer/setup/user_experiment.cc
@@ -531,7 +531,7 @@
   base::CommandLine command_line(chrome_exe);
 #if defined(OS_WIN)
   command_line.AppendSwitchNative(::switches::kTryChromeAgain,
-                                  base::IntToString16(experiment.group()));
+                                  base::NumberToString16(experiment.group()));
 #endif  // defined(OS_WIN)
 
   STARTUPINFOW startup_info = {sizeof(startup_info)};
diff --git a/chrome/installer/util/google_update_settings.cc b/chrome/installer/util/google_update_settings.cc
index ccac9b7..4f0ad53 100644
--- a/chrome/installer/util/google_update_settings.cc
+++ b/chrome/installer/util/google_update_settings.cc
@@ -138,7 +138,7 @@
     WriteGoogleUpdateAggregateNumKeyInternal(value_name, value, L"sum()");
   } else {
     // Write |value| as a string in value |value_name|.
-    WriteUserGoogleUpdateStrKey(value_name, base::UintToString16(value));
+    WriteUserGoogleUpdateStrKey(value_name, base::NumberToString16(value));
   }
 }
 
@@ -359,10 +359,10 @@
                               base::UTF8ToUTF16(client_info.client_id));
   WriteUserGoogleUpdateStrKey(
       google_update::kRegMetricsIdInstallDate,
-      base::Int64ToString16(client_info.installation_date));
+      base::NumberToString16(client_info.installation_date));
   WriteUserGoogleUpdateStrKey(
       google_update::kRegMetricsIdEnabledDate,
-      base::Int64ToString16(client_info.reporting_enabled_date));
+      base::NumberToString16(client_info.reporting_enabled_date));
 }
 
 // EULA consent is only relevant for system-level installs.
@@ -396,7 +396,7 @@
 bool GoogleUpdateSettings::SetLastRunTime() {
   int64_t time = base::Time::NowFromSystemTime().ToInternalValue();
   return WriteUserGoogleUpdateStrKey(google_update::kRegLastRunTimeField,
-                                     base::Int64ToString16(time));
+                                     base::NumberToString16(time));
 }
 
 bool GoogleUpdateSettings::RemoveLastRunTime() {
diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc
index 14fcde2..6d665d7 100644
--- a/chrome/installer/util/shell_util.cc
+++ b/chrome/installer/util/shell_util.cc
@@ -1760,7 +1760,7 @@
                                              int icon_index) {
   base::string16 icon_string(icon_path.value());
   icon_string.append(L",");
-  icon_string.append(base::IntToString16(icon_index));
+  icon_string.append(base::NumberToString16(icon_index));
   return icon_string;
 }
 
diff --git a/chrome/renderer/benchmarking_extension.cc b/chrome/renderer/benchmarking_extension.cc
index 2d957fd..68ed3660 100644
--- a/chrome/renderer/benchmarking_extension.cc
+++ b/chrome/renderer/benchmarking_extension.cc
@@ -79,8 +79,8 @@
   }
 };
 
-v8::Extension* BenchmarkingExtension::Get() {
-  return new BenchmarkingWrapper();
+std::unique_ptr<v8::Extension> BenchmarkingExtension::Get() {
+  return std::make_unique<BenchmarkingWrapper>();
 }
 
 }  // namespace extensions_v8
diff --git a/chrome/renderer/benchmarking_extension.h b/chrome/renderer/benchmarking_extension.h
index 13afbce..750560d2 100644
--- a/chrome/renderer/benchmarking_extension.h
+++ b/chrome/renderer/benchmarking_extension.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_RENDERER_BENCHMARKING_EXTENSION_H_
 #define CHROME_RENDERER_BENCHMARKING_EXTENSION_H_
 
+#include <memory>
+
 namespace v8 {
 class Extension;
 }
@@ -16,7 +18,7 @@
 // name is to distinguish it from the built-in V8 Profiler.
 class BenchmarkingExtension {
  public:
-  static v8::Extension* Get();
+  static std::unique_ptr<v8::Extension> Get();
 };
 
 }  // namespace extensions_v8
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc
index 5fbee13a..60a6f4e 100644
--- a/chrome/renderer/chrome_content_renderer_client.cc
+++ b/chrome/renderer/chrome_content_renderer_client.cc
@@ -1553,7 +1553,7 @@
 
 bool ChromeContentRendererClient::IsExcludedHeaderForServiceWorkerFetchEvent(
     const std::string& header_name) {
-  return header_name == variations::kClientDataHeader;
+  return variations::IsVariationsHeader(header_name);
 }
 
 // If we're in an extension, there is no need disabling multiple routes as
diff --git a/chrome/renderer/chrome_render_frame_observer.cc b/chrome/renderer/chrome_render_frame_observer.cc
index 05f80c0..39b7daebe 100644
--- a/chrome/renderer/chrome_render_frame_observer.cc
+++ b/chrome/renderer/chrome_render_frame_observer.cc
@@ -216,7 +216,7 @@
   // TODO(dglazkov): This code is clearly in the wrong place. Need
   // to investigate what it is doing and fix (http://crbug.com/606164).
   WebNode context_node = frame->ContextMenuNode();
-  if (!context_node.IsNull() && context_node.IsElementNode()) {
+  if (!context_node.IsNull()) {
     frame->ReloadImage(context_node);
   }
 }
diff --git a/chrome/renderer/content_settings_observer.cc b/chrome/renderer/content_settings_observer.cc
index 0dcaec6f..46a031c 100644
--- a/chrome/renderer/content_settings_observer.cc
+++ b/chrome/renderer/content_settings_observer.cc
@@ -251,9 +251,7 @@
   bindings_.AddBinding(this, std::move(request));
 }
 
-bool ContentSettingsObserver::AllowDatabase(const WebString& name,
-                                            const WebString& display_name,
-                                            unsigned estimated_size) {
+bool ContentSettingsObserver::AllowDatabase() {
   WebFrame* frame = render_frame()->GetWebFrame();
   if (IsUniqueFrame(frame))
     return false;
@@ -261,8 +259,7 @@
   bool result = false;
   Send(new ChromeViewHostMsg_AllowDatabase(
       routing_id(), url::Origin(frame->GetSecurityOrigin()).GetURL(),
-      url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(), name.Utf16(),
-      display_name.Utf16(), &result));
+      url::Origin(frame->Top()->GetSecurityOrigin()).GetURL(), &result));
   return result;
 }
 
diff --git a/chrome/renderer/content_settings_observer.h b/chrome/renderer/content_settings_observer.h
index b77c34d9..23783cac 100644
--- a/chrome/renderer/content_settings_observer.h
+++ b/chrome/renderer/content_settings_observer.h
@@ -75,9 +75,7 @@
                            const base::string16& details);
 
   // blink::WebContentSettingsClient:
-  bool AllowDatabase(const blink::WebString& name,
-                     const blink::WebString& display_name,
-                     unsigned estimated_size) override;
+  bool AllowDatabase() override;
   void RequestFileSystemAccessAsync(
       const blink::WebContentSettingCallbacks& callbacks) override;
   bool AllowImage(bool enabled_per_settings,
diff --git a/chrome/renderer/loadtimes_extension_bindings.cc b/chrome/renderer/loadtimes_extension_bindings.cc
index a79a0a2d..d32f972 100644
--- a/chrome/renderer/loadtimes_extension_bindings.cc
+++ b/chrome/renderer/loadtimes_extension_bindings.cc
@@ -404,8 +404,8 @@
   }
 };
 
-v8::Extension* LoadTimesExtension::Get() {
-  return new LoadTimesExtensionWrapper();
+std::unique_ptr<v8::Extension> LoadTimesExtension::Get() {
+  return std::make_unique<LoadTimesExtensionWrapper>();
 }
 
 }  // namespace extensions_v8
diff --git a/chrome/renderer/loadtimes_extension_bindings.h b/chrome/renderer/loadtimes_extension_bindings.h
index c97b0b1d..e6a7e83 100644
--- a/chrome/renderer/loadtimes_extension_bindings.h
+++ b/chrome/renderer/loadtimes_extension_bindings.h
@@ -8,6 +8,8 @@
 #ifndef CHROME_RENDERER_LOADTIMES_EXTENSION_BINDINGS_H_
 #define CHROME_RENDERER_LOADTIMES_EXTENSION_BINDINGS_H_
 
+#include <memory>
+
 namespace v8 {
 class Extension;
 }
@@ -16,7 +18,7 @@
 
 class LoadTimesExtension {
  public:
-  static v8::Extension* Get();
+  static std::unique_ptr<v8::Extension> Get();
 };
 
 }  // namespace extensions_v8
diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc
index d0a33d79..21643d3 100644
--- a/chrome/renderer/net/net_error_helper.cc
+++ b/chrome/renderer/net/net_error_helper.cc
@@ -439,12 +439,8 @@
 }
 
 void NetErrorHelper::EnablePageHelperFunctions(net::Error net_error) {
-  if (net::IsCertificateError(net_error)) {
-    SSLCertificateErrorPageController::Install(
-        render_frame(),
-        weak_ssl_error_controller_delegate_factory_.GetWeakPtr());
-    return;
-  }
+  SSLCertificateErrorPageController::Install(
+      render_frame(), weak_ssl_error_controller_delegate_factory_.GetWeakPtr());
   NetErrorPageController::Install(
       render_frame(), weak_controller_delegate_factory_.GetWeakPtr());
 
diff --git a/chrome/renderer/net_benchmarking_extension.cc b/chrome/renderer/net_benchmarking_extension.cc
index 5979ea6..3dd6b13 100644
--- a/chrome/renderer/net_benchmarking_extension.cc
+++ b/chrome/renderer/net_benchmarking_extension.cc
@@ -108,8 +108,8 @@
   }
 };
 
-v8::Extension* NetBenchmarkingExtension::Get() {
-  return new NetBenchmarkingWrapper();
+std::unique_ptr<v8::Extension> NetBenchmarkingExtension::Get() {
+  return std::make_unique<NetBenchmarkingWrapper>();
 }
 
 }  // namespace extensions_v8
diff --git a/chrome/renderer/net_benchmarking_extension.h b/chrome/renderer/net_benchmarking_extension.h
index f1cc7ee..7501bab 100644
--- a/chrome/renderer/net_benchmarking_extension.h
+++ b/chrome/renderer/net_benchmarking_extension.h
@@ -5,6 +5,8 @@
 #ifndef CHROME_RENDERER_NET_BENCHMARKING_EXTENSION_H_
 #define CHROME_RENDERER_NET_BENCHMARKING_EXTENSION_H_
 
+#include <memory>
+
 namespace v8 {
 class Extension;
 }
@@ -13,7 +15,7 @@
 
 class NetBenchmarkingExtension {
  public:
-  static v8::Extension* Get();
+  static std::unique_ptr<v8::Extension> Get();
 };
 
 }  // namespace extensions_v8
diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
index ddec560..eac14f9a 100644
--- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc
+++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc
@@ -136,8 +136,8 @@
           roundf(power_saver_info.custom_poster_size.width() / zoom_factor);
       int height =
           roundf(power_saver_info.custom_poster_size.height() / zoom_factor);
-      values.SetString("visibleWidth", base::IntToString(width) + "px");
-      values.SetString("visibleHeight", base::IntToString(height) + "px");
+      values.SetString("visibleWidth", base::NumberToString(width) + "px");
+      values.SetString("visibleHeight", base::NumberToString(height) + "px");
     }
   }
 
diff --git a/chrome/service/cloud_print/cloud_print_connector.cc b/chrome/service/cloud_print/cloud_print_connector.cc
index 2e071b2..2e5ca0d 100644
--- a/chrome/service/cloud_print/cloud_print_connector.cc
+++ b/chrome/service/cloud_print/cloud_print_connector.cc
@@ -619,8 +619,8 @@
   net::AddMultipartValueForUpload(kPrinterDescValue,
       info.printer_description, mime_boundary, std::string(), &post_data);
   net::AddMultipartValueForUpload(kPrinterStatusValue,
-      base::IntToString(info.printer_status),
-      mime_boundary, std::string(), &post_data);
+                                  base::NumberToString(info.printer_status),
+                                  mime_boundary, std::string(), &post_data);
   // Add local_settings with a current XMPP ping interval.
   net::AddMultipartValueForUpload(kPrinterLocalSettingsValue,
       base::StringPrintf(kCreateLocalSettingsXmppPingFormat,
diff --git a/chrome/service/cloud_print/connector_settings_unittest.cc b/chrome/service/cloud_print/connector_settings_unittest.cc
index 3b870e6..da4e309 100644
--- a/chrome/service/cloud_print/connector_settings_unittest.cc
+++ b/chrome/service/cloud_print/connector_settings_unittest.cc
@@ -10,9 +10,9 @@
 
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
-#include "base/message_loop/message_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/stl_util.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/values.h"
 #include "chrome/common/cloud_print/cloud_print_constants.h"
@@ -72,7 +72,7 @@
   }
 
   base::ScopedTempDir temp_dir_;
-  base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
 };
 
diff --git a/chrome/service/cloud_print/printer_job_handler.cc b/chrome/service/cloud_print/printer_job_handler.cc
index 887236dc..a497a3a 100644
--- a/chrome/service/cloud_print/printer_job_handler.cc
+++ b/chrome/service/cloud_print/printer_job_handler.cc
@@ -732,9 +732,9 @@
       std::string(), &post_data);
   }
   if (printer_info.printer_status != printer_info_.printer_status) {
-    net::AddMultipartValueForUpload(kPrinterStatusValue,
-        base::IntToString(printer_info.printer_status), mime_boundary,
-        std::string(), &post_data);
+    net::AddMultipartValueForUpload(
+        kPrinterStatusValue, base::NumberToString(printer_info.printer_status),
+        mime_boundary, std::string(), &post_data);
   }
 
   // Add local_settings with a current XMPP ping interval.
diff --git a/chrome/service/service_process.cc b/chrome/service/service_process.cc
index 494138da..88d7c4d 100644
--- a/chrome/service/service_process.cc
+++ b/chrome/service/service_process.cc
@@ -154,19 +154,15 @@
   service_process_state_ = std::move(state);
 
   // Initialize TaskScheduler.
-  constexpr int kMaxBackgroundThreads = 1;
-  constexpr int kMaxBackgroundBlockingThreads = 1;
-  constexpr int kMaxForegroundThreads = 3;
-  constexpr int kMaxForegroundBlockingThreads = 3;
+  constexpr int kMaxBackgroundThreads = 2;
+  constexpr int kMaxForegroundThreads = 6;
   constexpr base::TimeDelta kSuggestedReclaimTime =
       base::TimeDelta::FromSeconds(30);
 
   base::TaskScheduler::Create("CloudPrintServiceProcess");
   base::TaskScheduler::GetInstance()->Start(
       {{kMaxBackgroundThreads, kSuggestedReclaimTime},
-       {kMaxBackgroundBlockingThreads, kSuggestedReclaimTime},
-       {kMaxForegroundThreads, kSuggestedReclaimTime},
-       {kMaxForegroundBlockingThreads, kSuggestedReclaimTime,
+       {kMaxForegroundThreads, kSuggestedReclaimTime,
         base::SchedulerBackwardCompatibility::INIT_COM_STA}});
 
   // The NetworkChangeNotifier must be created after TaskScheduler because it
diff --git a/chrome/service/service_process_prefs_unittest.cc b/chrome/service/service_process_prefs_unittest.cc
index 9fea70f..7fb42f2 100644
--- a/chrome/service/service_process_prefs_unittest.cc
+++ b/chrome/service/service_process_prefs_unittest.cc
@@ -5,9 +5,10 @@
 #include <string>
 
 #include "base/files/scoped_temp_dir.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "base/sequenced_task_runner.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/service/service_process_prefs.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,15 +20,14 @@
 
     prefs_.reset(new ServiceProcessPrefs(
         temp_dir_.GetPath().AppendASCII("service_process_prefs.txt"),
-        message_loop_.task_runner().get()));
+        base::ThreadTaskRunnerHandle::Get().get()));
   }
 
   void TearDown() override { prefs_.reset(); }
 
   // The path to temporary directory used to contain the test operations.
   base::ScopedTempDir temp_dir_;
-  // A message loop that we can use as the file thread message loop.
-  base::MessageLoop message_loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
   std::unique_ptr<ServiceProcessPrefs> prefs_;
 };
 
@@ -37,7 +37,7 @@
   prefs_->SetString("tests", "testvalue");
   prefs_->WritePrefs();
   base::RunLoop().RunUntilIdle();
-  prefs_->SetBoolean("testb", false);   // overwrite
+  prefs_->SetBoolean("testb", false);         // overwrite
   prefs_->SetString("tests", std::string());  // overwrite
   prefs_->ReadPrefs();
   EXPECT_EQ(prefs_->GetBoolean("testb", false), true);
diff --git a/chrome/services/app_service/app_service_impl_unittest.cc b/chrome/services/app_service/app_service_impl_unittest.cc
index e0608264..529a8e8 100644
--- a/chrome/services/app_service/app_service_impl_unittest.cc
+++ b/chrome/services/app_service/app_service_impl_unittest.cc
@@ -9,8 +9,8 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
-#include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "chrome/services/app_service/app_service_impl.h"
 #include "chrome/services/app_service/public/mojom/types.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -118,10 +118,7 @@
 
 class AppServiceImplTest : public testing::Test {
  private:
-  // https://www.chromium.org/developers/design-documents/mojo/mojo-migration-guide#TOC-Mocking-in-tests
-  // says, "You will not actually use the loop_ variable, but one need to exist
-  // and this declaration causes a global message loop to be created".
-  base::MessageLoop loop_;
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
 };
 
 TEST_F(AppServiceImplTest, PubSub) {
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 5cc0b98c..e2a0d8d0 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -20,6 +20,7 @@
 import("//components/spellcheck/spellcheck_build_features.gni")
 import("//extensions/buildflags/buildflags.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
+import("//net/features.gni")
 import("//ppapi/buildflags/buildflags.gni")
 import("//remoting/remoting_enable.gni")
 import("//rlz/buildflags/buildflags.gni")
@@ -765,7 +766,7 @@
       "../browser/no_best_effort_tasks_browsertest.cc",
       "../browser/ntp_snippets/content_suggestions_service_factory_browsertest.cc",
       "../browser/ntp_tiles/ntp_tiles_browsertest.cc",
-      "../browser/page_load_metrics/observers/ads_page_load_metrics_observer_browsertest.cc",
+      "../browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/data_saver_site_breakdown_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/data_use_metrics_observer_browsertest.cc",
       "../browser/page_load_metrics/observers/foreground_duration_ukm_observer_browsertest.cc",
@@ -1636,11 +1637,13 @@
         "../browser/ui/views/payments/payment_request_completion_status_metrics_browsertest.cc",
         "../browser/ui/views/payments/payment_request_data_url_browsertest.cc",
         "../browser/ui/views/payments/payment_request_debit_browsertest.cc",
+        "../browser/ui/views/payments/payment_request_has_enrolled_instrument_browsertest.cc",
         "../browser/ui/views/payments/payment_request_journey_logger_browsertest.cc",
         "../browser/ui/views/payments/payment_request_no_update_with_browsertest.cc",
         "../browser/ui/views/payments/payment_request_payment_app_browsertest.cc",
         "../browser/ui/views/payments/payment_request_payment_response_browsertest.cc",
         "../browser/ui/views/payments/payment_request_shipping_address_instance_browsertest.cc",
+        "../browser/ui/views/payments/payment_request_update_with_browsertest.cc",
         "../browser/ui/views/payments/payment_request_use_stats_browsertest.cc",
         "../browser/ui/views/payments/payment_sheet_view_controller_browsertest.cc",
         "../browser/ui/views/payments/profile_list_view_controller_browsertest.cc",
@@ -2292,6 +2295,11 @@
       sources +=
           [ "../browser/ui/screen_capture_notification_ui_browsertest.cc" ]
     }
+
+    if (trial_comparison_cert_verifier_supported) {
+      sources +=
+          [ "../browser/net/trial_comparison_cert_verifier_browsertest.cc" ]
+    }
   }
 }
 
@@ -2667,7 +2675,7 @@
     "../browser/ntp_snippets/download_suggestions_provider_unittest.cc",
     "../browser/page_load_metrics/metrics_web_contents_observer_unittest.cc",
     "../browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc",
-    "../browser/page_load_metrics/observers/ads_page_load_metrics_observer_unittest.cc",
+    "../browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc",
@@ -4530,10 +4538,14 @@
       sources += [
         "../browser/ui/views/apps/shaped_app_window_targeter_unittest.cc",
 
-        # Get these compiling on Mac - see http://crbug.com/657883.
-        "../browser/ui/views/crypto_module_password_dialog_view_unittest.cc",
+        # Get this compiling on Mac - see http://crbug.com/657883.
         "../browser/ui/views/desktop_capture/desktop_media_picker_views_unittest.cc",
       ]
+      if (use_nss_certs) {
+        sources += [
+          "../browser/ui/views/crypto_module_password_dialog_view_unittest.cc",
+        ]
+      }
     }
   }
   if (use_nss_certs) {
@@ -4542,8 +4554,10 @@
   if (!is_android && use_nss_certs) {
     sources += [ "../common/net/x509_certificate_model_nss_unittest.cc" ]
   }
-  if (is_desktop_linux || is_mac) {
-    sources += [ "../browser/net/trial_comparison_cert_verifier_unittest.cc" ]
+  if (trial_comparison_cert_verifier_supported) {
+    sources += [
+      "../browser/net/trial_comparison_cert_verifier_controller_unittest.cc",
+    ]
   }
   if (enable_supervised_users) {
     sources += [
@@ -4583,6 +4597,7 @@
       "../browser/ui/app_list/search/answer_card/answer_card_result_unittest.cc",
       "../browser/ui/app_list/search/answer_card/answer_card_search_provider_unittest.cc",
       "../browser/ui/app_list/search/arc/arc_app_data_search_provider_unittest.cc",
+      "../browser/ui/app_list/search/arc/arc_app_reinstall_search_provider_unittest.cc",
       "../browser/ui/app_list/search/arc/arc_app_shortcuts_search_provider_unittest.cc",
       "../browser/ui/app_list/search/arc/arc_playstore_search_provider_unittest.cc",
       "../browser/ui/app_list/search/launcher_search/launcher_search_icon_image_loader_unittest.cc",
diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/omaha/MockRequestGenerator.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/omaha/MockRequestGenerator.java
index 75dc803..d9e4493 100644
--- a/chrome/test/android/javatests/src/org/chromium/chrome/test/omaha/MockRequestGenerator.java
+++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/omaha/MockRequestGenerator.java
@@ -14,8 +14,6 @@
         HANDSET, TABLET
     }
 
-    public enum SignedInStatus { TRUE, FALSE }
-
     public static final String UUID_PHONE = "uuid_phone";
     public static final String UUID_TABLET = "uuid_tablet";
     public static final String SERVER_URL = "http://totallylegitserver.com";
@@ -29,13 +27,9 @@
 
     private final boolean mIsOnTablet;
 
-    private final boolean mIsSignedIn;
-
-    public MockRequestGenerator(
-            Context context, DeviceType deviceType, SignedInStatus signInStatus) {
+    public MockRequestGenerator(Context context, DeviceType deviceType) {
         super(context);
         mIsOnTablet = deviceType == DeviceType.TABLET;
-        mIsSignedIn = signInStatus == SignedInStatus.TRUE;
     }
 
     @Override
@@ -79,11 +73,6 @@
     }
 
     @Override
-    public int getNumSignedIn() {
-        return mIsSignedIn ? 1 : 0;
-    }
-
-    @Override
     public String getAdditionalParameters() {
         return ADDITIONAL_PARAMETERS;
     }
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index e07533a..74d9029 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -72,6 +72,7 @@
                           int reason) override {}
   void OnTabDetached(content::WebContents* contents, bool was_active) override {
   }
+  void OnTabRestoredFromMenu(int command_id) override {}
   void ZoomChangedForActiveTab(bool can_show_bubble) override {}
   gfx::Rect GetRestoredBounds() const override;
   ui::WindowShowState GetRestoredState() const override;
diff --git a/chrome/test/base/v8_unit_test.cc b/chrome/test/base/v8_unit_test.cc
index d8cf606f..18626ac4 100644
--- a/chrome/test/base/v8_unit_test.cc
+++ b/chrome/test/base/v8_unit_test.cc
@@ -97,10 +97,13 @@
   v8::MicrotasksScope microtasks(
       isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
 
-  v8::Local<v8::Value> function_property = context->Global()->Get(
-      v8::String::NewFromUtf8(isolate, "runTest",
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked());
+  v8::Local<v8::Value> function_property =
+      context->Global()
+          ->Get(context,
+                v8::String::NewFromUtf8(isolate, "runTest",
+                                        v8::NewStringType::kInternalized)
+                    .ToLocalChecked())
+          .ToLocalChecked();
   EXPECT_FALSE(function_property.IsEmpty());
   if (::testing::Test::HasNonfatalFailure())
     return false;
@@ -111,14 +114,20 @@
       v8::Local<v8::Function>::Cast(function_property);
 
   v8::Local<v8::Array> params = v8::Array::New(isolate);
-  params->Set(0, v8::String::NewFromUtf8(isolate, test_fixture.data(),
-                                         v8::NewStringType::kNormal,
-                                         test_fixture.size())
-                     .ToLocalChecked());
-  params->Set(
-      1, v8::String::NewFromUtf8(isolate, test_name.data(),
-                                 v8::NewStringType::kNormal, test_name.size())
-             .ToLocalChecked());
+  params
+      ->Set(context, 0,
+            v8::String::NewFromUtf8(isolate, test_fixture.data(),
+                                    v8::NewStringType::kNormal,
+                                    test_fixture.size())
+                .ToLocalChecked())
+      .Check();
+  params
+      ->Set(
+          context, 1,
+          v8::String::NewFromUtf8(isolate, test_name.data(),
+                                  v8::NewStringType::kNormal, test_name.size())
+              .ToLocalChecked())
+      .Check();
   v8::Local<v8::Value> args[] = {
       v8::Boolean::New(isolate, false),
       v8::String::NewFromUtf8(isolate, "RUN_TEST_F", v8::NewStringType::kNormal)
@@ -224,7 +233,7 @@
                                       v8::NewStringType::kInternalized)
                   .ToLocalChecked(),
               console->NewInstance(context).ToLocalChecked())
-        .ToChecked();
+        .Check();
   }
 }
 
@@ -234,14 +243,16 @@
   v8::Local<v8::Context> context =
       v8::Local<v8::Context>::New(isolate, context_);
   v8::Context::Scope context_scope(context);
-  context->Global()->Set(
-      v8::String::NewFromUtf8(isolate, var_name.c_str(),
-                              v8::NewStringType::kInternalized,
-                              var_name.length())
-          .ToLocalChecked(),
-      v8::String::NewFromUtf8(isolate, value.c_str(),
-                              v8::NewStringType::kNormal, value.length())
-          .ToLocalChecked());
+  context->Global()
+      ->Set(context,
+            v8::String::NewFromUtf8(isolate, var_name.c_str(),
+                                    v8::NewStringType::kInternalized,
+                                    var_name.length())
+                .ToLocalChecked(),
+            v8::String::NewFromUtf8(isolate, value.c_str(),
+                                    v8::NewStringType::kNormal, value.length())
+                .ToLocalChecked())
+      .Check();
 }
 
 void V8UnitTest::ExecuteScriptInContext(const base::StringPiece& script_source,
@@ -306,10 +317,13 @@
   v8::MicrotasksScope microtasks(
       isolate, v8::MicrotasksScope::kDoNotRunMicrotasks);
 
-  v8::Local<v8::Value> function_property = context->Global()->Get(
-      v8::String::NewFromUtf8(isolate, function_name.c_str(),
-                              v8::NewStringType::kInternalized)
-          .ToLocalChecked());
+  v8::Local<v8::Value> function_property =
+      context->Global()
+          ->Get(context,
+                v8::String::NewFromUtf8(isolate, function_name.c_str(),
+                                        v8::NewStringType::kInternalized)
+                    .ToLocalChecked())
+          .ToLocalChecked();
   ASSERT_FALSE(function_property.IsEmpty());
   ASSERT_TRUE(function_property->IsFunction());
   v8::Local<v8::Function> function =
@@ -354,9 +368,12 @@
   EXPECT_EQ(2U, test_result->Length());
   if (::testing::Test::HasNonfatalFailure())
     return;
-  g_test_result_ok = test_result->Get(0)->BooleanValue(isolate);
+  v8::Local<v8::Context> context = isolate->GetCurrentContext();
+  g_test_result_ok =
+      test_result->Get(context, 0).ToLocalChecked()->BooleanValue(isolate);
   if (!g_test_result_ok) {
-    v8::String::Utf8Value message(isolate, test_result->Get(1));
+    v8::String::Utf8Value message(
+        isolate, test_result->Get(context, 1).ToLocalChecked());
     LOG(ERROR) << *message;
   }
 }
diff --git a/chrome/test/chromedriver/commands_unittest.cc b/chrome/test/chromedriver/commands_unittest.cc
index 3a27261..7b730e1 100644
--- a/chrome/test/chromedriver/commands_unittest.cc
+++ b/chrome/test/chromedriver/commands_unittest.cc
@@ -15,10 +15,11 @@
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
 #include "base/location.h"
-#include "base/message_loop/message_loop.h"
+
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/lock.h"
+#include "base/test/scoped_task_environment.h"
 #include "base/threading/thread.h"
 #include "base/values.h"
 #include "chrome/test/chromedriver/chrome/status.h"
@@ -148,7 +149,7 @@
   Command cmd = base::Bind(&ExecuteStubGetSession, &count);
 
   base::DictionaryValue params;
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
 
   ExecuteGetSessions(cmd, &map, params, std::string(),
                      base::Bind(&OnGetSessions));
@@ -191,7 +192,7 @@
   int count = 0;
   Command cmd = base::Bind(&ExecuteStubQuit, &count);
   base::DictionaryValue params;
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   ExecuteQuitAll(cmd, &map, params, std::string(), base::Bind(&OnQuitAll));
   ASSERT_EQ(2, count);
 }
@@ -242,7 +243,7 @@
   SessionCommand cmd = base::Bind(
       &ExecuteSimpleCommand, id, &params, &expected_value);
 
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   base::RunLoop run_loop;
   ExecuteSessionCommand(
       &map,
@@ -327,7 +328,7 @@
   std::string id("id");
   map[id] = std::move(thread);
 
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   base::RunLoop run_loop;
   ExecuteSessionCommand(&map,
                         "cmd",
@@ -733,7 +734,7 @@
   // verify the listener was called. The session owns and will destroy |proxy|.
   SessionCommand cmd =
       base::Bind(&ExecuteAddListenerToSessionCommand, base::Passed(&proxy));
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   base::RunLoop run_loop_addlistener;
 
   // |CommandListener|s are notified immediately before commands are run.
@@ -827,7 +828,7 @@
   base::DictionaryValue params;
   // The command should never be executed if BeforeCommand fails for a listener.
   SessionCommand cmd = base::Bind(&ShouldNotBeCalled);
-  base::MessageLoop loop;
+  base::test::ScopedTaskEnvironment scoped_task_environment;
   base::RunLoop run_loop;
 
   ExecuteSessionCommand(
diff --git a/chrome/test/data/banners/nested_sw_test_page.html b/chrome/test/data/banners/nested_sw_test_page.html
new file mode 100644
index 0000000..456033219
--- /dev/null
+++ b/chrome/test/data/banners/nested_sw_test_page.html
@@ -0,0 +1,22 @@
+<html>
+  <head>
+    <title>Web app banner test page</title>
+    <script src="main.js"></script>
+    <script>
+      // If a "manifest=/path/to/manifest.json" query argument is provided to
+      // the URL accessing this page, that path is injected as the manifest tag.
+      // Otherwise, "manifest.json" is used as the manifest tag.
+      addManifestLinkTag();
+    </script>
+    <script>
+      // Register page scoped service worker.
+      navigator.serviceWorker.register('service_worker_with_no_fetch_handler.js', { scope: 'nested_sw_test_page.html' });
+
+      // Register app scoped service worker.
+      navigator.serviceWorker.register('service_worker.js');
+    </script>
+  </head>
+  <body onload="initialize()">
+    Do-nothing page with a manifest and a service worker for '/' and for 'nested_sw_test_page.html'.
+  </body>
+</html>
diff --git a/chrome/test/data/client_hints/accept_ch_with_lifetime.html.mock-http-headers b/chrome/test/data/client_hints/accept_ch_with_lifetime.html.mock-http-headers
index 79c04b9..6e22fe119 100644
--- a/chrome/test/data/client_hints/accept_ch_with_lifetime.html.mock-http-headers
+++ b/chrome/test/data/client_hints/accept_ch_with_lifetime.html.mock-http-headers
@@ -1,3 +1,3 @@
 HTTP/1.1 200 OK
-Accept-CH: dpr,device-memory,viewport-width,rtt,downlink,ect
-Accept-CH-Lifetime: 3600
\ No newline at end of file
+Accept-CH: dpr,device-memory,viewport-width,rtt,downlink,ect,lang
+Accept-CH-Lifetime: 3600
diff --git a/chrome/test/data/client_hints/accept_ch_without_lifetime.html.mock-http-headers b/chrome/test/data/client_hints/accept_ch_without_lifetime.html.mock-http-headers
index 9e5f7cfc..0234b8c 100644
--- a/chrome/test/data/client_hints/accept_ch_without_lifetime.html.mock-http-headers
+++ b/chrome/test/data/client_hints/accept_ch_without_lifetime.html.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Accept-CH: dpr,device-memory,viewport-width,rtt,downlink,ect
\ No newline at end of file
+Accept-CH: dpr,device-memory,viewport-width,rtt,downlink,ect,lang
diff --git a/chrome/test/data/client_hints/accept_ch_without_lifetime_img_localhost.html.mock-http-headers b/chrome/test/data/client_hints/accept_ch_without_lifetime_img_localhost.html.mock-http-headers
index 9e5f7cfc..0234b8c 100644
--- a/chrome/test/data/client_hints/accept_ch_without_lifetime_img_localhost.html.mock-http-headers
+++ b/chrome/test/data/client_hints/accept_ch_without_lifetime_img_localhost.html.mock-http-headers
@@ -1,2 +1,2 @@
 HTTP/1.1 200 OK
-Accept-CH: dpr,device-memory,viewport-width,rtt,downlink,ect
\ No newline at end of file
+Accept-CH: dpr,device-memory,viewport-width,rtt,downlink,ect,lang
diff --git a/chrome/test/data/client_hints/http_equiv_accept_ch_with_lifetime.html b/chrome/test/data/client_hints/http_equiv_accept_ch_with_lifetime.html
index 6f52936..d3c6d8cf 100644
--- a/chrome/test/data/client_hints/http_equiv_accept_ch_with_lifetime.html
+++ b/chrome/test/data/client_hints/http_equiv_accept_ch_with_lifetime.html
@@ -1,5 +1,5 @@
 <html>
-<meta http-equiv="Accept-CH" content="dpr,device-memory,viewport-width,rtt,downlink,ect">
+<meta http-equiv="Accept-CH" content="dpr,device-memory,viewport-width,rtt,downlink,ect,lang">
 <meta http-equiv="Accept-CH-Lifetime" content="3600">
 <link rel="icon" href="data:;base64,=">
 </html>
diff --git a/chrome/test/data/client_hints/http_equiv_accept_ch_without_lifetime.html b/chrome/test/data/client_hints/http_equiv_accept_ch_without_lifetime.html
index 454c025..19f02f1 100644
--- a/chrome/test/data/client_hints/http_equiv_accept_ch_without_lifetime.html
+++ b/chrome/test/data/client_hints/http_equiv_accept_ch_without_lifetime.html
@@ -1,5 +1,5 @@
 <html>
-<meta http-equiv="Accept-CH" content="dpr,device-memory,viewport-width,rtt,downlink,ect">
+<meta http-equiv="Accept-CH" content="dpr,device-memory,viewport-width,rtt,downlink,ect,lang">
 <link rel="icon" href="data:;base64,=">
 <head></head>
 Empty file which uses link-rel to disable favicon fetches. The corresponding
diff --git a/chrome/test/data/client_hints/http_equiv_accept_ch_without_lifetime_img_localhost.html b/chrome/test/data/client_hints/http_equiv_accept_ch_without_lifetime_img_localhost.html
index 7b56475f..02e61fa 100644
--- a/chrome/test/data/client_hints/http_equiv_accept_ch_without_lifetime_img_localhost.html
+++ b/chrome/test/data/client_hints/http_equiv_accept_ch_without_lifetime_img_localhost.html
@@ -1,5 +1,5 @@
 <html>
-<meta http-equiv="Accept-CH" content="dpr,device-memory,viewport-width,rtt,downlink,ect">
+<meta http-equiv="Accept-CH" content="dpr,device-memory,viewport-width,rtt,downlink,ect,lang">
 <link rel="icon" href="data:;base64,=">
 <head></head>
 Empty file which uses link-rel to disable favicon fetches. The corresponding
diff --git a/chrome/test/data/extensions/api_test/bindings/user_gesture_test.html b/chrome/test/data/extensions/api_test/bindings/user_gesture_test.html
new file mode 100644
index 0000000..ec6a08d5
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/bindings/user_gesture_test.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html>
+<style>
+  html {
+    margin: 0;
+    padding: 0;
+  }
+  body {
+    margin: 0;
+    padding: 0;
+    min-width: 100px;
+    min-height: 100px;
+  }
+</style>
+<body>
+</body>
+<script src="user_gesture_test.js"></script>
+</html>
diff --git a/chrome/test/data/extensions/api_test/bindings/user_gesture_test.js b/chrome/test/data/extensions/api_test/bindings/user_gesture_test.js
new file mode 100644
index 0000000..71722c1
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/bindings/user_gesture_test.js
@@ -0,0 +1,16 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var resolveEntereedFullscreen;
+window.getEnteredFullscreen = new Promise((resolve) => {
+  resolveEnteredFullscreen = resolve;
+});
+
+document.body.onclick = function() {
+  document.body.requestFullscreen().then(() => {
+    resolveEnteredFullscreen('success');
+  }).catch(() => {
+    resolveEnteredFullscreen('failure');
+  });
+};
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js b/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
index 04d1eea2..dc0f2666 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_extra_headers.js
@@ -184,4 +184,29 @@
       }));
     });
   },
+
+  function testRedirectToUrlWithExtraHeadersListener() {
+    // Set a cookie so the cookie request header is set.
+    navigateAndWait(getSetCookieUrl('foo', 'bar'), function() {
+      var finalURL = getServerURL('echoheader?Cookie');
+      var url = getServerURL('server-redirect?' + finalURL);
+      var listener = callbackPass(function(details) {
+        removeHeader(details.requestHeaders, 'cookie');
+        return {requestHeaders: details.requestHeaders};
+      });
+      chrome.webRequest.onBeforeSendHeaders.addListener(listener,
+          {urls: [finalURL]}, ['requestHeaders', 'blocking', 'extraHeaders']);
+
+      navigateAndWait(url, function(tab) {
+        chrome.test.assertEq(finalURL, tab.url);
+        chrome.webRequest.onBeforeSendHeaders.removeListener(listener);
+        chrome.tabs.executeScript(tab.id, {
+          code: 'document.body.innerText'
+        }, callbackPass(function(results) {
+          chrome.test.assertTrue(results[0].indexOf('bar') == -1,
+              'Header not removed.');
+        }));
+      });
+    });
+  },
 ]);
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_redirects.js b/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
index d4c19779..6d593a9 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
@@ -27,203 +27,253 @@
   });
 }
 
-runTests([
-  function redirectToDataUrlOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: dataURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+chrome.test.getConfig(function(config) {
+  var onHeadersReceivedExtraInfoSpec = ['blocking'];
+  if (config.customArg === 'useExtraHeaders')
+    onHeadersReceivedExtraInfoSpec.push('extraHeaders');
 
-    assertRedirectSucceeds(url, dataURL, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+  runTests([
+    function redirectToDataUrlOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: dataURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function redirectToAboutUrlOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: aboutURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, dataURL, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, aboutURL, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function redirectToAboutUrlOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: aboutURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function redirectToNonWebAccessibleUrlOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: getURLNonWebAccessible()};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, aboutURL, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function redirectToNonWebAccessibleUrlOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: getURLNonWebAccessible()};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function redirectToServerRedirectOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' + getURLWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLWebAccessible(), function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function redirectToServerRedirectOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function serverRedirectThenExtensionRedirectOnHeadersReceived() {
-    var url_1 = getServerURL('echo');
-    var url_2 = getURLWebAccessible();
-    var serverRedirect = getServerURL('server-redirect?' + url_1);
-    var listener = function(details) {
-      return {redirectUrl: url_2};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(
-      listener,
-      { urls: [url_1] },
-      ["blocking"]
-    );
+      assertRedirectSucceeds(url, getURLWebAccessible(), function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(serverRedirect, url_2, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function serverRedirectThenExtensionRedirectOnHeadersReceived() {
+      var url_1 = getServerURL('echo');
+      var url_2 = getURLWebAccessible();
+      var serverRedirect = getServerURL('server-redirect?' + url_1);
+      var listener = function(details) {
+        return {redirectUrl: url_2};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(
+        listener,
+        { urls: [url_1] },
+        ["blocking"]
+      );
 
-  function redirectToUnallowedServerRedirectOnHeadersReceived() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' +
-        getURLNonWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onHeadersReceived.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(serverRedirect, url_2, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    // The page should be redirected to redirectURL, but not to the non web
-    // accessible URL.
-    assertRedirectSucceeds(url, redirectURL, function() {
-      chrome.webRequest.onHeadersReceived.removeListener(listener);
-    });
-  },
+    function redirectToUnallowedServerRedirectOnHeadersReceived() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLNonWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-  function redirectToDataUrlOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: dataURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      // The page should be redirected to redirectURL, but not to the non web
+      // accessible URL.
+      assertRedirectSucceeds(url, redirectURL, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, dataURL, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToDataUrlOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: dataURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function redirectToAboutUrlOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: aboutURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, dataURL, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, aboutURL, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToAboutUrlOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: aboutURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function redirectToNonWebAccessibleUrlOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var listener = function(details) {
-      return {redirectUrl: getURLNonWebAccessible()};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, aboutURL, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToNonWebAccessibleUrlOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var listener = function(details) {
+        return {redirectUrl: getURLNonWebAccessible()};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function redirectToServerRedirectOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' + getURLWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(url, getURLWebAccessible(), function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToServerRedirectOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  // A server redirect immediately followed by an extension redirect.
-  // Regression test for:
-  // - https://crbug.com/882661
-  // - https://crbug.com/880741
-  function serverRedirectThenExtensionRedirectOnBeforeRequest() {
-    var url_1 = getServerURL('echo');
-    var url_2 = getURLWebAccessible();
-    var serverRedirect = getServerURL('server-redirect?' + url_1);
-    var listener = function(details) {
-      return {redirectUrl: url_2};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(
-      listener,
-      { urls: [url_1] },
-      ["blocking"]
-    );
+      assertRedirectSucceeds(url, getURLWebAccessible(), function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    assertRedirectSucceeds(serverRedirect, url_2, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    // A server redirect immediately followed by an extension redirect.
+    // Regression test for:
+    // - https://crbug.com/882661
+    // - https://crbug.com/880741
+    function serverRedirectThenExtensionRedirectOnBeforeRequest() {
+      var url_1 = getServerURL('echo');
+      var url_2 = getURLWebAccessible();
+      var serverRedirect = getServerURL('server-redirect?' + url_1);
+      var listener = function(details) {
+        return {redirectUrl: url_2};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(
+        listener,
+        { urls: [url_1] },
+        ["blocking"]
+      );
 
-  function redirectToUnallowedServerRedirectOnBeforeRequest() {
-    var url = getServerURL('echo');
-    var redirectURL = getServerURL('server-redirect?' +
-        getURLNonWebAccessible());
-    var listener = function(details) {
-      return {redirectUrl: redirectURL};
-    };
-    chrome.webRequest.onBeforeRequest.addListener(listener,
-        {urls: [url]}, ['blocking']);
+      assertRedirectSucceeds(serverRedirect, url_2, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-    // The page should be redirected to redirectURL, but not to the non web
-    // accessible URL.
-    assertRedirectSucceeds(url, redirectURL, function() {
-      chrome.webRequest.onBeforeRequest.removeListener(listener);
-    });
-  },
+    function redirectToUnallowedServerRedirectOnBeforeRequest() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getURLNonWebAccessible());
+      var listener = function(details) {
+        return {redirectUrl: redirectURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(listener,
+          {urls: [url]}, ['blocking']);
 
-  function redirectToAboutUrlWithServerRedirect() {
-    assertRedirectFails(getServerURL('server-redirect?' + aboutURL));
-  },
+      // The page should be redirected to redirectURL, but not to the non web
+      // accessible URL.
+      assertRedirectSucceeds(url, redirectURL, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(listener);
+      });
+    },
 
-  function redirectToDataUrlWithServerRedirect() {
-    assertRedirectFails(getServerURL('server-redirect?' + dataURL));
-  },
+    function redirectToAboutUrlWithServerRedirect() {
+      assertRedirectFails(getServerURL('server-redirect?' + aboutURL));
+    },
 
-  function redirectToNonWebAccessibleUrlWithServerRedirect() {
-    assertRedirectFails(
-        getServerURL('server-redirect?' + getURLNonWebAccessible()));
-  },
+    function redirectToDataUrlWithServerRedirect() {
+      assertRedirectFails(getServerURL('server-redirect?' + dataURL));
+    },
 
-  function redirectToWebAccessibleUrlWithServerRedirect() {
-    assertRedirectSucceeds(
-        getServerURL('server-redirect?' + getURLWebAccessible()),
-        getURLWebAccessible());
-  },
-]);
+    function redirectToNonWebAccessibleUrlWithServerRedirect() {
+      assertRedirectFails(
+          getServerURL('server-redirect?' + getURLNonWebAccessible()));
+    },
+
+    function redirectToWebAccessibleUrlWithServerRedirect() {
+      assertRedirectSucceeds(
+          getServerURL('server-redirect?' + getURLWebAccessible()),
+          getURLWebAccessible());
+    },
+
+    function beforeRequestRedirectAfterServerRedirect() {
+      var finalURL = getServerURL('echo?foo');
+      var intermediateURL = getServerURL('echo?bar');
+      var redirectURL = getServerURL('server-redirect?' + intermediateURL);
+
+      var onBeforeSendHeadersListener = function(details) {
+        chrome.test.assertFalse(details.url == intermediateURL,
+            'intermediateURL should be redirected before the request starts.');
+      };
+      // Make sure all URLs use the extraHeaders path to expose
+      // http://crbug.com/918761.
+      chrome.webRequest.onBeforeSendHeaders.addListener(
+          onBeforeSendHeadersListener,
+          {urls: ['<all_urls>']}, ['blocking', 'extraHeaders']);
+
+      var onBeforeRequestListener = function(details) {
+        return {redirectUrl: finalURL};
+      };
+      chrome.webRequest.onBeforeRequest.addListener(onBeforeRequestListener,
+          {urls: [intermediateURL]}, ['blocking']);
+
+      assertRedirectSucceeds(redirectURL, finalURL, function() {
+        chrome.webRequest.onBeforeRequest.removeListener(
+            onBeforeRequestListener);
+        chrome.webRequest.onBeforeSendHeaders.removeListener(
+            onBeforeSendHeadersListener);
+      });
+    },
+
+    function serverRedirectChain() {
+      var url = getServerURL('echo');
+      var redirectURL = getServerURL('server-redirect?' +
+          getServerURL('server-redirect?' + url));
+      var listener = function(details) {};
+      chrome.webRequest.onHeadersReceived.addListener(listener,
+          {urls: ['<all_urls>']}, onHeadersReceivedExtraInfoSpec);
+
+      assertRedirectSucceeds(redirectURL, url, function() {
+        chrome.webRequest.onHeadersReceived.removeListener(listener);
+      });
+    },
+  ]);
+});
diff --git a/chrome/test/data/load_image/image.html b/chrome/test/data/load_image/image.html
new file mode 100644
index 0000000..e995a16e
--- /dev/null
+++ b/chrome/test/data/load_image/image.html
@@ -0,0 +1 @@
+<img alt="long_placeholder_text" src="image.png" />
diff --git a/chrome/test/data/load_image/image.png b/chrome/test/data/load_image/image.png
new file mode 100644
index 0000000..12070dd5
--- /dev/null
+++ b/chrome/test/data/load_image/image.png
Binary files differ
diff --git a/chrome/test/data/load_image/image_with_map.html b/chrome/test/data/load_image/image_with_map.html
new file mode 100644
index 0000000..f97f199
--- /dev/null
+++ b/chrome/test/data/load_image/image_with_map.html
@@ -0,0 +1,4 @@
+<img alt="placeholder_text" src="image.png" usemap="#imgmap" width="50" height="50" />
+<map name="imgmap">
+    <area shape="rect" coords="0,0,30,30" alt="top_left_corner" />
+</map>
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index 4af0267..deea4c2 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -4187,6 +4187,12 @@
     "pref_mappings": [{ "pref": "child_user.parent_access_code.config" }]
   },
 
+  "CertificateManagementAllowed": {
+    "os": ["chromeos"],
+    "test_policy": { "CertificateManagementAllowed": 2 },
+    "pref_mappings": [{ "pref": "certificate_management_allowed" }]
+  },
+
   "----- Chrome Frame policies -------------------------------------------": {},
 
   "ChromeFrameRendererSettings": {
diff --git a/chrome/test/data/webui/downloads/manager_tests.js b/chrome/test/data/webui/downloads/manager_tests.js
index 4ed9e60..eac56aa 100644
--- a/chrome/test/data/webui/downloads/manager_tests.js
+++ b/chrome/test/data/webui/downloads/manager_tests.js
@@ -6,7 +6,7 @@
   /** @type {!downloads.Manager} */
   let manager;
 
-  /** @type {!mdDownloads.mojom.PageHandlerCallbackRouter} */
+  /** @type {!downloads.mojom.PageHandlerCallbackRouter} */
   let pageRouterProxy;
 
   /** @type {TestDownloadsProxy} */
diff --git a/chrome/test/data/webui/downloads/test_support.js b/chrome/test/data/webui/downloads/test_support.js
index 713ab7d..c631a9b 100644
--- a/chrome/test/data/webui/downloads/test_support.js
+++ b/chrome/test/data/webui/downloads/test_support.js
@@ -4,22 +4,22 @@
 
 class TestDownloadsProxy {
   constructor() {
-    /** @type {mdDownloads.mojom.PageCallbackRouter} */
-    this.callbackRouter = new mdDownloads.mojom.PageCallbackRouter();
+    /** @type {downloads.mojom.PageCallbackRouter} */
+    this.callbackRouter = new downloads.mojom.PageCallbackRouter();
 
-    /** @type {!mdDownloads.mojom.PageInterface} */
+    /** @type {!downloads.mojom.PageInterface} */
     this.pageRouterProxy = this.callbackRouter.createProxy();
 
-    /** @type {mdDownloads.mojom.PageHandlerInterface} */
+    /** @type {downloads.mojom.PageHandlerInterface} */
     this.handler = new TestDownloadsMojoHandler(this.pageRouterProxy);
   }
 }
 
-/** @implements {mdDownloads.mojom.PageHandlerInterface} */
+/** @implements {downloads.mojom.PageHandlerInterface} */
 class TestDownloadsMojoHandler {
-  /** @param {mdDownloads.mojom.PageInterface} */
+  /** @param {downloads.mojom.PageInterface} */
   constructor(pageRouterProxy) {
-    /** @private {mdDownloads.mojom.PageInterface} */
+    /** @private {downloads.mojom.PageInterface} */
     this.pageRouterProxy_ = pageRouterProxy;
 
     /** @private {TestBrowserProxy} */
diff --git a/chrome/test/data/webui/settings/android_apps_page_test.js b/chrome/test/data/webui/settings/android_apps_page_test.js
index 7ed4174..468d7ddb 100644
--- a/chrome/test/data/webui/settings/android_apps_page_test.js
+++ b/chrome/test/data/webui/settings/android_apps_page_test.js
@@ -93,23 +93,20 @@
 
     test('Sanity', function() {
       assertTrue(!!subpage.$$('#remove'));
-      assertTrue(!subpage.$$('settings-android-settings-element'));
+      assertTrue(!subpage.$$('#manageApps'));
     });
 
     test('ManageAppsUpdate', function() {
-      assertTrue(!subpage.$$('settings-android-settings-element'));
+      assertTrue(!subpage.$$('#manageApps'));
       setAndroidAppsState(true, true);
-      assertTrue(!!subpage.$$('settings-android-settings-element'));
-      assertTrue(
-          !!subpage.$$('settings-android-settings-element').$$('#manageApps'));
+      assertTrue(!!subpage.$$('#manageApps'));
       setAndroidAppsState(true, false);
-      assertTrue(!subpage.$$('settings-android-settings-element'));
+      assertTrue(!subpage.$$('#manageApps'));
     });
 
     test('ManageAppsOpenRequest', function() {
       setAndroidAppsState(true, true);
-      const button =
-          subpage.$$('settings-android-settings-element').$$('#manageApps');
+      const button = subpage.$$('#manageApps');
       assertTrue(!!button);
       const promise =
           androidAppsBrowserProxy.whenCalled('showAndroidAppsSettings');
@@ -165,9 +162,7 @@
     test('Sanity', function() {
       Polymer.dom.flush();
       assertFalse(!!subpage.$$('#remove'));
-      assertTrue(!!subpage.$$('settings-android-settings-element'));
-      assertTrue(
-          !!subpage.$$('settings-android-settings-element').$$('#manageApps'));
+      assertTrue(!!subpage.$$('#manageApps'));
     });
   });
 
@@ -179,14 +174,11 @@
     });
 
     test('Sanity', function() {
-      assertTrue(!!androidAppsPage.$$('settings-android-settings-element'));
-      assertTrue(!!androidAppsPage.$$('settings-android-settings-element')
-                       .$$('#manageApps'));
+      assertTrue(!!androidAppsPage.$$('#manageApps'));
     });
 
     test('ManageAppsOpenRequest', function() {
-      const button = androidAppsPage.$$('settings-android-settings-element')
-                         .$$('#manageApps');
+      const button = androidAppsPage.$$('#manageApps');
       assertTrue(!!button);
       const promise =
           androidAppsBrowserProxy.whenCalled('showAndroidAppsSettings');
diff --git a/chrome/test/data/webui/settings/bluetooth_page_tests.js b/chrome/test/data/webui/settings/bluetooth_page_tests.js
index 2672009..831c4eb 100644
--- a/chrome/test/data/webui/settings/bluetooth_page_tests.js
+++ b/chrome/test/data/webui/settings/bluetooth_page_tests.js
@@ -22,10 +22,10 @@
   let bluetoothPage = null;
 
   /** @type {Bluetooth} */
-  let bluetoothApi_;
+  let bluetoothApi;
 
   /** @type {BluetoothPrivate} */
-  let bluetoothPrivateApi_;
+  let bluetoothPrivateApi;
 
   /** @type {!chrome.bluetooth.Device} */
   let fakeUnpairedDevice1 = {
@@ -44,6 +44,14 @@
   };
 
   /** @type {!chrome.bluetooth.Device} */
+  let fakeUnpairedDevice3 = {
+    address: '00:00:00:00:00:03',
+    name: 'FakeUnpairedDevice3',
+    paired: false,
+    connected: false,
+  };
+
+  /** @type {!chrome.bluetooth.Device} */
   let fakePairedDevice1 = {
     address: '10:00:00:00:00:01',
     name: 'FakePairedDevice1',
@@ -59,6 +67,14 @@
     connected: false,
   };
 
+  /** @type {!chrome.bluetooth.Device} */
+  let fakePairedDevice3 = {
+    address: '10:00:00:00:00:03',
+    name: 'FakePairedDevice3',
+    paired: true,
+    connected: false,
+  };
+
   suiteSetup(function() {
     loadTimeData.overrideValues({
       deviceOff: 'deviceOff',
@@ -69,12 +85,12 @@
       bluetoothStartConnecting: 'bluetoothStartConnecting',
     });
 
-    bluetoothApi_ = new settings.FakeBluetooth();
-    bluetoothPrivateApi_ = new settings.FakeBluetoothPrivate(bluetoothApi_);
+    bluetoothApi = new settings.FakeBluetooth();
+    bluetoothPrivateApi = new settings.FakeBluetoothPrivate(bluetoothApi);
 
     // Set globals to override Settings Bluetooth Page apis.
-    bluetoothApis.bluetoothApiForTest = bluetoothApi_;
-    bluetoothApis.bluetoothPrivateApiForTest = bluetoothPrivateApi_;
+    bluetoothApis.bluetoothApiForTest = bluetoothApi;
+    bluetoothApis.bluetoothPrivateApiForTest = bluetoothPrivateApi;
 
     // Disable animations so sub-pages open within one event loop.
     testing.Test.disableAnimationsAndTransitions();
@@ -86,7 +102,7 @@
     bluetoothPage.prefs = getFakePrefs();
     assertTrue(!!bluetoothPage);
 
-    bluetoothApi_.setDevicesForTest([]);
+    bluetoothApi.clearDevicesForTest();
     document.body.appendChild(bluetoothPage);
     Polymer.dom.flush();
   });
@@ -96,14 +112,14 @@
   });
 
   test('MainPage', function() {
-    assertFalse(bluetoothApi_.getAdapterStateForTest().powered);
+    assertFalse(bluetoothApi.getAdapterStateForTest().powered);
     assertFalse(bluetoothPage.bluetoothToggleState_);
     // Test that tapping the single settings-box div enables bluetooth.
     const div = bluetoothPage.$$('div.settings-box');
     assertTrue(!!div);
     div.click();
     assertTrue(bluetoothPage.bluetoothToggleState_);
-    assertTrue(bluetoothApi_.getAdapterStateForTest().powered);
+    assertTrue(bluetoothApi.getAdapterStateForTest().powered);
   });
 
   suite('SubPage', function() {
@@ -117,7 +133,7 @@
     }
 
     setup(async function() {
-      bluetoothApi_.setEnabled(true);
+      bluetoothApi.setEnabled(true);
       Polymer.dom.flush();
       const div = bluetoothPage.$$('div.settings-box');
       div.click();
@@ -141,7 +157,7 @@
 
       subpage.bluetoothToggleState = false;
       assertFalse(enableButton.checked);
-      assertFalse(bluetoothApi_.getAdapterStateForTest().powered);
+      assertFalse(bluetoothApi.getAdapterStateForTest().powered);
       assertFalse(bluetoothPage.bluetoothToggleState_);
     });
 
@@ -156,7 +172,7 @@
     }
 
     test('pair device', async function() {
-      bluetoothApi_.setDevicesForTest([
+      bluetoothApi.simulateDevicesAddedForTest([
         fakeUnpairedDevice1, fakeUnpairedDevice2, fakePairedDevice1,
         fakePairedDevice2
       ]);
@@ -169,7 +185,7 @@
 
       const address = subpage.unpairedDeviceList_[0].address;
       await new Promise(
-          resolve => bluetoothPrivateApi_.connect(address, resolve));
+          resolve => bluetoothPrivateApi.connect(address, resolve));
 
       Polymer.dom.flush();
       assertEquals(3, subpage.pairedDeviceList_.length);
@@ -177,7 +193,7 @@
     });
 
     test('pair dialog', async function() {
-      bluetoothApi_.setDevicesForTest([
+      bluetoothApi.simulateDevicesAddedForTest([
         fakeUnpairedDevice1, fakeUnpairedDevice2, fakePairedDevice1,
         fakePairedDevice2
       ]);
@@ -227,8 +243,9 @@
         assertTrue(!!pairedDeviceIronList);
       });
 
-      test('Unpaired devices: devices added', async function() {
-        bluetoothApi_.setDevicesForTest(
+      test('Unpaired devices: added and removed', async function() {
+        // Add two unpaired devices.
+        bluetoothApi.simulateDevicesAddedForTest(
             [fakeUnpairedDevice1, fakeUnpairedDevice2]);
         await waitForListUpdateTimeout();
         Polymer.dom.flush();
@@ -239,6 +256,11 @@
         assertTrue(subpage.$.noUnpairedDevices.hidden);
         assertFalse(subpage.$.noPairedDevices.hidden);
 
+        assertEquals(
+            unpairedDeviceList()[0].address, fakeUnpairedDevice1.address);
+        assertEquals(
+            unpairedDeviceList()[1].address, fakeUnpairedDevice2.address);
+
         unpairedDeviceIronList.notifyResize();
         Polymer.dom.flush();
 
@@ -247,10 +269,94 @@
         assertEquals(2, devices.length);
         assertFalse(devices[0].device.paired);
         assertFalse(devices[1].device.paired);
+
+        // Remove the first device.
+        bluetoothApi.simulateDevicesRemovedForTest(
+            [fakeUnpairedDevice1.address]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(1, deviceList().length);
+        assertEquals(1, unpairedDeviceList().length);
+        assertEquals(0, pairedDeviceList().length);
+        assertTrue(subpage.$.noUnpairedDevices.hidden);
+        assertFalse(subpage.$.noPairedDevices.hidden);
+
+        assertEquals(
+            unpairedDeviceList()[0].address, fakeUnpairedDevice2.address);
+
+        // Add the first device again. Since the devices are always sorted by
+        // address, the new device will be added at the beginning of the list.
+        // TODO(ortuno): Devices should always be added at the end of the list.
+        bluetoothApi.simulateDevicesAddedForTest([fakeUnpairedDevice1]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(2, deviceList().length);
+        assertEquals(2, unpairedDeviceList().length);
+        assertEquals(0, pairedDeviceList().length);
+        assertTrue(subpage.$.noUnpairedDevices.hidden);
+        assertFalse(subpage.$.noPairedDevices.hidden);
+
+        assertEquals(
+            unpairedDeviceList()[0].address, fakeUnpairedDevice1.address);
+        assertEquals(
+            unpairedDeviceList()[1].address, fakeUnpairedDevice2.address);
+
+        // Remove both devices.
+        bluetoothApi.simulateDevicesRemovedForTest(
+            [fakeUnpairedDevice1.address, fakeUnpairedDevice2.address]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(0, deviceList().length);
+        assertEquals(0, unpairedDeviceList().length);
+        assertEquals(0, pairedDeviceList().length);
+        assertFalse(subpage.$.noUnpairedDevices.hidden);
+        assertFalse(subpage.$.noPairedDevices.hidden);
       });
 
-      test('Paired devices: devices added', async function() {
-        bluetoothApi_.setDevicesForTest([fakePairedDevice1, fakePairedDevice2]);
+      test('Unpaired devices: device updated', async function() {
+        // Add three unpaired devices.
+        bluetoothApi.simulateDevicesAddedForTest(
+            [fakeUnpairedDevice1, fakeUnpairedDevice2, fakeUnpairedDevice3]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(3, deviceList().length);
+        assertEquals(3, unpairedDeviceList().length);
+        assertEquals(0, pairedDeviceList().length);
+        assertTrue(subpage.$.noUnpairedDevices.hidden);
+        assertFalse(subpage.$.noPairedDevices.hidden);
+
+        // Update the one in the middle.
+        let updatedDevice = Object.assign({}, fakeUnpairedDevice2);
+        updatedDevice.name = 'Updated Name';
+        bluetoothApi.simulateDeviceUpdatedForTest(updatedDevice);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(3, deviceList().length);
+        assertEquals(3, unpairedDeviceList().length);
+        assertEquals(0, pairedDeviceList().length);
+        assertTrue(subpage.$.noUnpairedDevices.hidden);
+        assertFalse(subpage.$.noPairedDevices.hidden);
+
+        for (const prop in updatedDevice) {
+          assertEquals(updatedDevice[prop], deviceList()[1][prop]);
+          assertEquals(updatedDevice[prop], unpairedDeviceList()[1][prop]);
+        }
+      });
+
+      test('Paired devices: devices added and removed', async function() {
+        // Add two paired devices.
+        bluetoothApi.simulateDevicesAddedForTest(
+            [fakePairedDevice1, fakePairedDevice2]);
         await waitForListUpdateTimeout();
         Polymer.dom.flush();
 
@@ -270,10 +376,154 @@
         assertTrue(devices[0].device.connected);
         assertTrue(devices[1].device.paired);
         assertFalse(devices[1].device.connected);
+
+        // Remove the first device.
+        bluetoothApi.simulateDevicesRemovedForTest([fakePairedDevice1.address]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(1, deviceList().length);
+        assertEquals(0, unpairedDeviceList().length);
+        assertEquals(1, pairedDeviceList().length);
+        assertFalse(subpage.$.noUnpairedDevices.hidden);
+        assertTrue(subpage.$.noPairedDevices.hidden);
+
+        assertEquals(pairedDeviceList()[0].address, fakePairedDevice2.address);
+
+        // Add the first device again. Since the devices are always sorted by
+        // address, the new device will be added at the beginning of the list.
+        // TODO(ortuno): Devices should always be added at the end of the list.
+        bluetoothApi.simulateDevicesAddedForTest([fakePairedDevice1]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(2, deviceList().length);
+        assertEquals(0, unpairedDeviceList().length);
+        assertEquals(2, pairedDeviceList().length);
+        assertFalse(subpage.$.noUnpairedDevices.hidden);
+        assertTrue(subpage.$.noPairedDevices.hidden);
+
+        assertEquals(pairedDeviceList()[0].address, fakePairedDevice1.address);
+        assertEquals(pairedDeviceList()[1].address, fakePairedDevice2.address);
+
+        // Remove both devices.
+        bluetoothApi.simulateDevicesRemovedForTest(
+            [fakePairedDevice1.address, fakePairedDevice2.address]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(0, deviceList().length);
+        assertEquals(0, unpairedDeviceList().length);
+        assertEquals(0, pairedDeviceList().length);
+        assertFalse(subpage.$.noUnpairedDevices.hidden);
+        assertFalse(subpage.$.noPairedDevices.hidden);
+      });
+
+      test('Paired devices: device updated', async function() {
+        // Add three paired devices.
+        bluetoothApi.simulateDevicesAddedForTest(
+            [fakePairedDevice1, fakePairedDevice2, fakePairedDevice3]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(3, deviceList().length);
+        assertEquals(0, unpairedDeviceList().length);
+        assertEquals(3, pairedDeviceList().length);
+        assertFalse(subpage.$.noUnpairedDevices.hidden);
+        assertTrue(subpage.$.noPairedDevices.hidden);
+
+        // Update the one in the middle.
+        let updatedDevice = Object.assign({}, fakePairedDevice2);
+        updatedDevice.name = 'Updated Name';
+        bluetoothApi.simulateDeviceUpdatedForTest(updatedDevice);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(3, deviceList().length);
+        assertEquals(0, unpairedDeviceList().length);
+        assertEquals(3, pairedDeviceList().length);
+        assertFalse(subpage.$.noUnpairedDevices.hidden);
+        assertTrue(subpage.$.noPairedDevices.hidden);
+
+        for (const prop in updatedDevice) {
+          assertEquals(updatedDevice[prop], deviceList()[1][prop]);
+          assertEquals(updatedDevice[prop], pairedDeviceList()[1][prop]);
+        }
+      });
+
+      test('Unpaired device becomes paired', async function() {
+        // Add unpaired device.
+        bluetoothApi.simulateDevicesAddedForTest([fakeUnpairedDevice1]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(1, deviceList().length);
+        assertEquals(1, unpairedDeviceList().length);
+        assertEquals(0, pairedDeviceList().length);
+
+        assertTrue(subpage.$.noUnpairedDevices.hidden);
+        assertFalse(subpage.$.noPairedDevices.hidden);
+
+        // Mark the device as paired.
+        let nowPairedDevice = Object.assign({}, fakeUnpairedDevice1);
+        nowPairedDevice.paired = true;
+        bluetoothApi.simulateDeviceUpdatedForTest(nowPairedDevice);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(1, deviceList().length);
+        assertEquals(0, unpairedDeviceList().length);
+        assertEquals(1, pairedDeviceList().length);
+
+        assertFalse(subpage.$.noUnpairedDevices.hidden);
+        assertTrue(subpage.$.noPairedDevices.hidden);
+
+        assertTrue(deviceList()[0].paired);
+        assertTrue(pairedDeviceList()[0].paired);
+      });
+
+      test('Paired device becomes unpaired', async function() {
+        // Add paired device.
+        bluetoothApi.simulateDevicesAddedForTest([fakePairedDevice1]);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(1, deviceList().length);
+        assertEquals(0, unpairedDeviceList().length);
+        assertEquals(1, pairedDeviceList().length);
+
+        assertFalse(subpage.$.noUnpairedDevices.hidden);
+        assertTrue(subpage.$.noPairedDevices.hidden);
+
+        // Mark the device as not paired.
+        let nowUnpairedDevice = Object.assign({}, fakePairedDevice1);
+        nowUnpairedDevice.paired = false;
+        bluetoothApi.simulateDeviceUpdatedForTest(nowUnpairedDevice);
+
+        await waitForListUpdateTimeout();
+        Polymer.dom.flush();
+
+        assertEquals(1, deviceList().length);
+        assertEquals(1, unpairedDeviceList().length);
+        assertEquals(0, pairedDeviceList().length);
+
+        assertTrue(subpage.$.noUnpairedDevices.hidden);
+        assertFalse(subpage.$.noPairedDevices.hidden);
+
+        assertFalse(deviceList()[0].paired);
+        assertFalse(unpairedDeviceList()[0].paired);
       });
 
       test('Unpaired and paired devices: devices added', async function() {
-        bluetoothApi_.setDevicesForTest([
+        bluetoothApi.simulateDevicesAddedForTest([
           fakeUnpairedDevice1, fakeUnpairedDevice2, fakePairedDevice1,
           fakePairedDevice2
         ]);
diff --git a/chrome/test/data/webui/settings/crostini_page_test.js b/chrome/test/data/webui/settings/crostini_page_test.js
index 6277eba3..90897c2 100644
--- a/chrome/test/data/webui/settings/crostini_page_test.js
+++ b/chrome/test/data/webui/settings/crostini_page_test.js
@@ -140,25 +140,26 @@
 
     test('Sanity', function() {
       assertEquals(
-          3, subpage.shadowRoot.querySelectorAll('.settings-box').length);
+          2, subpage.shadowRoot.querySelectorAll('.settings-box').length);
+      assertEquals(2, subpage.shadowRoot.querySelectorAll('.list-item').length);
     });
 
     test('Remove', function() {
       assertFalse(subpage.$.crostiniInstructionsRemove.hidden);
-      assertTrue(!!subpage.$$('.settings-box button'));
+      assertTrue(!!subpage.$$('.list-item button'));
       // Remove first shared path, still one left.
-      subpage.$$('.settings-box button').click();
+      subpage.$$('.list-item button').click();
       assertEquals(1, crostiniBrowserProxy.sharedPaths.length);
       setCrostiniPrefs(true, crostiniBrowserProxy.sharedPaths);
       return flushAsync()
           .then(() => {
             Polymer.dom.flush();
             assertEquals(
-                2, subpage.shadowRoot.querySelectorAll('.settings-box').length);
+                1, subpage.shadowRoot.querySelectorAll('.list-item').length);
             assertFalse(subpage.$.crostiniInstructionsRemove.hidden);
 
             // Remove remaining shared path, none left.
-            subpage.$$('.settings-box button').click();
+            subpage.$$('.list-item button').click();
             assertEquals(0, crostiniBrowserProxy.sharedPaths.length);
             setCrostiniPrefs(true, crostiniBrowserProxy.sharedPaths);
             return flushAsync();
@@ -166,7 +167,7 @@
           .then(() => {
             Polymer.dom.flush();
             assertEquals(
-                1, subpage.shadowRoot.querySelectorAll('.settings-box').length);
+                0, subpage.shadowRoot.querySelectorAll('.list-item').length);
             // Verify remove instructions are hidden.
             assertTrue(subpage.$.crostiniInstructionsRemove.hidden);
           });
diff --git a/chrome/test/data/webui/settings/fake_bluetooth.js b/chrome/test/data/webui/settings/fake_bluetooth.js
index 5497f17..c9cadea 100644
--- a/chrome/test/data/webui/settings/fake_bluetooth.js
+++ b/chrome/test/data/webui/settings/fake_bluetooth.js
@@ -42,17 +42,68 @@
       return Object.assign({}, this.adapterState_);
     },
 
+    clearDevicesForTest: function() {
+      this.devices.length = 0;
+    },
+
     /** @param {!Array<!chrome.bluetooth.Device>} devices */
-    setDevicesForTest: function(devices) {
-      for (const d of this.devices) {
-        this.onDeviceRemoved.callListeners(d);
+    simulateDevicesAddedForTest: function(devices) {
+      let newDevices = devices.slice();
+      // Make sure the new devices don't already exist.
+      for (const d of newDevices) {
+        const found = this.devices.find(element => {
+          return element.address == d.address;
+        });
+        assert(
+            !found,
+            'Device already added. Use ' +
+                'simulateDeviceUpdatedForTest to update existing ' +
+                'devices.');
       }
-      this.devices = devices.slice();
-      for (const d of this.devices) {
-        this.onDeviceAdded.callListeners(d);
+      this.devices.push(...newDevices);
+      // The underlying Bluetooth API always returns the devices sorted by
+      // address.
+      this.devices.sort((d1, d2) => {
+        if (d1.address < d2.address) {
+          return -1;
+        }
+        if (d1.address > d2.address) {
+          return 1;
+        }
+        return 0;
+      });
+
+      for (const newDevice of newDevices) {
+        this.onDeviceAdded.callListeners(newDevice);
       }
     },
 
+    /** @param {!Array<!String>} devices */
+    simulateDevicesRemovedForTest: function(deviceAddresses) {
+      for (const deviceAddress of deviceAddresses) {
+        const removedDeviceIndex = this.devices.findIndex(element => {
+          return element.address == deviceAddress;
+        });
+        assert(
+            removedDeviceIndex !== -1,
+            'Tried to remove a non-existent device.');
+
+        const [removedDevice] = this.devices.splice(removedDeviceIndex, 1);
+        this.onDeviceRemoved.callListeners(removedDevice);
+      }
+    },
+
+    /** @param {!chrome.bluetooth.Device} updateDevice */
+    simulateDeviceUpdatedForTest: function(updatedDevice) {
+      const updatedDeviceIndex = this.devices.findIndex(element => {
+        return element.address === updatedDevice.address;
+      });
+      assert(
+          updatedDeviceIndex !== -1, 'Tried to update a non-existent device.');
+      this.devices[updatedDeviceIndex] = updatedDevice;
+      this.onDeviceChanged.callListeners(updatedDevice);
+    },
+
     /**
      * @param {string}
      * @return {!chrome.bluetooth.Device}
@@ -63,20 +114,6 @@
       });
     },
 
-    /** @param {!chrome.bluetooth.Device} device */
-    updateDeviceForTest: function(device, opt_callback) {
-      const index = this.devices.findIndex(function(d) {
-        return d.address == device.address;
-      });
-      if (index == -1) {
-        this.devices.push(device);
-        this.onDeviceAdded.callListeners(device);
-        return;
-      }
-      this.devices[index] = device;
-      this.onDeviceChanged.callListeners(device);
-    },
-
     // Bluetooth overrides.
     /** @override */
     getAdapterState: function(callback) {
diff --git a/chrome/test/data/webui/settings/fake_bluetooth_private.js b/chrome/test/data/webui/settings/fake_bluetooth_private.js
index e72b439..6c79605 100644
--- a/chrome/test/data/webui/settings/fake_bluetooth_private.js
+++ b/chrome/test/data/webui/settings/fake_bluetooth_private.js
@@ -54,7 +54,7 @@
       device = Object.assign({}, device);
       device.paired = true;
       device.connecting = true;
-      this.bluetoothApi_.updateDeviceForTest(device);
+      this.bluetoothApi_.simulateDeviceUpdatedForTest(device);
       if (opt_callback) {
         opt_callback(chrome.bluetoothPrivate.ConnectResultType.IN_PROGRESS);
       }
diff --git a/chrome/test/nacl/nacl_browsertest.cc b/chrome/test/nacl/nacl_browsertest.cc
index aca82d4d..ce153b5 100644
--- a/chrome/test/nacl/nacl_browsertest.cc
+++ b/chrome/test/nacl/nacl_browsertest.cc
@@ -303,7 +303,7 @@
     base::PathService::Get(chrome::DIR_TEST_DATA, &script);
     script = script.AppendASCII("nacl/debug_stub_browser_tests.py");
     cmd.AppendArgPath(script);
-    cmd.AppendArg(base::IntToString(debug_stub_port));
+    cmd.AppendArg(base::NumberToString(debug_stub_port));
     cmd.AppendArg("continue");
     LOG(INFO) << cmd.GetCommandLineString();
     *test_process = base::LaunchProcess(cmd, base::LaunchOptions());
diff --git a/chrome/tools/build/win/create_installer_archive.py b/chrome/tools/build/win/create_installer_archive.py
index 5162a926..be9c38d 100755
--- a/chrome/tools/build/win/create_installer_archive.py
+++ b/chrome/tools/build/win/create_installer_archive.py
@@ -122,9 +122,10 @@
     if option.endswith('dir'):
       continue
 
+    src_subdir = option.replace('\\', os.sep)
     dst_dir = os.path.join(staging_dir, config.get(section, option))
     dst_dir = dst_dir.replace('\\', os.sep)
-    src_paths = glob.glob(os.path.join(src_dir, option))
+    src_paths = glob.glob(os.path.join(src_dir, src_subdir))
     if src_paths and not os.path.exists(dst_dir):
       os.makedirs(dst_dir)
     for src_path in src_paths:
diff --git a/chrome/updater/BUILD.gn b/chrome/updater/BUILD.gn
index 1bcfb84..1223fddf 100644
--- a/chrome/updater/BUILD.gn
+++ b/chrome/updater/BUILD.gn
@@ -12,6 +12,11 @@
       "//chrome/updater/win",
     ]
   }
+  if (is_mac) {
+    deps = [
+      "//chrome/updater/mac",
+    ]
+  }
 }
 
 source_set("common") {
@@ -27,16 +32,23 @@
 
 source_set("updater_tests") {
   testonly = true
-  if (is_win) {
-    sources = [
-      "win/updater_unittest.cc",
-    ]
 
+  sources = [
+    "updater_unittest.cc",
+  ]
+
+  if (is_win) {
     data_deps = [
       "//chrome/updater/win:updater",
     ]
   }
 
+  if (is_mac) {
+    data_deps = [
+      "//chrome/updater/mac:updater",
+    ]
+  }
+
   deps = [
     ":updater",
     "//base/test:test_support",
diff --git a/chrome/updater/mac/BUILD.gn b/chrome/updater/mac/BUILD.gn
new file mode 100644
index 0000000..cb2dcb59
--- /dev/null
+++ b/chrome/updater/mac/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.
+
+group("mac") {
+  deps = [
+    ":updater",
+  ]
+}
+
+executable("updater") {
+  sources = [
+    "main.cc",
+  ]
+
+  deps = [
+    "//chrome/updater:common",
+  ]
+}
diff --git a/chrome/updater/mac/main.cc b/chrome/updater/mac/main.cc
new file mode 100644
index 0000000..0974901c
--- /dev/null
+++ b/chrome/updater/mac/main.cc
@@ -0,0 +1,9 @@
+// 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/updater/updater.h"
+
+int main(int argc, const char* argv[]) {
+  return updater::UpdaterMain();
+}
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index 3b4b973..cfc6c16 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -33,14 +33,8 @@
               base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.1, 0),
               base::TimeDelta::FromSeconds(30)),
           base::SchedulerWorkerPoolParams(
-              base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.1, 0),
-              base::TimeDelta::FromSeconds(40)),
-          base::SchedulerWorkerPoolParams(
               base::RecommendedMaxNumberOfThreadsInPool(8, 32, 0.3, 0),
-              base::TimeDelta::FromSeconds(30)),
-          base::SchedulerWorkerPoolParams(
-              base::RecommendedMaxNumberOfThreadsInPool(8, 32, 0.3, 0),
-              base::TimeDelta::FromSeconds(60)));
+              base::TimeDelta::FromSeconds(30)));
   base::TaskScheduler::GetInstance()->Start(*task_scheduler_init_params);
 }
 
diff --git a/chrome/updater/updater_unittest.cc b/chrome/updater/updater_unittest.cc
new file mode 100644
index 0000000..e086140
--- /dev/null
+++ b/chrome/updater/updater_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/process/launch.h"
+#include "base/process/process.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#if defined(OS_WIN)
+#define EXECUTABLE_EXTENSION ".exe"
+#else
+#define EXECUTABLE_EXTENSION ""
+#endif
+
+// Tests the updater process returns 0 when run with no arguments.
+TEST(UpdaterTest, UpdaterExitCode) {
+  base::FilePath this_executable_path;
+
+  ASSERT_TRUE(base::PathService::Get(base::FILE_EXE, &this_executable_path));
+
+  const base::FilePath updater = this_executable_path.DirName().Append(
+      FILE_PATH_LITERAL("updater" EXECUTABLE_EXTENSION));
+  base::LaunchOptions options;
+
+#if defined(OS_WIN)
+  options.start_hidden = true;
+#endif
+  auto process = base::LaunchProcess(base::CommandLine(updater), options);
+  ASSERT_TRUE(process.IsValid());
+  int exit_code = -1;
+  EXPECT_TRUE(process.WaitForExitWithTimeout(base::TimeDelta::FromSeconds(60),
+                                             &exit_code));
+  EXPECT_EQ(0, exit_code);
+}
diff --git a/chrome/updater/win/updater_unittest.cc b/chrome/updater/win/updater_unittest.cc
deleted file mode 100644
index e5a3093..0000000
--- a/chrome/updater/win/updater_unittest.cc
+++ /dev/null
@@ -1,29 +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 <windows.h>
-
-#include "base/command_line.h"
-#include "base/files/file_path.h"
-#include "base/process/launch.h"
-#include "base/process/process.h"
-#include "base/time/time.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-// Tests the updater process returns 0 when run with no arguments.
-TEST(UpdaterTest, UpdaterExitCode) {
-  base::FilePath::CharType buffer[MAX_PATH] = {0};
-  ASSERT_NE(0u, GetModuleFileName(0, buffer, base::size(buffer)));
-  const base::FilePath updater =
-      base::FilePath(buffer).DirName().Append(FILE_PATH_LITERAL("updater.exe"));
-  base::LaunchOptions options;
-  options.start_hidden = true;
-  auto process = base::LaunchProcess(base::CommandLine(updater), options);
-  ASSERT_TRUE(process.IsValid());
-  int exit_code = -1;
-  EXPECT_TRUE(process.WaitForExitWithTimeout(base::TimeDelta::FromSeconds(60),
-                                             &exit_code));
-  EXPECT_EQ(0, exit_code);
-}
diff --git a/chrome/utility/image_writer/image_writer_mac.cc b/chrome/utility/image_writer/image_writer_mac.cc
index b536062..a516a84 100644
--- a/chrome/utility/image_writer/image_writer_mac.cc
+++ b/chrome/utility/image_writer/image_writer_mac.cc
@@ -108,7 +108,7 @@
   }
 
   // Build the command line.
-  std::string rdwr = base::IntToString(O_RDWR);
+  std::string rdwr = base::NumberToString(O_RDWR);
 
   base::CommandLine cmd_line = base::CommandLine(base::FilePath(kAuthOpenPath));
   cmd_line.AppendSwitch("-stdoutpipe");
diff --git a/chromecast/browser/cast_network_contexts.cc b/chromecast/browser/cast_network_contexts.cc
index 61aaed0..c5faa86 100644
--- a/chromecast/browser/cast_network_contexts.cc
+++ b/chromecast/browser/cast_network_contexts.cc
@@ -16,6 +16,7 @@
 #include "chromecast/browser/cast_http_user_agent_settings.h"
 #include "chromecast/browser/url_request_context_factory.h"
 #include "chromecast/common/cast_content_client.h"
+#include "components/proxy_config/pref_proxy_config_tracker_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
 #include "content/public/browser/network_service_instance.h"
@@ -254,6 +255,8 @@
   network_context_params->disable_idle_sockets_close_on_memory_pressure =
       IsFeatureEnabled(kDisableIdleSocketsCloseOnMemoryPressure);
 
+  AddProxyToNetworkContextParams(network_context_params.get());
+
   return network_context_params;
 }
 
@@ -269,5 +272,60 @@
   return network_context_params;
 }
 
+void CastNetworkContexts::AddProxyToNetworkContextParams(
+    network::mojom::NetworkContextParams* network_context_params) {
+  if (!proxy_config_service_) {
+    pref_proxy_config_tracker_impl_ =
+        std::make_unique<PrefProxyConfigTrackerImpl>(
+            CastBrowserProcess::GetInstance()->pref_service(), nullptr);
+    proxy_config_service_ =
+        pref_proxy_config_tracker_impl_->CreateTrackingProxyConfigService(
+            nullptr);
+    proxy_config_service_->AddObserver(this);
+  }
+
+  network::mojom::ProxyConfigClientPtr proxy_config_client;
+  network_context_params->proxy_config_client_request =
+      mojo::MakeRequest(&proxy_config_client);
+  proxy_config_client_set_.AddPtr(std::move(proxy_config_client));
+
+  poller_binding_set_.AddBinding(
+      this,
+      mojo::MakeRequest(&network_context_params->proxy_config_poller_client));
+
+  net::ProxyConfigWithAnnotation proxy_config;
+  net::ProxyConfigService::ConfigAvailability availability =
+      proxy_config_service_->GetLatestProxyConfig(&proxy_config);
+  if (availability != net::ProxyConfigService::CONFIG_PENDING)
+    network_context_params->initial_proxy_config = proxy_config;
+}
+
+void CastNetworkContexts::OnProxyConfigChanged(
+    const net::ProxyConfigWithAnnotation& config,
+    net::ProxyConfigService::ConfigAvailability availability) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  proxy_config_client_set_.ForAllPtrs(
+      [config,
+       availability](network::mojom::ProxyConfigClient* proxy_config_client) {
+        switch (availability) {
+          case net::ProxyConfigService::CONFIG_VALID:
+            proxy_config_client->OnProxyConfigUpdated(config);
+            break;
+          case net::ProxyConfigService::CONFIG_UNSET:
+            proxy_config_client->OnProxyConfigUpdated(
+                net::ProxyConfigWithAnnotation::CreateDirect());
+            break;
+          case net::ProxyConfigService::CONFIG_PENDING:
+            NOTREACHED();
+            break;
+        }
+      });
+}
+
+void CastNetworkContexts::OnLazyProxyConfigPoll() {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+  proxy_config_service_->OnLazyPoll();
+}
+
 }  // namespace shell
 }  // namespace chromecast
diff --git a/chromecast/browser/cast_network_contexts.h b/chromecast/browser/cast_network_contexts.h
index ed3fcba..6ac8883a 100644
--- a/chromecast/browser/cast_network_contexts.h
+++ b/chromecast/browser/cast_network_contexts.h
@@ -9,7 +9,13 @@
 
 #include "base/memory/scoped_refptr.h"
 #include "content/public/browser/browser_thread.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/bindings/interface_ptr_set.h"
+#include "net/proxy_resolution/proxy_config_service.h"
 #include "services/network/public/mojom/network_service.mojom.h"
+#include "services/network/public/mojom/proxy_config.mojom.h"
+
+class PrefProxyConfigTracker;
 
 namespace base {
 class FilePath;
@@ -38,12 +44,13 @@
 // Otherwise it will create and configure its own NetworkContext for the system,
 // and create the BrowserContext's main StoragePartition's NetworkContext.
 // It lives on the UI thread.
-class CastNetworkContexts {
+class CastNetworkContexts : public net::ProxyConfigService::Observer,
+                            public network::mojom::ProxyConfigPollerClient {
  public:
   // |url_request_context_factory| needs to outlive this object.
   explicit CastNetworkContexts(
       URLRequestContextFactory* url_request_context_factory);
-  ~CastNetworkContexts();
+  ~CastNetworkContexts() override;
 
   // Returns the System NetworkContext. Does any initialization of the
   // NetworkService that may be needed when first called.
@@ -84,6 +91,21 @@
   // since it initializes some class members.
   network::mojom::NetworkContextParamsPtr CreateSystemNetworkContextParams();
 
+  // Populates proxy-related fields of |network_context_params|. Updated
+  // ProxyConfigs will be sent to a NetworkContext created with those params
+  // whenever the configuration changes. Can be called more than once to inform
+  // multiple NetworkContexts of proxy changes.
+  void AddProxyToNetworkContextParams(
+      network::mojom::NetworkContextParams* network_context_params);
+
+  // net::ProxyConfigService::Observer implementation:
+  void OnProxyConfigChanged(
+      const net::ProxyConfigWithAnnotation& config,
+      net::ProxyConfigService::ConfigAvailability availability) override;
+
+  // network::mojom::ProxyConfigPollerClient implementation:
+  void OnLazyProxyConfigPoll() override;
+
   // The following members are used when the network service is disabled.
   URLRequestContextFactory* url_request_context_factory_;
 
@@ -104,6 +126,14 @@
   scoped_refptr<URLLoaderFactoryForSystem> system_shared_url_loader_factory_;
   network::mojom::URLLoaderFactoryPtr system_url_loader_factory_;
 
+  std::unique_ptr<net::ProxyConfigService> proxy_config_service_;
+  // Monitors prefs related to proxy configuration.
+  std::unique_ptr<PrefProxyConfigTracker> pref_proxy_config_tracker_impl_;
+
+  mojo::BindingSet<network::mojom::ProxyConfigPollerClient> poller_binding_set_;
+  mojo::InterfacePtrSet<network::mojom::ProxyConfigClient>
+      proxy_config_client_set_;
+
   DISALLOW_COPY_AND_ASSIGN(CastNetworkContexts);
 };
 
diff --git a/chromecast/browser/metrics/cast_metrics_service_client.cc b/chromecast/browser/metrics/cast_metrics_service_client.cc
index 5422cab..1048402 100644
--- a/chromecast/browser/metrics/cast_metrics_service_client.cc
+++ b/chromecast/browser/metrics/cast_metrics_service_client.cc
@@ -227,21 +227,21 @@
       base::TimeDelta::FromSeconds(kMetricsFetchTimeoutSeconds));
 }
 
-std::string CastMetricsServiceClient::GetMetricsServerUrl() {
+GURL CastMetricsServiceClient::GetMetricsServerUrl() {
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kOverrideMetricsUploadUrl)) {
-    return command_line->GetSwitchValueASCII(
-        switches::kOverrideMetricsUploadUrl);
+    return GURL(
+        command_line->GetSwitchValueASCII(switches::kOverrideMetricsUploadUrl));
   }
   // Note: This uses the old metrics service URL because some server-side
   // provisioning is needed to support the extra Cast traffic on the new URL.
-  return ::metrics::kOldMetricsServerUrl;
+  return GURL(::metrics::kOldMetricsServerUrl);
 }
 
 std::unique_ptr<::metrics::MetricsLogUploader>
 CastMetricsServiceClient::CreateUploader(
-    base::StringPiece server_url,
-    base::StringPiece insecure_server_url,
+    const GURL& server_url,
+    const GURL& insecure_server_url,
     base::StringPiece mime_type,
     ::metrics::MetricsLogUploader::MetricServiceType service_type,
     const ::metrics::MetricsLogUploader::UploadCallback& on_upload_complete) {
diff --git a/chromecast/browser/metrics/cast_metrics_service_client.h b/chromecast/browser/metrics/cast_metrics_service_client.h
index 7b245e3..1fe3c5ba 100644
--- a/chromecast/browser/metrics/cast_metrics_service_client.h
+++ b/chromecast/browser/metrics/cast_metrics_service_client.h
@@ -71,10 +71,10 @@
   ::metrics::SystemProfileProto::Channel GetChannel() override;
   std::string GetVersionString() override;
   void CollectFinalMetricsForLog(const base::Closure& done_callback) override;
-  std::string GetMetricsServerUrl() override;
+  GURL GetMetricsServerUrl() override;
   std::unique_ptr<::metrics::MetricsLogUploader> CreateUploader(
-      base::StringPiece server_url,
-      base::StringPiece insecure_server_url,
+      const GURL& server_url,
+      const GURL& insecure_server_url,
       base::StringPiece mime_type,
       ::metrics::MetricsLogUploader::MetricServiceType service_type,
       const ::metrics::MetricsLogUploader::UploadCallback& on_upload_complete)
diff --git a/chromecast/media/base/slew_volume.cc b/chromecast/media/base/slew_volume.cc
index a5946e6..18b6d75 100644
--- a/chromecast/media/base/slew_volume.cc
+++ b/chromecast/media/base/slew_volume.cc
@@ -5,6 +5,7 @@
 #include "chromecast/media/base/slew_volume.h"
 
 #include <algorithm>
+#include <cmath>
 #include <cstring>
 
 #include "base/logging.h"
@@ -78,7 +79,6 @@
   SetVolume(volume_scale_);
 }
 
-// Slew rate should be volume_to_slew / slew_time / sample_rate
 void SlewVolume::SetVolume(double volume_scale) {
   volume_scale_ = volume_scale;
   if (interrupted_) {
@@ -86,13 +86,12 @@
     last_starting_volume_ = current_volume_;
   }
 
-  if (volume_scale_ > current_volume_) {
-    max_slew_per_sample_ = (volume_scale_ - current_volume_) * 1000.0 /
-                           (max_slew_time_ms_ * sample_rate_);
-  } else {
-    max_slew_per_sample_ = (current_volume_ - volume_scale_) * 1000.0 /
-                           (max_slew_time_ms_ * sample_rate_);
-  }
+  // Slew rate should be volume_to_slew / slew_time / sample_rate, but use a
+  // minimum volume_to_slew of 0.1 to avoid very small slew per sample.
+  double volume_diff =
+      std::max(0.1, std::fabs(volume_scale_ - current_volume_));
+  max_slew_per_sample_ =
+      volume_diff * 1000.0 / (max_slew_time_ms_ * sample_rate_);
 }
 
 float SlewVolume::LastBufferMaxMultiplier() {
diff --git a/chromecast/media/cma/backend/mixer_input.cc b/chromecast/media/cma/backend/mixer_input.cc
index bb59b13..63c1e266 100644
--- a/chromecast/media/cma/backend/mixer_input.cc
+++ b/chromecast/media/cma/backend/mixer_input.cc
@@ -237,7 +237,8 @@
                                        int frames,
                                        float* dest) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  slew_volume_.ProcessFMAC(!volume_applied_, src, frames, 1, dest);
+  slew_volume_.ProcessFMAC(volume_applied_ /* repeat_transition */, src, frames,
+                           1, dest);
   volume_applied_ = true;
 }
 
diff --git a/chromeos/dbus/cicerone_client.cc b/chromeos/dbus/cicerone_client.cc
index 1ee08d8c..b0a3e5b6 100644
--- a/chromeos/dbus/cicerone_client.cc
+++ b/chromeos/dbus/cicerone_client.cc
@@ -145,6 +145,31 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
+  void GetLinuxPackageInfoFromApt(
+      const vm_tools::cicerone::LinuxPackageInfoFromAptRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::LinuxPackageInfoResponse> callback)
+      override {
+    dbus::MethodCall method_call(
+        vm_tools::cicerone::kVmCiceroneInterface,
+        vm_tools::cicerone::kGetLinuxPackageInfoFromAptMethod);
+    dbus::MessageWriter writer(&method_call);
+
+    if (!writer.AppendProtoAsArrayOfBytes(request)) {
+      LOG(ERROR)
+          << "Failed to encode GetLinuxPackageInfoFromAptRequest protobuf";
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+
+      return;
+    }
+
+    cicerone_proxy_->CallMethod(
+        &method_call, kDefaultTimeout.InMilliseconds(),
+        base::BindOnce(&CiceroneClientImpl::OnDBusProtoResponse<
+                           vm_tools::cicerone::LinuxPackageInfoResponse>,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void InstallLinuxPackage(
       const vm_tools::cicerone::InstallLinuxPackageRequest& request,
       DBusMethodCallback<vm_tools::cicerone::InstallLinuxPackageResponse>
@@ -168,6 +193,30 @@
                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
+  void InstallLinuxPackageFromApt(
+      const vm_tools::cicerone::InstallLinuxPackageFromAptRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::InstallLinuxPackageResponse>
+          callback) override {
+    dbus::MethodCall method_call(
+        vm_tools::cicerone::kVmCiceroneInterface,
+        vm_tools::cicerone::kInstallLinuxPackageFromAptMethod);
+    dbus::MessageWriter writer(&method_call);
+
+    if (!writer.AppendProtoAsArrayOfBytes(request)) {
+      LOG(ERROR)
+          << "Failed to encode InstallLinuxPackageFromAptRequest protobuf";
+      base::ThreadTaskRunnerHandle::Get()->PostTask(
+          FROM_HERE, base::BindOnce(std::move(callback), base::nullopt));
+      return;
+    }
+
+    cicerone_proxy_->CallMethod(
+        &method_call, kDefaultTimeout.InMilliseconds(),
+        base::BindOnce(&CiceroneClientImpl::OnDBusProtoResponse<
+                           vm_tools::cicerone::InstallLinuxPackageResponse>,
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+  }
+
   void UninstallPackageOwningFile(
       const vm_tools::cicerone::UninstallPackageOwningFileRequest& request,
       DBusMethodCallback<vm_tools::cicerone::UninstallPackageOwningFileResponse>
diff --git a/chromeos/dbus/cicerone_client.h b/chromeos/dbus/cicerone_client.h
index 0f344cef..7b26bb57 100644
--- a/chromeos/dbus/cicerone_client.h
+++ b/chromeos/dbus/cicerone_client.h
@@ -125,6 +125,14 @@
       DBusMethodCallback<vm_tools::cicerone::LinuxPackageInfoResponse>
           callback) = 0;
 
+  // Gets information about a Linux package via it's name from an APT
+  // repository inside a container. |callback| is called after the method
+  // call finishes.
+  virtual void GetLinuxPackageInfoFromApt(
+      const vm_tools::cicerone::LinuxPackageInfoFromAptRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::LinuxPackageInfoResponse>
+          callback) = 0;
+
   // Installs a package inside the container.
   // |callback| is called after the method call finishes.
   virtual void InstallLinuxPackage(
@@ -132,6 +140,13 @@
       DBusMethodCallback<vm_tools::cicerone::InstallLinuxPackageResponse>
           callback) = 0;
 
+  // Installs a package inside the container from an APT repository using a
+  // package_id. |callback| is called after the method call finishes.
+  virtual void InstallLinuxPackageFromApt(
+      const vm_tools::cicerone::InstallLinuxPackageFromAptRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::InstallLinuxPackageResponse>
+          callback) = 0;
+
   // Uninstalls the package that owns the indicated .desktop file.
   // |callback| is called after the method call finishes.
   virtual void UninstallPackageOwningFile(
diff --git a/chromeos/dbus/fake_cicerone_client.cc b/chromeos/dbus/fake_cicerone_client.cc
index d354799..7ad8e0b 100644
--- a/chromeos/dbus/fake_cicerone_client.cc
+++ b/chromeos/dbus/fake_cicerone_client.cc
@@ -105,6 +105,14 @@
       base::BindOnce(std::move(callback), get_linux_package_info_response_));
 }
 
+void FakeCiceroneClient::GetLinuxPackageInfoFromApt(
+    const vm_tools::cicerone::LinuxPackageInfoFromAptRequest& request,
+    DBusMethodCallback<vm_tools::cicerone::LinuxPackageInfoResponse> callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), get_linux_package_info_response_));
+}
+
 void FakeCiceroneClient::InstallLinuxPackage(
     const vm_tools::cicerone::InstallLinuxPackageRequest& request,
     DBusMethodCallback<vm_tools::cicerone::InstallLinuxPackageResponse>
@@ -114,6 +122,15 @@
       base::BindOnce(std::move(callback), install_linux_package_response_));
 }
 
+void FakeCiceroneClient::InstallLinuxPackageFromApt(
+    const vm_tools::cicerone::InstallLinuxPackageFromAptRequest& request,
+    DBusMethodCallback<vm_tools::cicerone::InstallLinuxPackageResponse>
+        callback) {
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), install_linux_package_response_));
+}
+
 void FakeCiceroneClient::UninstallPackageOwningFile(
     const vm_tools::cicerone::UninstallPackageOwningFileRequest& request,
     DBusMethodCallback<vm_tools::cicerone::UninstallPackageOwningFileResponse>
diff --git a/chromeos/dbus/fake_cicerone_client.h b/chromeos/dbus/fake_cicerone_client.h
index dc7de67..a5a0c26 100644
--- a/chromeos/dbus/fake_cicerone_client.h
+++ b/chromeos/dbus/fake_cicerone_client.h
@@ -72,6 +72,14 @@
       DBusMethodCallback<vm_tools::cicerone::LinuxPackageInfoResponse> callback)
       override;
 
+  // Fake version of the method that gets information about a Linux package via
+  // its name, inside a Container. |callback| is called after the method call
+  // finishes.
+  void GetLinuxPackageInfoFromApt(
+      const vm_tools::cicerone::LinuxPackageInfoFromAptRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::LinuxPackageInfoResponse> callback)
+      override;
+
   // Fake version of the method that installs an application inside a running
   // Container. |callback| is called after the method call finishes. This does
   // not cause progress events to be fired.
@@ -80,6 +88,14 @@
       DBusMethodCallback<vm_tools::cicerone::InstallLinuxPackageResponse>
           callback) override;
 
+  // Fake version of the method that installs an application inside a running
+  // Container. |callback| is called after the method call finishes. This does
+  // not cause progress events to be fired.
+  void InstallLinuxPackageFromApt(
+      const vm_tools::cicerone::InstallLinuxPackageFromAptRequest& request,
+      DBusMethodCallback<vm_tools::cicerone::InstallLinuxPackageResponse>
+          callback) override;
+
   // Fake version of the method that uninstalls an application inside a running
   // Container. |callback| is called after the method call finishes. This does
   // not cause progress events to be fired.
@@ -229,6 +245,11 @@
     setup_lxd_container_user_response_ = setup_lxd_container_user_response;
   }
 
+  void set_search_app_response(
+      const vm_tools::cicerone::AppSearchResponse& search_app_response) {
+    search_app_response_ = search_app_response;
+  }
+
   // Additional functions to allow tests to trigger Signals.
   void NotifyLxdContainerCreated(
       const vm_tools::cicerone::LxdContainerCreatedSignal& signal);
diff --git a/chromeos/network/network_cert_loader.cc b/chromeos/network/network_cert_loader.cc
index 3e03f0e1..53088d1 100644
--- a/chromeos/network/network_cert_loader.cc
+++ b/chromeos/network/network_cert_loader.cc
@@ -142,44 +142,18 @@
 
 namespace {
 
-// Checks if |certificate| is on the given |slot|.
-bool IsCertificateOnSlot(CERTCertificate* certificate, PK11SlotInfo* slot) {
-  crypto::ScopedPK11SlotList slots_for_cert(
-      PK11_GetAllSlotsForCert(certificate, nullptr));
-  if (!slots_for_cert)
-    return false;
-
-  for (PK11SlotListElement* slot_element =
-           PK11_GetFirstSafe(slots_for_cert.get());
-       slot_element; slot_element = PK11_GetNextSafe(slots_for_cert.get(),
-                                                     slot_element, PR_FALSE)) {
-    if (slot_element->slot == slot) {
-      // All previously visited elements have been freed by PK11_GetNextSafe,
-      // but we're not calling that for the last one, so free it explicitly.
-      // The slots_for_cert list itself will be freed because ScopedPK11SlotList
-      // is a unique_ptr.
-      PK11_FreeSlotListElement(slots_for_cert.get(), slot_element);
-      return true;
-    }
-  }
-  return false;
-}
-
 // Goes through all certificates in |certs| and copies those certificates
-// which are on |system_slot| to a new list.
+// which are on the system slot to a new list.
 net::ScopedCERTCertificateList FilterSystemTokenCertificates(
     net::ScopedCERTCertificateList certs,
-    crypto::ScopedPK11Slot system_slot) {
+    const net::NSSCertDatabase* cert_db) {
   VLOG(1) << "FilterSystemTokenCertificates";
-  if (!system_slot)
-    return net::ScopedCERTCertificateList();
 
-  PK11SlotInfo* system_slot_ptr = system_slot.get();
-  // Only keep certificates which are on the |system_slot|.
+  // Only keep certificates which are on the system slot.
   certs.erase(
       std::remove_if(certs.begin(), certs.end(),
-                     [system_slot_ptr](const net::ScopedCERTCertificate& cert) {
-                       return !IsCertificateOnSlot(cert.get(), system_slot_ptr);
+                     [cert_db](const net::ScopedCERTCertificate& cert) {
+                       return !cert_db->IsCertificateOnSystemSlot(cert.get());
                      }),
       certs.end());
   return certs;
@@ -350,16 +324,13 @@
   // filter.
   if (user_cert_cache_->initial_load_finished() &&
       user_cert_cache_->has_system_certificates()) {
-    crypto::ScopedPK11Slot system_slot =
-        user_cert_cache_->nss_database()->GetSystemSlot();
-    DCHECK(system_slot);
     base::PostTaskWithTraitsAndReplyWithResult(
         FROM_HERE,
         {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
         base::BindOnce(&FilterSystemTokenCertificates,
                        net::x509_util::DupCERTCertificateList(
                            user_cert_cache_->cert_list()),
-                       std::move(system_slot)),
+                       user_cert_cache_->nss_database()),
         base::BindOnce(&NetworkCertLoader::StoreCertsFromCache,
                        weak_factory_.GetWeakPtr(),
                        net::x509_util::DupCERTCertificateList(
diff --git a/chromeos/services/assistant/assistant_manager_service_impl.cc b/chromeos/services/assistant/assistant_manager_service_impl.cc
index 6966933..0627762 100644
--- a/chromeos/services/assistant/assistant_manager_service_impl.cc
+++ b/chromeos/services/assistant/assistant_manager_service_impl.cc
@@ -1188,7 +1188,7 @@
     mojom::AssistantFeedbackPtr assistant_feedback) {
   const std::string interaction = CreateSendFeedbackInteraction(
       assistant_feedback->assistant_debug_info_allowed,
-      assistant_feedback->description);
+      assistant_feedback->description, assistant_feedback->screenshot_png);
   assistant_client::VoicelessOptions voiceless_options;
 
   voiceless_options.is_user_initiated = false;
diff --git a/chromeos/services/assistant/platform/audio_input_impl.cc b/chromeos/services/assistant/platform/audio_input_impl.cc
index 4f79c164..86fec80 100644
--- a/chromeos/services/assistant/platform/audio_input_impl.cc
+++ b/chromeos/services/assistant/platform/audio_input_impl.cc
@@ -182,7 +182,7 @@
   {
     base::AutoLock lock(lock_);
     for (auto* observer : observers_)
-      observer->OnBufferAvailable(input_buffer, time);
+      observer->OnAudioBufferAvailable(input_buffer, time);
   }
 
   captured_frames_count_ += audio_source->frames();
@@ -201,7 +201,7 @@
   LOG(ERROR) << "Capture error " << message;
   base::AutoLock lock(lock_);
   for (auto* observer : observers_)
-    observer->OnError(AudioInput::Error::FATAL_ERROR);
+    observer->OnAudioError(AudioInput::Error::FATAL_ERROR);
 }
 
 // Runs on audio service thread.
diff --git a/chromeos/services/assistant/public/mojom/assistant.mojom b/chromeos/services/assistant/public/mojom/assistant.mojom
index 6f0afa4c..19c5970 100644
--- a/chromeos/services/assistant/public/mojom/assistant.mojom
+++ b/chromeos/services/assistant/public/mojom/assistant.mojom
@@ -253,4 +253,8 @@
 
   // Whether user consent to send debug info.
   bool assistant_debug_info_allowed;
+
+  // Screenshot if allowed by user.
+  // Raw data (non-encoded binary octets)
+  string screenshot_png;
 };
diff --git a/chromeos/services/assistant/utils.cc b/chromeos/services/assistant/utils.cc
index 8422c02..b7d212c4 100644
--- a/chromeos/services/assistant/utils.cc
+++ b/chromeos/services/assistant/utils.cc
@@ -47,6 +47,9 @@
   discovery.SetKey("enable_mdns", Value(false));
   config.SetKey("discovery", std::move(discovery));
 
+  Value internal(Type::DICTIONARY);
+  internal.SetKey("surface_type", Value("OPA_CROS"));
+
   if (base::SysInfo::IsRunningOnChromeOS()) {
     // Log to 'log' sub dir in user's home dir.
     Value logging(Type::DICTIONARY);
@@ -61,10 +64,9 @@
     config.SetKey("logging", std::move(logging));
   } else {
     // Print logs to console if running in desktop mode.
-    Value internal(Type::DICTIONARY);
     internal.SetKey("disable_log_files", Value(true));
-    config.SetKey("internal", std::move(internal));
   }
+  config.SetKey("internal", std::move(internal));
 
   Value audio_input(Type::DICTIONARY);
   Value sources(Type::LIST);
diff --git a/chromeos/services/device_sync/BUILD.gn b/chromeos/services/device_sync/BUILD.gn
index 69c9ea5..98b37b5 100644
--- a/chromeos/services/device_sync/BUILD.gn
+++ b/chromeos/services/device_sync/BUILD.gn
@@ -50,6 +50,8 @@
     "device_sync_service.h",
     "device_sync_type_converters.cc",
     "device_sync_type_converters.h",
+    "network_aware_enrollment_scheduler.cc",
+    "network_aware_enrollment_scheduler.h",
     "network_request_error.cc",
     "network_request_error.h",
     "persistent_enrollment_scheduler.cc",
@@ -85,6 +87,7 @@
     "//base",
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice/logging",
+    "//chromeos/network",
     "//chromeos/services/device_sync/proto:util",
     "//chromeos/services/device_sync/public/cpp",
     "//chromeos/services/device_sync/public/mojom",
@@ -162,6 +165,7 @@
     "cryptauth_key_registry_impl_unittest.cc",
     "cryptauth_key_unittest.cc",
     "device_sync_service_unittest.cc",
+    "network_aware_enrollment_scheduler_unittest.cc",
     "persistent_enrollment_scheduler_unittest.cc",
     "remote_device_loader_unittest.cc",
     "remote_device_provider_impl_unittest.cc",
@@ -177,6 +181,8 @@
     "//chromeos/components/multidevice",
     "//chromeos/components/multidevice:test_support",
     "//chromeos/dbus:test_support",
+    "//chromeos/network",
+    "//chromeos/network:test_support",
     "//chromeos/services/device_sync/proto:util",
     "//chromeos/services/device_sync/public/cpp:test_support",
     "//chromeos/services/device_sync/public/cpp:unit_tests",
diff --git a/chromeos/services/device_sync/network_aware_enrollment_scheduler.cc b/chromeos/services/device_sync/network_aware_enrollment_scheduler.cc
new file mode 100644
index 0000000..9b6d2bc
--- /dev/null
+++ b/chromeos/services/device_sync/network_aware_enrollment_scheduler.cc
@@ -0,0 +1,161 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/network_aware_enrollment_scheduler.h"
+
+#include <utility>
+
+#include "base/memory/ptr_util.h"
+#include "base/no_destructor.h"
+#include "chromeos/components/multidevice/logging/logging.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/services/device_sync/persistent_enrollment_scheduler.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+// static
+NetworkAwareEnrollmentScheduler::Factory*
+    NetworkAwareEnrollmentScheduler::Factory::test_factory_ = nullptr;
+
+// static
+NetworkAwareEnrollmentScheduler::Factory*
+NetworkAwareEnrollmentScheduler::Factory::Get() {
+  if (test_factory_)
+    return test_factory_;
+
+  static base::NoDestructor<Factory> factory;
+  return factory.get();
+}
+
+// static
+void NetworkAwareEnrollmentScheduler::Factory::SetFactoryForTesting(
+    Factory* test_factory) {
+  test_factory_ = test_factory;
+}
+
+NetworkAwareEnrollmentScheduler::Factory::~Factory() = default;
+
+std::unique_ptr<CryptAuthEnrollmentScheduler>
+NetworkAwareEnrollmentScheduler::Factory::BuildInstance(
+    CryptAuthEnrollmentScheduler::Delegate* delegate,
+    PrefService* pref_service,
+    NetworkStateHandler* network_state_handler) {
+  std::unique_ptr<NetworkAwareEnrollmentScheduler> network_aware_scheduler =
+      base::WrapUnique(
+          new NetworkAwareEnrollmentScheduler(delegate, network_state_handler));
+
+  std::unique_ptr<CryptAuthEnrollmentScheduler> network_unaware_scheduler =
+      PersistentEnrollmentScheduler::Factory::Get()->BuildInstance(
+          network_aware_scheduler.get(), pref_service);
+
+  network_aware_scheduler->network_unaware_scheduler_ =
+      std::move(network_unaware_scheduler);
+
+  return network_aware_scheduler;
+}
+
+NetworkAwareEnrollmentScheduler::NetworkAwareEnrollmentScheduler(
+    CryptAuthEnrollmentScheduler::Delegate* delegate,
+    NetworkStateHandler* network_state_handler)
+    : CryptAuthEnrollmentScheduler(delegate),
+      network_state_handler_(network_state_handler) {
+  DCHECK(network_state_handler_);
+  network_state_handler_->AddObserver(this, FROM_HERE);
+}
+
+NetworkAwareEnrollmentScheduler::~NetworkAwareEnrollmentScheduler() {
+  if (network_state_handler_)
+    network_state_handler_->RemoveObserver(this, FROM_HERE);
+}
+
+void NetworkAwareEnrollmentScheduler::RequestEnrollmentNow() {
+  network_unaware_scheduler_->RequestEnrollmentNow();
+}
+
+void NetworkAwareEnrollmentScheduler::HandleEnrollmentResult(
+    const CryptAuthEnrollmentResult& enrollment_result) {
+  network_unaware_scheduler_->HandleEnrollmentResult(enrollment_result);
+}
+
+base::Optional<base::Time>
+NetworkAwareEnrollmentScheduler::GetLastSuccessfulEnrollmentTime() const {
+  return network_unaware_scheduler_->GetLastSuccessfulEnrollmentTime();
+}
+
+base::TimeDelta NetworkAwareEnrollmentScheduler::GetRefreshPeriod() const {
+  return network_unaware_scheduler_->GetRefreshPeriod();
+}
+
+base::TimeDelta
+NetworkAwareEnrollmentScheduler::GetTimeToNextEnrollmentRequest() const {
+  return network_unaware_scheduler_->GetTimeToNextEnrollmentRequest();
+}
+
+bool NetworkAwareEnrollmentScheduler::IsWaitingForEnrollmentResult() const {
+  return network_unaware_scheduler_->IsWaitingForEnrollmentResult() &&
+         !pending_client_directive_policy_reference_.has_value();
+}
+
+size_t NetworkAwareEnrollmentScheduler::GetNumConsecutiveFailures() const {
+  return network_unaware_scheduler_->GetNumConsecutiveFailures();
+}
+
+void NetworkAwareEnrollmentScheduler::OnEnrollmentRequested(
+    const base::Optional<cryptauthv2::PolicyReference>&
+        client_directive_policy_reference) {
+  if (DoesMachineHaveNetworkConnectivity()) {
+    NotifyEnrollmentRequested(client_directive_policy_reference);
+    return;
+  }
+
+  PA_LOG(INFO) << "NetworkAwareEnrollmentScheduler::OnEnrollmentRequested(): "
+               << "Enrollment scheduled while the device is offline. Waiting "
+               << "for online connectivity before making request.";
+  pending_client_directive_policy_reference_ =
+      client_directive_policy_reference;
+}
+
+void NetworkAwareEnrollmentScheduler::DefaultNetworkChanged(
+    const NetworkState* network) {
+  // If enrollment has not been requested, there is nothing to do.
+  if (!pending_client_directive_policy_reference_)
+    return;
+
+  // The updated default network may not be online.
+  if (!DoesMachineHaveNetworkConnectivity())
+    return;
+
+  // Now that the device has connectivity, request enrollment. Note that the
+  // |pending_client_directive_policy_reference_| field is cleared before the
+  // delegate is notified to ensure internal consistency.
+  base::Optional<cryptauthv2::PolicyReference> policy_reference_for_enrollment =
+      *pending_client_directive_policy_reference_;
+  pending_client_directive_policy_reference_.reset();
+  NotifyEnrollmentRequested(policy_reference_for_enrollment);
+}
+
+void NetworkAwareEnrollmentScheduler::OnShuttingDown() {
+  DCHECK(network_state_handler_);
+  network_state_handler_->RemoveObserver(this, FROM_HERE);
+  network_state_handler_ = nullptr;
+}
+
+bool NetworkAwareEnrollmentScheduler::DoesMachineHaveNetworkConnectivity() {
+  if (!network_state_handler_)
+    return false;
+
+  // TODO(khorimoto): IsConnectedState() can still return true if connected to a
+  // captive portal; use the "online" boolean once we fetch data via the
+  // networking Mojo API. See https://crbug.com/862420.
+  const NetworkState* default_network =
+      network_state_handler_->DefaultNetwork();
+  return default_network && default_network->IsConnectedState();
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/chromeos/services/device_sync/network_aware_enrollment_scheduler.h b/chromeos/services/device_sync/network_aware_enrollment_scheduler.h
new file mode 100644
index 0000000..e6f4e5b
--- /dev/null
+++ b/chromeos/services/device_sync/network_aware_enrollment_scheduler.h
@@ -0,0 +1,97 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_DEVICE_SYNC_NETWORK_AWARE_ENROLLMENT_SCHEDULER_H_
+#define CHROMEOS_SERVICES_DEVICE_SYNC_NETWORK_AWARE_ENROLLMENT_SCHEDULER_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state_handler_observer.h"
+#include "chromeos/services/device_sync/cryptauth_enrollment_scheduler.h"
+
+class PrefService;
+
+namespace chromeos {
+
+class NetworkStateHandler;
+
+namespace device_sync {
+
+// CryptAuthEnrollmentScheduler implementation which ensures that enrollment is
+// only requested while online. This class owns and serves as a delegate to a
+// network-unaware scheduler which requests enrollment without checking for
+// network connectivity. When this class receives a request from the network-
+// unaware scheduler, it:
+//   *Requests enrollment immediately if there is network connectivity, or
+//   *Caches the request until network connectivity has been attained, then
+//    requests enrollment.
+class NetworkAwareEnrollmentScheduler
+    : public CryptAuthEnrollmentScheduler,
+      public CryptAuthEnrollmentScheduler::Delegate,
+      public NetworkStateHandlerObserver {
+ public:
+  class Factory {
+   public:
+    static Factory* Get();
+    static void SetFactoryForTesting(Factory* test_factory);
+    virtual ~Factory();
+    virtual std::unique_ptr<CryptAuthEnrollmentScheduler> BuildInstance(
+        CryptAuthEnrollmentScheduler::Delegate* delegate,
+        PrefService* pref_service,
+        NetworkStateHandler* network_state_handler =
+            NetworkHandler::Get()->network_state_handler());
+
+   private:
+    static Factory* test_factory_;
+  };
+
+  ~NetworkAwareEnrollmentScheduler() override;
+
+ private:
+  NetworkAwareEnrollmentScheduler(
+      CryptAuthEnrollmentScheduler::Delegate* delegate,
+      NetworkStateHandler* network_state_handler);
+
+  // CryptAuthEnrollmentScheduler:
+  void RequestEnrollmentNow() override;
+  void HandleEnrollmentResult(
+      const CryptAuthEnrollmentResult& enrollment_result) override;
+  base::Optional<base::Time> GetLastSuccessfulEnrollmentTime() const override;
+  base::TimeDelta GetRefreshPeriod() const override;
+  base::TimeDelta GetTimeToNextEnrollmentRequest() const override;
+  bool IsWaitingForEnrollmentResult() const override;
+  size_t GetNumConsecutiveFailures() const override;
+
+  // CryptAuthEnrollmentScheduler::Delegate:
+  void OnEnrollmentRequested(const base::Optional<cryptauthv2::PolicyReference>&
+                                 client_directive_policy_reference) override;
+
+  // NetworkStateHandlerObserver:
+  void DefaultNetworkChanged(const NetworkState* network) override;
+  void OnShuttingDown() override;
+
+  bool DoesMachineHaveNetworkConnectivity();
+
+  NetworkStateHandler* network_state_handler_;
+  std::unique_ptr<CryptAuthEnrollmentScheduler> network_unaware_scheduler_;
+
+  // The pending enrollment request, if it exists. If OnEnrollmentRequested() is
+  // invoked while offline, this class stores the optional PolicyReference
+  // parameter for that function to this instance field; then, when online
+  // connectivity has been restored, this class notifies *its* observer,
+  // forwarding this instance field. If this class does not have a pending
+  // enrollment, this field is null.
+  base::Optional<base::Optional<cryptauthv2::PolicyReference>>
+      pending_client_directive_policy_reference_;
+
+  DISALLOW_COPY_AND_ASSIGN(NetworkAwareEnrollmentScheduler);
+};
+
+}  // namespace device_sync
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_DEVICE_SYNC_NETWORK_AWARE_ENROLLMENT_SCHEDULER_H_
diff --git a/chromeos/services/device_sync/network_aware_enrollment_scheduler_unittest.cc b/chromeos/services/device_sync/network_aware_enrollment_scheduler_unittest.cc
new file mode 100644
index 0000000..620ec42
--- /dev/null
+++ b/chromeos/services/device_sync/network_aware_enrollment_scheduler_unittest.cc
@@ -0,0 +1,245 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/device_sync/network_aware_enrollment_scheduler.h"
+
+#include <memory>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/dbus/shill_service_client.h"
+#include "chromeos/network/network_state_handler.h"
+#include "chromeos/network/network_state_test.h"
+#include "chromeos/services/device_sync/fake_cryptauth_enrollment_scheduler.h"
+#include "chromeos/services/device_sync/persistent_enrollment_scheduler.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
+
+namespace chromeos {
+
+namespace device_sync {
+
+namespace {
+
+const char kWifiServiceGuid[] = "wifiGuid";
+
+enum class NetworkConnectionStatus { kDisconnected, kConnecting, kConnected };
+
+class FakePersistentEnrollmentSchedulerFactory
+    : public PersistentEnrollmentScheduler::Factory {
+ public:
+  FakePersistentEnrollmentSchedulerFactory() = default;
+  ~FakePersistentEnrollmentSchedulerFactory() override = default;
+
+  FakeCryptAuthEnrollmentScheduler* instance() { return instance_; }
+
+ private:
+  // PersistentEnrollmentScheduler::Factory:
+  std::unique_ptr<CryptAuthEnrollmentScheduler> BuildInstance(
+      CryptAuthEnrollmentScheduler::Delegate* delegate,
+      PrefService* pref_service,
+      base::Clock* clock,
+      std::unique_ptr<base::OneShotTimer> timer,
+      scoped_refptr<base::TaskRunner> task_runner) override {
+    EXPECT_FALSE(instance_);
+    auto fake_network_unaware_scheduler =
+        std::make_unique<FakeCryptAuthEnrollmentScheduler>(delegate);
+    instance_ = fake_network_unaware_scheduler.get();
+    return std::move(fake_network_unaware_scheduler);
+  }
+
+  FakeCryptAuthEnrollmentScheduler* instance_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(FakePersistentEnrollmentSchedulerFactory);
+};
+
+}  // namespace
+
+class DeviceSyncNetworkAwareEnrollmentSchedulerTest : public NetworkStateTest {
+ protected:
+  DeviceSyncNetworkAwareEnrollmentSchedulerTest() = default;
+  ~DeviceSyncNetworkAwareEnrollmentSchedulerTest() override = default;
+
+  void SetUp() override {
+    fake_persistent_enrollment_scheduler_factory_ =
+        std::make_unique<FakePersistentEnrollmentSchedulerFactory>();
+    PersistentEnrollmentScheduler::Factory::SetFactoryForTesting(
+        fake_persistent_enrollment_scheduler_factory_.get());
+
+    DBusThreadManager::Initialize();
+    NetworkStateTest::SetUp();
+    ClearDefaultServices();
+
+    fake_delegate_ =
+        std::make_unique<FakeCryptAuthEnrollmentSchedulerDelegate>();
+    scheduler_ = NetworkAwareEnrollmentScheduler::Factory::Get()->BuildInstance(
+        fake_delegate_.get(), nullptr /* pref_service */,
+        network_state_handler());
+  }
+
+  void TearDown() override {
+    ShutdownNetworkState();
+    NetworkStateTest::TearDown();
+    DBusThreadManager::Shutdown();
+
+    PersistentEnrollmentScheduler::Factory::SetFactoryForTesting(nullptr);
+  }
+
+  void AddDisconnectedWifiNetwork() {
+    EXPECT_TRUE(wifi_network_service_path_.empty());
+
+    std::stringstream ss;
+    ss << "{"
+       << "  \"GUID\": \"" << kWifiServiceGuid << "\","
+       << "  \"Type\": \"" << shill::kTypeWifi << "\","
+       << "  \"State\": \"" << shill::kStateIdle << "\""
+       << "}";
+
+    wifi_network_service_path_ = ConfigureService(ss.str());
+  }
+
+  void SetWifiNetworkStatus(NetworkConnectionStatus connection_status) {
+    std::string shill_connection_status;
+    switch (connection_status) {
+      case NetworkConnectionStatus::kDisconnected:
+        shill_connection_status = shill::kStateIdle;
+        break;
+      case NetworkConnectionStatus::kConnecting:
+        shill_connection_status = shill::kStateAssociation;
+        break;
+      case NetworkConnectionStatus::kConnected:
+        shill_connection_status = shill::kStateReady;
+        break;
+    }
+
+    SetServiceProperty(wifi_network_service_path_, shill::kStateProperty,
+                       base::Value(shill_connection_status));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void RemoveWifiNetwork() {
+    EXPECT_TRUE(!wifi_network_service_path_.empty());
+
+    DBusThreadManager::Get()
+        ->GetShillServiceClient()
+        ->GetTestInterface()
+        ->RemoveService(wifi_network_service_path_);
+    base::RunLoop().RunUntilIdle();
+
+    wifi_network_service_path_.clear();
+  }
+
+  const std::vector<base::Optional<cryptauthv2::PolicyReference>>&
+  delegate_policy_references() const {
+    return fake_delegate_->policy_references_from_enrollment_requests();
+  }
+
+  CryptAuthEnrollmentScheduler* scheduler() { return scheduler_.get(); }
+
+  FakeCryptAuthEnrollmentScheduler* fake_network_unaware_scheduler() {
+    return fake_persistent_enrollment_scheduler_factory_->instance();
+  }
+
+ private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+
+  std::unique_ptr<FakePersistentEnrollmentSchedulerFactory>
+      fake_persistent_enrollment_scheduler_factory_;
+
+  std::unique_ptr<FakeCryptAuthEnrollmentSchedulerDelegate> fake_delegate_;
+  std::unique_ptr<CryptAuthEnrollmentScheduler> scheduler_;
+
+  std::string wifi_network_service_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(DeviceSyncNetworkAwareEnrollmentSchedulerTest);
+};
+
+TEST_F(DeviceSyncNetworkAwareEnrollmentSchedulerTest,
+       RequestReceivedWhileOnline) {
+  AddDisconnectedWifiNetwork();
+  SetWifiNetworkStatus(NetworkConnectionStatus::kConnected);
+
+  fake_network_unaware_scheduler()->RequestEnrollmentNow();
+  EXPECT_EQ(1u, delegate_policy_references().size());
+
+  fake_network_unaware_scheduler()->RequestEnrollmentNow();
+  EXPECT_EQ(2u, delegate_policy_references().size());
+}
+
+TEST_F(DeviceSyncNetworkAwareEnrollmentSchedulerTest,
+       RequestReceivedWhileOffline) {
+  AddDisconnectedWifiNetwork();
+  SetWifiNetworkStatus(NetworkConnectionStatus::kDisconnected);
+
+  // Request enrollment while disconnected using a null PolicyReference; since
+  // the network is disconnected, nothing should occur.
+  fake_network_unaware_scheduler()->set_client_directive_policy_reference(
+      base::nullopt);
+  fake_network_unaware_scheduler()->RequestEnrollmentNow();
+  EXPECT_TRUE(delegate_policy_references().empty());
+  EXPECT_TRUE(fake_network_unaware_scheduler()->IsWaitingForEnrollmentResult());
+  EXPECT_FALSE(scheduler()->IsWaitingForEnrollmentResult());
+
+  // Start connecting; no enrollment should have been requested yet.
+  SetWifiNetworkStatus(NetworkConnectionStatus::kConnecting);
+  EXPECT_TRUE(delegate_policy_references().empty());
+
+  // Try requesting while connecting, this time passing a PolicyReference; since
+  // the network is only connecting, nothing should occur.
+  static const std::string kPolicyReferenceName = "policyReferenceName";
+  cryptauthv2::PolicyReference policy_reference;
+  policy_reference.set_name(kPolicyReferenceName);
+  fake_network_unaware_scheduler()->set_client_directive_policy_reference(
+      policy_reference);
+  fake_network_unaware_scheduler()->RequestEnrollmentNow();
+  EXPECT_TRUE(delegate_policy_references().empty());
+  EXPECT_TRUE(fake_network_unaware_scheduler()->IsWaitingForEnrollmentResult());
+  EXPECT_FALSE(scheduler()->IsWaitingForEnrollmentResult());
+
+  // Connect; this should cause the enrollment request to be sent. Because the
+  // enrollment request with |policy_reference| was sent after the one with a
+  // null PolicyReference, the received request should have a PolicyReference.
+  SetWifiNetworkStatus(NetworkConnectionStatus::kConnected);
+  EXPECT_EQ(1u, delegate_policy_references().size());
+  EXPECT_EQ(kPolicyReferenceName, delegate_policy_references()[0]->name());
+  EXPECT_TRUE(fake_network_unaware_scheduler()->IsWaitingForEnrollmentResult());
+  EXPECT_TRUE(scheduler()->IsWaitingForEnrollmentResult());
+
+  scheduler()->HandleEnrollmentResult(CryptAuthEnrollmentResult(
+      CryptAuthEnrollmentResult::ResultCode::kSuccessNoSyncRequired,
+      base::nullopt /* client_directive */));
+  EXPECT_FALSE(
+      fake_network_unaware_scheduler()->IsWaitingForEnrollmentResult());
+  EXPECT_FALSE(scheduler()->IsWaitingForEnrollmentResult());
+
+  // Now, remove the network entirely.
+  RemoveWifiNetwork();
+
+  // Requesting enrollment when there is no default network at all should not
+  // trigger an enrollment request to be sent.
+  fake_network_unaware_scheduler()->RequestEnrollmentNow();
+  EXPECT_EQ(1u, delegate_policy_references().size());
+}
+
+TEST_F(DeviceSyncNetworkAwareEnrollmentSchedulerTest,
+       RequestReceivedWhenNoNetworksExist) {
+  fake_network_unaware_scheduler()->RequestEnrollmentNow();
+  EXPECT_TRUE(delegate_policy_references().empty());
+
+  // Add a network and connect it; this should cause the pending request to be
+  // forwarded.
+  AddDisconnectedWifiNetwork();
+  SetWifiNetworkStatus(NetworkConnectionStatus::kConnected);
+  EXPECT_EQ(1u, delegate_policy_references().size());
+}
+
+}  // namespace device_sync
+
+}  // namespace chromeos
diff --git a/components/arc/arc_prefs.cc b/components/arc/arc_prefs.cc
index 1e56771..e72aa38 100644
--- a/components/arc/arc_prefs.cc
+++ b/components/arc/arc_prefs.cc
@@ -87,9 +87,6 @@
 // the user directory (i.e., the user finished required migration.)
 const char kArcCompatibleFilesystemChosen[] =
     "arc.compatible_filesystem.chosen";
-// A preference that indicates that user accepted Voice Interaction Value Prop.
-const char kArcVoiceInteractionValuePropAccepted[] =
-    "arc.voice_interaction_value_prop.accepted";
 // Integer pref indicating the ecryptfs to ext4 migration strategy. One of
 // options: forbidden = 0, migrate = 1, wipe = 2 or ask the user = 3.
 const char kEcryptfsMigrationStrategy[] = "ecryptfs_migration_strategy";
@@ -179,7 +176,6 @@
   registry->RegisterBooleanPref(kArcSkippedReportingNotice, false);
   registry->RegisterBooleanPref(kArcTermsAccepted, false);
   registry->RegisterBooleanPref(kArcTermsShownInOobe, false);
-  registry->RegisterBooleanPref(kArcVoiceInteractionValuePropAccepted, false);
   registry->RegisterTimeDeltaPref(kEngagementTimeBackground, base::TimeDelta());
   registry->RegisterIntegerPref(kEngagementTimeDayId, 0);
   registry->RegisterTimeDeltaPref(kEngagementTimeForeground, base::TimeDelta());
diff --git a/components/arc/arc_prefs.h b/components/arc/arc_prefs.h
index ecfc6f2..5da55db 100644
--- a/components/arc/arc_prefs.h
+++ b/components/arc/arc_prefs.h
@@ -37,7 +37,6 @@
 ARC_EXPORT extern const char kArcSkippedReportingNotice[];
 ARC_EXPORT extern const char kArcSupervisionTransition[];
 ARC_EXPORT extern const char kArcCompatibleFilesystemChosen[];
-ARC_EXPORT extern const char kArcVoiceInteractionValuePropAccepted[];
 ARC_EXPORT extern const char kEcryptfsMigrationStrategy[];
 ARC_EXPORT extern const char kEngagementTimeBackground[];
 ARC_EXPORT extern const char kEngagementTimeDayId[];
diff --git a/components/arc/common/app.mojom b/components/arc/common/app.mojom
index 99489cd..8a4449e 100644
--- a/components/arc/common/app.mojom
+++ b/components/arc/common/app.mojom
@@ -274,19 +274,12 @@
 };
 
 // Next method ID: 18
+// Deprecated method IDs: 1
 interface AppHost {
   // Sends newly added ARC app to Chrome. This message is sent when ARC receives
   // package added notification. Multiple apps may be added in the one package.
   [MinVersion=1] OnAppAddedDeprecated@2(AppInfo app);
 
-  // Receives an icon of required |scale_factor| for specific ARC app. The app
-  // is defined by |package_name| and |activity|. The icon content cannot be
-  // empty and must match to |scale_factor| assuming 48x48 for
-  // SCALE_FACTOR_100P.  |scale_factor| is an enum defined at ui/base/layout.h.
-  // |icon_png_data| is a png-encoded image.
-  OnAppIconDeprecated@1(string package_name, string activity,
-                        ScaleFactor scale_factor, array<uint8> icon_png_data);
-
   // Receives a list of available ARC apps to Chrome. Members of AppInfo must
   // contain non-empty string.
   OnAppListRefreshed@0(array<AppInfo> apps);
@@ -356,6 +349,7 @@
 
 // TODO(lhchavez): Migrate all request/response messages to Mojo.
 // Next method ID: 33
+// Deprecated method IDs: 3, 13
 interface AppInstance {
   // DEPRECATED: Please use Init@21 instead.
   InitDeprecated@0(AppHost host_ptr);
@@ -410,24 +404,12 @@
                                     int32 pixel_size) =>
                                         (array<uint8> icon_png_data);
 
-  // Sends a request to ARC for the ARC app icon of a required scale factor.
-  // Scale factor is an enum defined at ui/base/layout.h. App is defined by
-  // |package_name| and |activity|, which cannot be empty.
-  RequestAppIconDeprecated@3(string package_name, string activity,
-                             ScaleFactor scale_factor);
-
   // Sends a request for the ARC shortcut icon of a given resource id and
   // |pixel_size|.
   [MinVersion=36] RequestShortcutIcon@28(string icon_resource_id,
                                          int32 pixel_size) =>
                                              (array<uint8> icon_png_data);
 
-  // Sends a request for the ARC shortcut icon of a given resource id and scale
-  // factor.
-  [MinVersion=9] RequestShortcutIconDeprecated@13(
-      string icon_resource_id, ScaleFactor scale_factor) =>
-          (array<uint8> icon_png_data);
-
   // Sends a request for the ARC icon for a given |package_name| and
   // |pixel_size|. If |normalize| is true, the icon will be normalized per
   // Android's icon guidelines, otherwise, the raw unnormalized icon is
diff --git a/components/arc/test/fake_app_instance.h b/components/arc/test/fake_app_instance.h
index c1b848a..188eb5a 100644
--- a/components/arc/test/fake_app_instance.h
+++ b/components/arc/test/fake_app_instance.h
@@ -102,9 +102,6 @@
                       const std::string& activity,
                       int dimension,
                       RequestAppIconCallback callback) override;
-  void RequestAppIconDeprecated(const std::string& package_name,
-                                const std::string& activity,
-                                mojom::ScaleFactor scale_factor) override {}
   void LaunchIntentDeprecated(
       const std::string& intent_uri,
       const base::Optional<gfx::Rect>& dimension_on_screen) override;
@@ -112,10 +109,6 @@
   void RequestShortcutIcon(const std::string& icon_resource_id,
                            int dimension,
                            RequestShortcutIconCallback callback) override;
-  void RequestShortcutIconDeprecated(
-      const std::string& icon_resource_id,
-      mojom::ScaleFactor scale_factor,
-      RequestShortcutIconDeprecatedCallback callback) override {}
   void RequestPackageIcon(const std::string& package_name,
                           int dimension,
                           bool normalize,
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 37c6b00..f5b9fe2 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -601,6 +601,7 @@
     "//components/ukm:test_support",
     "//components/variations",
     "//components/variations:test_support",
+    "//components/variations/net",
     "//components/version_info:version_info",
     "//components/webdata/common",
     "//components/webdata_services:test_support",
diff --git a/components/autofill/core/browser/autofill_download_manager.cc b/components/autofill/core/browser/autofill_download_manager.cc
index adeb0145..9092416a6 100644
--- a/components/autofill/core/browser/autofill_download_manager.cc
+++ b/components/autofill/core/browser/autofill_download_manager.cc
@@ -637,11 +637,11 @@
   resource_request->method = method;
 
   // Add Chrome experiment state to the request headers.
-  variations::AppendVariationHeadersUnknownSignedIn(
+  variations::AppendVariationsHeaderUnknownSignedIn(
       request_url,
       driver_->IsIncognito() ? variations::InIncognito::kYes
                              : variations::InIncognito::kNo,
-      &resource_request->headers);
+      resource_request.get());
 
   // Set headers specific to the API if using it.
   if (UseApi())
@@ -650,8 +650,15 @@
                                         "base64");
 
   // Put API key in request's header if there is.
+  // Note: variations::AppendVariationsHeaderUnknownsignedIn() returned true
+  // when the vadiations header was added, but variations header is not set if
+  // variations::Incognito::kYes is passed. On the other hand, the API key is
+  // needed even for variations::Incognito::kYes. So, we need to call
+  // variations::ShouldAppendVariationsHeader() below to check the condition
+  // without variations::Incognito value. Probably we want to factor out some
+  // code to know if the URL needs the API key.
   if (!api_key_.empty() &&
-      variations::ShouldAppendVariationHeaders(request_url)) {
+      variations::ShouldAppendVariationsHeader(request_url)) {
     // Make sure that we only send the API key to endpoints trusted by Chrome.
     resource_request->headers.SetHeader(kGoogApiKey, api_key_);
   }
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index 383ed23..e1b119c6 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -846,11 +846,11 @@
     // Add Chrome experiment state to the request headers.
     net::HttpRequestHeaders headers;
     // User is always signed-in to be able to upload card to Google Payments.
-    variations::AppendVariationHeaders(
+    variations::AppendVariationsHeader(
         resource_request_->url,
         is_off_the_record_ ? variations::InIncognito::kYes
                            : variations::InIncognito::kNo,
-        variations::SignedIn::kYes, &resource_request_->headers);
+        variations::SignedIn::kYes, resource_request_.get());
   }
 }
 
diff --git a/components/autofill/core/browser/payments/payments_client_unittest.cc b/components/autofill/core/browser/payments/payments_client_unittest.cc
index 7fe6841..2d3e5350 100644
--- a/components/autofill/core/browser/payments/payments_client_unittest.cc
+++ b/components/autofill/core/browser/payments/payments_client_unittest.cc
@@ -25,6 +25,7 @@
 #include "components/autofill/core/common/autofill_switches.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/testing_pref_service.h"
+#include "components/variations/net/variations_http_headers.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/variations/variations_http_header_provider.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
@@ -65,11 +66,13 @@
     server_id_.clear();
     real_pan_.clear();
     legal_message_.reset();
+    has_variations_header_ = false;
 
     factory()->SetInterceptor(base::BindLambdaForTesting(
         [&](const network::ResourceRequest& request) {
           intercepted_headers_ = request.headers;
           intercepted_body_ = network::GetUploadData(request);
+          has_variations_header_ = variations::HasVariationsHeader(request);
         }));
     test_shared_loader_factory_ =
         base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
@@ -208,7 +211,7 @@
 
   const std::string& GetUploadData() { return intercepted_body_; }
 
-  net::HttpRequestHeaders* GetRequestHeaders() { return &intercepted_headers_; }
+  bool HasVariationsHeader() { return has_variations_header_; }
 
   // Issues access token in response to any access token request. This will
   // start the Payments Request which requires the authentication.
@@ -246,6 +249,7 @@
   identity::IdentityTestEnvironment identity_test_env_;
 
   net::HttpRequestHeaders intercepted_headers_;
+  bool has_variations_header_;
   std::string intercepted_body_;
   base::WeakPtrFactory<PaymentsClientTest> weak_ptr_factory_;
 
@@ -537,10 +541,8 @@
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartGettingUploadDetails();
 
-  std::string value;
-  EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
-  EXPECT_FALSE(value.empty());
+  EXPECT_TRUE(HasVariationsHeader());
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -555,10 +557,8 @@
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartGettingUploadDetails();
 
-  std::string value;
-  EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
-  EXPECT_TRUE(value.empty());
+  EXPECT_FALSE(HasVariationsHeader());
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -573,10 +573,8 @@
   StartUploading(/*include_cvc=*/true);
   IssueOAuthToken();
 
-  std::string value;
-  EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
-  EXPECT_FALSE(value.empty());
+  EXPECT_TRUE(HasVariationsHeader());
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -591,10 +589,8 @@
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartUploading(/*include_cvc=*/true);
 
-  std::string value;
-  EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
-  EXPECT_TRUE(value.empty());
+  EXPECT_FALSE(HasVariationsHeader());
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -609,10 +605,8 @@
   StartUnmasking();
   IssueOAuthToken();
 
-  std::string value;
-  EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
-  EXPECT_FALSE(value.empty());
+  EXPECT_TRUE(HasVariationsHeader());
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -627,10 +621,8 @@
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartUnmasking();
 
-  std::string value;
-  EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
-  EXPECT_TRUE(value.empty());
+  EXPECT_FALSE(HasVariationsHeader());
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -646,10 +638,8 @@
   StartMigrating(/*has_cardholder_name=*/true);
   IssueOAuthToken();
 
-  std::string value;
-  EXPECT_TRUE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
-  EXPECT_FALSE(value.empty());
+  EXPECT_TRUE(HasVariationsHeader());
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
@@ -664,10 +654,8 @@
   CreateFieldTrialWithId("AutofillTest", "Group", 369);
   StartMigrating(/*has_cardholder_name=*/true);
 
-  std::string value;
-  EXPECT_FALSE(GetRequestHeaders()->GetHeader("X-Client-Data", &value));
   // Note that experiment information is stored in X-Client-Data.
-  EXPECT_TRUE(value.empty());
+  EXPECT_FALSE(HasVariationsHeader());
 
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc
index 140650b..b006285 100644
--- a/components/autofill/core/browser/personal_data_manager.cc
+++ b/components/autofill/core/browser/personal_data_manager.cc
@@ -484,16 +484,12 @@
     }
 
     is_data_loaded_ = true;
-    // TODO(crbug.com/915229): Remove once the investigation is over.
-    DLOG(WARNING) << this << " refresh is done, notifying PersonalDataChanged";
     NotifyPersonalDataChanged();
   }
 }
 
 void PersonalDataManager::AutofillMultipleChanged() {
   has_synced_new_data_ = true;
-  // TODO(crbug.com/915229): Remove once the investigation is over.
-  DLOG(WARNING) << this << " has synced new data, refreshing";
   Refresh();
 }
 
@@ -2338,9 +2334,6 @@
     UpdateCardsBillingAddressReference(guids_merge_map);
 
     // Force a reload of the profiles and cards.
-    // TODO(crbug.com/915229): Remove once the investigation is over.
-    if (has_converted_addresses)
-      DLOG(WARNING) << this << " conversion of addresses done";
   }
 }
 
@@ -2351,11 +2344,6 @@
   // If the full Sync feature isn't enabled, then do NOT convert any Wallet
   // addresses to local ones.
   if (!IsSyncFeatureEnabled()) {
-    // TODO(crbug.com/915229): Remove once the investigation is over.
-    DLOG(WARNING) << this
-                  << " not converting as sync feature is not enabled, probably "
-                     "due to sync_service_ being "
-                  << sync_service_;
     return false;
   }
 
@@ -2382,8 +2370,6 @@
 
       // Update the wallet addresses metadata to record the conversion.
       wallet_address->set_has_converted(true);
-      // TODO(crbug.com/915229): Remove once the investigation is over.
-      DLOG(WARNING) << this << " converting address " << *wallet_address;
       database_helper_->GetServerDatabase()->UpdateServerAddressMetadata(
           *wallet_address);
 
diff --git a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
index 86fd8a07..297d627 100644
--- a/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
+++ b/components/autofill/core/browser/webdata/autofill_wallet_metadata_syncable_service.cc
@@ -511,8 +511,6 @@
     // get rid of this hack.
     DCHECK(!ignore_multiple_changed_notification_);
     ignore_multiple_changed_notification_ = true;
-    // TODO(crbug.com/915229): Remove once the investigation is over.
-    DLOG(WARNING) << this << " ProcessSyncChanges notify the PDM";
     web_data_backend_->NotifyOfMultipleAutofillChanges();
     ignore_multiple_changed_notification_ = false;
   }
@@ -541,13 +539,9 @@
         it->GetSpecifics().wallet_metadata();
     const AutofillProfile& local = *change.data_model();
 
-    // TODO(crbug.com/915229): Remove once the investigation is over.
-    DLOG(WARNING) << this << " AutofillProfileChanged " << local;
 
     if (!AreLocalUseStatsUpdated(remote, local) &&
         !IsLocalHasConvertedStatusUpdated(remote, local)) {
-      // TODO(crbug.com/915229): Remove once the investigation is over.
-      DLOG(WARNING) << this << " Nothing has changed, not syncing up.";
       return;
     }
 
@@ -662,8 +656,6 @@
 
 bool AutofillWalletMetadataSyncableService::UpdateAddressStats(
     const AutofillProfile& profile) {
-  // TODO(crbug.com/915229): Remove once the investigation is over.
-  DLOG(WARNING) << this << " Applying change from sync " << profile;
   return AutofillTable::FromWebDatabase(web_data_backend_->GetDatabase())
       ->UpdateServerAddressMetadata(profile);
 }
@@ -763,8 +755,6 @@
     // get rid of this hack.
     DCHECK(!ignore_multiple_changed_notification_);
     ignore_multiple_changed_notification_ = true;
-    // TODO(crbug.com/915229): Remove once the investigation is over.
-    DLOG(WARNING) << this << " MergeData notify the PDM";
     web_data_backend_->NotifyOfMultipleAutofillChanges();
     ignore_multiple_changed_notification_ = false;
   }
diff --git a/components/autofill_assistant/browser/BUILD.gn b/components/autofill_assistant/browser/BUILD.gn
index bfbd58c..125113e 100644
--- a/components/autofill_assistant/browser/BUILD.gn
+++ b/components/autofill_assistant/browser/BUILD.gn
@@ -23,6 +23,7 @@
   sources = [
     "chip.h",
     "metrics.h",
+    "overlay_state.h",
   ]
 }
 
@@ -85,6 +86,7 @@
     "features.h",
     "metrics.cc",
     "metrics.h",
+    "overlay_state.h",
     "payment_information.h",
     "protocol_utils.cc",
     "protocol_utils.h",
@@ -139,6 +141,8 @@
     "batch_element_checker_unittest.cc",
     "controller_unittest.cc",
     "element_area_unittest.cc",
+    "fake_script_executor_delegate.cc",
+    "fake_script_executor_delegate.h",
     "mock_client_memory.cc",
     "mock_client_memory.h",
     "mock_run_once_callback.h",
diff --git a/components/autofill_assistant/browser/actions/action_delegate.h b/components/autofill_assistant/browser/actions/action_delegate.h
index e090948..1c5ebf2 100644
--- a/components/autofill_assistant/browser/actions/action_delegate.h
+++ b/components/autofill_assistant/browser/actions/action_delegate.h
@@ -197,10 +197,7 @@
   virtual void SetDetails(const Details& details) = 0;
 
   // Show the progress bar and set it at |progress|%.
-  virtual void ShowProgressBar(int progress) = 0;
-
-  // Hide the progress bar.
-  virtual void HideProgressBar() = 0;
+  virtual void SetProgress(int progress) = 0;
 
  protected:
   ActionDelegate() = default;
diff --git a/components/autofill_assistant/browser/actions/mock_action_delegate.h b/components/autofill_assistant/browser/actions/mock_action_delegate.h
index ae9a8d4..99bc53f 100644
--- a/components/autofill_assistant/browser/actions/mock_action_delegate.h
+++ b/components/autofill_assistant/browser/actions/mock_action_delegate.h
@@ -138,8 +138,8 @@
   MOCK_METHOD1(StopCurrentScriptAndShutdown, void(const std::string& message));
   MOCK_METHOD1(SetDetails, void(const Details& details));
   MOCK_METHOD0(ClearDetails, void());
-  MOCK_METHOD1(ShowProgressBar, void(int progress));
-  MOCK_METHOD0(HideProgressBar, void());
+  MOCK_METHOD1(SetProgress, void(int progress));
+  MOCK_METHOD1(SetChips, void(std::unique_ptr<std::vector<Chip>> chips));
 };
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/actions/show_progress_bar_action.cc b/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
index 7094a1c..88fad8bd 100644
--- a/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
+++ b/components/autofill_assistant/browser/actions/show_progress_bar_action.cc
@@ -24,14 +24,17 @@
 void ShowProgressBarAction::InternalProcessAction(
     ActionDelegate* delegate,
     ProcessActionCallback callback) {
-  if (proto_.show_progress_bar().done()) {
-    delegate->HideProgressBar();
-  } else {
-    int progress =
-        std::min(100, std::max(0, proto_.show_progress_bar().progress()));
-    delegate->SetStatusMessage(proto_.show_progress_bar().message());
-    delegate->ShowProgressBar(progress);
+  if (proto_.show_progress_bar().progress() == 0) {
+    // Old script might still contain a ShowProgressBar action that clears the
+    // progress. Ignore these.
+    return;
   }
+  if (!proto_.show_progress_bar().message().empty()) {
+    delegate->SetStatusMessage(proto_.show_progress_bar().message());
+  }
+  int progress =
+      std::min(100, std::max(0, proto_.show_progress_bar().progress()));
+  delegate->SetProgress(progress);
 
   UpdateProcessedAction(ACTION_APPLIED);
   std::move(callback).Run(std::move(processed_action_proto_));
diff --git a/components/autofill_assistant/browser/controller.cc b/components/autofill_assistant/browser/controller.cc
index 721b514..3c673cfc 100644
--- a/components/autofill_assistant/browser/controller.cc
+++ b/components/autofill_assistant/browser/controller.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/json/json_writer.h"
+#include "base/no_destructor.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/task/post_task.h"
@@ -145,6 +146,44 @@
   return details_.get();
 }
 
+int Controller::GetProgress() const {
+  return progress_;
+}
+
+void Controller::SetProgress(int progress) {
+  // Progress can only increase.
+  if (progress_ >= progress)
+    return;
+
+  progress_ = progress;
+  GetUiController()->OnProgressChanged(progress);
+}
+
+const std::vector<Chip>& Controller::GetChips() const {
+  static const base::NoDestructor<std::vector<Chip>> no_chips_;
+  return chips_ ? *chips_ : *no_chips_;
+}
+
+void Controller::SetChips(std::unique_ptr<std::vector<Chip>> chips) {
+  if (!chips || chips->empty()) {
+    chips_.reset();
+  } else {
+    chips_ = std::move(chips);
+  }
+  GetUiController()->OnChipsChanged(GetChips());
+}
+
+void Controller::SelectChip(int chip_index) {
+  if (!chips_ || chip_index < 0 ||
+      static_cast<size_t>(chip_index) >= chips_->size()) {
+    NOTREACHED() << "Invalid chip index: " << chip_index;
+    return;
+  }
+  auto callback = std::move((*chips_)[chip_index].callback);
+  SetChips(nullptr);
+  std::move(callback).Run();
+}
+
 void Controller::EnterState(AutofillAssistantState state) {
   if (state_ == state)
     return;
@@ -255,8 +294,8 @@
   StopPeriodicScriptChecks();
   // Runnable scripts will be checked and reported if necessary after executing
   // the script.
-  script_tracker()->ClearRunnableScripts();
-  GetUiController()->ClearChips();
+  script_tracker_->ClearRunnableScripts();
+  SetChips(nullptr);
   // TODO(crbug.com/806868): Consider making ClearRunnableScripts part of
   // ExecuteScripts to simplify the controller.
   script_tracker()->ExecuteScript(
@@ -276,10 +315,6 @@
 
   if (result.touchable_element_area) {
     touchable_element_area()->SetFromProto(*result.touchable_element_area);
-  } else {
-    // For backward-compatibility, if no touchable elements are defined, the
-    // whole screen is available instead of nothing being available.
-    touchable_element_area()->CoverViewport();
   }
 
   switch (result.at_end) {
@@ -400,7 +435,7 @@
     MaybeSetInitialDetails();
     SetStatusMessage(l10n_util::GetStringFUTF8(
         IDS_AUTOFILL_ASSISTANT_LOADING, base::UTF8ToUTF16(initial_url.host())));
-    GetUiController()->ShowProgressBar(kAutostartInitialProgress);
+    SetProgress(kAutostartInitialProgress);
   }
 }
 
@@ -482,6 +517,11 @@
   return stop_reason_;
 }
 
+void Controller::GetTouchableArea(std::vector<RectF>* area) const {
+  if (touchable_element_area_)
+    touchable_element_area_->GetArea(area);
+}
+
 void Controller::OnNoRunnableScriptsAnymore() {
   if (script_tracker()->running())
     return;
@@ -541,14 +581,16 @@
     }
   }
 
-  if (state_ == AutofillAssistantState::STARTING) {
-    // If there's no script to autostart, allow access to the whole screen
-    // during the first prompt. In normal operations, touchable_element_area_ is
-    // set at the end of a successful script.
-    touchable_element_area()->CoverViewport();
+  if (allow_autostart_) {
+    // Autostart was expected, but only non-autostartable scripts were found.
+    //
+    // TODO(crbug.com/806868): Consider the case where no non-autostartable
+    // scripts were found.
+    EnterState(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT);
+  } else {
+    EnterState(AutofillAssistantState::PROMPT);
   }
-  EnterState(AutofillAssistantState::PROMPT);
-  GetUiController()->SetChips(std::move(chips));
+  SetChips(std::move(chips));
 }
 
 void Controller::DidAttachInterstitialPage() {
@@ -626,7 +668,7 @@
   if (!touchable_element_area_) {
     touchable_element_area_ = std::make_unique<ElementArea>(this);
     touchable_element_area_->SetOnUpdate(base::BindRepeating(
-        &UiController::UpdateTouchableArea,
+        &UiController::OnTouchableAreaChanged,
         // Unretained is safe, since touchable_element_area_ is guaranteed to be
         // deleted before the UI controller.
         base::Unretained(GetUiController())));
diff --git a/components/autofill_assistant/browser/controller.h b/components/autofill_assistant/browser/controller.h
index cf9ceb7..ad218db 100644
--- a/components/autofill_assistant/browser/controller.h
+++ b/components/autofill_assistant/browser/controller.h
@@ -79,9 +79,23 @@
   std::string GetStatusMessage() const override;
   void SetDetails(const Details& details) override;
   void ClearDetails() override;
+  void SetProgress(int progress) override;
+  void SetChips(std::unique_ptr<std::vector<Chip>> chips) override;
   void EnterState(AutofillAssistantState state) override;
   bool IsCookieExperimentEnabled() const;
 
+  // Overrides autofill_assistant::UiDelegate:
+  AutofillAssistantState GetState() override;
+  void UpdateTouchableArea() override;
+  void OnUserInteractionInsideTouchableArea() override;
+  const Details* GetDetails() const override;
+  int GetProgress() const override;
+  const std::vector<Chip>& GetChips() const override;
+  void SelectChip(int chip_index) override;
+  std::string GetDebugContext() override;
+  Metrics::DropOutReason GetDropOutReason() const override;
+  void GetTouchableArea(std::vector<RectF>* area) const override;
+
  private:
   friend ControllerTest;
 
@@ -126,14 +140,6 @@
   // Called when a script is selected.
   void OnScriptSelected(const std::string& script_path);
 
-  // Overrides autofill_assistant::UiDelegate:
-  AutofillAssistantState GetState() override;
-  void UpdateTouchableArea() override;
-  void OnUserInteractionInsideTouchableArea() override;
-  const Details* GetDetails() const override;
-  std::string GetDebugContext() override;
-  Metrics::DropOutReason GetDropOutReason() const override;
-
   // Overrides ScriptTracker::Listener:
   void OnNoRunnableScriptsAnymore() override;
   void OnRunnableScriptsChanged(
@@ -199,6 +205,12 @@
   // Current details, may be null.
   std::unique_ptr<Details> details_;
 
+  // Current progress.
+  int progress_ = 0;
+
+  // Current set of chips. May be null, but never empty.
+  std::unique_ptr<std::vector<Chip>> chips_;
+
   // Flag indicates whether it is ready to fetch and execute scripts.
   bool started_ = false;
 
diff --git a/components/autofill_assistant/browser/controller_unittest.cc b/components/autofill_assistant/browser/controller_unittest.cc
index 689e163..f80260d 100644
--- a/components/autofill_assistant/browser/controller_unittest.cc
+++ b/components/autofill_assistant/browser/controller_unittest.cc
@@ -20,10 +20,13 @@
 namespace autofill_assistant {
 
 using ::testing::_;
+using ::testing::AnyNumber;
 using ::testing::AtLeast;
 using ::testing::Contains;
 using ::testing::ElementsAre;
 using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Gt;
 using ::testing::InSequence;
 using ::testing::Invoke;
 using ::testing::NiceMock;
@@ -88,6 +91,9 @@
     ON_CALL(*mock_service_, OnGetActions(_, _, _, _, _, _))
         .WillByDefault(RunOnceCallback<5>(true, ""));
 
+    ON_CALL(*mock_service_, OnGetNextActions(_, _, _, _))
+        .WillByDefault(RunOnceCallback<3>(true, ""));
+
     // Make WebController::GetUrl accessible.
     ON_CALL(*mock_web_controller_, GetUrl()).WillByDefault(ReturnRef(url_));
 
@@ -125,6 +131,14 @@
     run_once->set_status(SCRIPT_STATUS_NOT_RUN);
   }
 
+  void SetupScriptsForURL(const std::string& url,
+                          SupportsScriptResponseProto scripts) {
+    std::string scripts_str;
+    scripts.SerializeToString(&scripts_str);
+    EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(Eq(GURL(url)), _, _))
+        .WillOnce(RunOnceCallback<2>(true, scripts_str));
+  }
+
   void Start() {
     GURL initialUrl("http://initialurl.com");
     controller_->Start(initialUrl, /* parameters= */ {});
@@ -165,7 +179,7 @@
   }
 
   void ExecuteScript(const std::string& script_path) {
-    controller_->ExecuteScript(script_path);
+    controller_->OnScriptSelected(script_path);
   }
 
   UiDelegate* GetUiDelegate() { return controller_.get(); }
@@ -185,45 +199,75 @@
   Start();
 
   // Going to the URL triggers a whole flow:
-  // 1. loading scripts
+  // loading scripts
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "script1");
+  auto* script2 = AddRunnableScript(&script_response, "script2");
+  RunOnce(script2);
+  SetNextScriptResponse(script_response);
+
+  testing::InSequence seq;
+
+  // Start the flow.
+  SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+
+  // Offering the choices: script1 and script2
+  EXPECT_EQ(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT,
+            controller_->GetState());
+  EXPECT_THAT(controller_->GetChips(),
+              UnorderedElementsAre(Field(&Chip::text, StrEq("script1")),
+                                   Field(&Chip::text, StrEq("script2"))));
+
+  // Choose script2 and run it successfully.
+  EXPECT_CALL(*mock_service_, OnGetActions(StrEq("script2"), _, _, _, _, _))
+      .WillOnce(RunOnceCallback<5>(true, ""));
+  controller_->SelectChip(1);
+
+  // Offering the remaining choice: script1 as script2 can only run once.
+  EXPECT_EQ(AutofillAssistantState::PROMPT, controller_->GetState());
+  EXPECT_THAT(controller_->GetChips(),
+              ElementsAre(Field(&Chip::text, StrEq("script1"))));
+}
+
+TEST_F(ControllerTest, ReportPromptAndChipsChanged) {
+  Start();
+
   SupportsScriptResponseProto script_response;
   AddRunnableScript(&script_response, "script1");
   AddRunnableScript(&script_response, "script2");
   SetNextScriptResponse(script_response);
 
-  // 2. checking the scripts
-  // 3. offering the choices: script1 and script2
-  // 4. script1 is chosen
-  EXPECT_CALL(mock_ui_controller_, SetChips(Pointee(SizeIs(2))))
-      .WillOnce([this](std::unique_ptr<std::vector<Chip>> chips) {
-        std::vector<std::string> texts;
-        for (const auto& chip : *chips) {
-          texts.emplace_back(chip.text);
-        }
-        EXPECT_THAT(texts, UnorderedElementsAre("script1", "script2"));
-
-        Sequence sequence;
-        // Selecting a script should clean the bottom bar.
-        EXPECT_CALL(mock_ui_controller_, ClearChips()).InSequence(sequence);
-        // After the script is done both scripts are again valid and should be
-        // shown.
-        EXPECT_CALL(mock_ui_controller_, SetChips(Pointee(SizeIs(2))))
-            .InSequence(sequence);
-
-        std::move((*chips)[0].callback).Run();
-      });
-
-  // 5. script1 run successfully (no actions).
-  EXPECT_CALL(*mock_service_, OnGetActions(StrEq("script1"), _, _, _, _, _))
-      .WillOnce(RunOnceCallback<5>(true, ""));
-
-  // 6. As nothing is selected the flow terminates.
-
-  // Start the flow.
+  EXPECT_CALL(mock_ui_controller_, OnChipsChanged(SizeIs(2)));
+  EXPECT_CALL(
+      mock_ui_controller_,
+      OnStateChanged(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT));
   SimulateNavigateToUrl(GURL("http://a.example.com/path"));
 }
 
-TEST_F(ControllerTest, ShowFirstInitialPrompt) {
+TEST_F(ControllerTest, ClearChipsWhenRunning) {
+  Start();
+
+  SupportsScriptResponseProto script_response;
+  AddRunnableScript(&script_response, "script1");
+  AddRunnableScript(&script_response, "script2");
+  SetNextScriptResponse(script_response);
+
+  // Discover 2 scripts, one is selected and run (with no chips shown), then the
+  // same chips are shown.
+  {
+    testing::InSequence seq;
+    // Discover 2 scripts, script1 and script2.
+    EXPECT_CALL(mock_ui_controller_, OnChipsChanged(SizeIs(2)));
+    // Set of chips is cleared while running script1.
+    EXPECT_CALL(mock_ui_controller_, OnChipsChanged(SizeIs(0)));
+    // This test doesn't specify what happens after that.
+    EXPECT_CALL(mock_ui_controller_, OnChipsChanged(_)).Times(AnyNumber());
+  }
+  SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+  controller_->SelectChip(0);
+}
+
+TEST_F(ControllerTest, ShowFirstInitialStatusMessage) {
   Start();
 
   SupportsScriptResponseProto script_response;
@@ -246,12 +290,12 @@
 
   SetNextScriptResponse(script_response);
 
-  // Script3, with higher priority (lower number), wins.
-  EXPECT_CALL(mock_ui_controller_, OnStatusMessageChanged("script3 prompt"));
-  EXPECT_CALL(mock_ui_controller_, SetChips(Pointee(SizeIs(4))));
-
   // Start the flow.
   SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+
+  // Script3, with higher priority (lower number), wins.
+  EXPECT_EQ("script3 prompt", controller_->GetStatusMessage());
+  EXPECT_THAT(controller_->GetChips(), SizeIs(4));
 }
 
 TEST_F(ControllerTest, Stop) {
@@ -263,8 +307,6 @@
   actions_response.SerializeToString(&actions_response_str);
   EXPECT_CALL(*mock_service_, OnGetActions(StrEq("stop"), _, _, _, _, _))
       .WillOnce(RunOnceCallback<5>(true, actions_response_str));
-  EXPECT_CALL(*mock_service_, OnGetNextActions(_, _, _, _))
-      .WillOnce(RunOnceCallback<3>(true, ""));
 
   EXPECT_CALL(mock_ui_controller_, Shutdown(_));
 
@@ -273,26 +315,21 @@
 
 TEST_F(ControllerTest, Reset) {
   Start();
-  {
-    InSequence sequence;
 
     // 1. Fetch scripts for URL, which in contains a single "reset" script.
     SupportsScriptResponseProto script_response;
-    AddRunnableScript(&script_response, "reset");
+    auto* reset_script = AddRunnableScript(&script_response, "reset");
+    RunOnce(reset_script);
     std::string script_response_str;
     script_response.SerializeToString(&script_response_str);
     EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
-        .WillOnce(RunOnceCallback<2>(true, script_response_str));
+        .WillRepeatedly(RunOnceCallback<2>(true, script_response_str));
+
+    SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+    EXPECT_THAT(controller_->GetChips(),
+                ElementsAre(Field(&Chip::text, StrEq("reset"))));
 
     // 2. Execute the "reset" script, which contains a reset action.
-    EXPECT_CALL(mock_ui_controller_, SetChips(Pointee(SizeIs(1))))
-        .WillOnce([](std::unique_ptr<std::vector<Chip>> chips) {
-          std::move((*chips)[0].callback).Run();
-        });
-
-    // Selecting a script should clean the bottom bar.
-    EXPECT_CALL(mock_ui_controller_, ClearChips());
-
     ActionsResponseProto actions_response;
     actions_response.add_actions()->mutable_reset();
     std::string actions_response_str;
@@ -300,26 +337,19 @@
     EXPECT_CALL(*mock_service_, OnGetActions(StrEq("reset"), _, _, _, _, _))
         .WillOnce(RunOnceCallback<5>(true, actions_response_str));
 
-    // 3. Report the result of running that action.
-    EXPECT_CALL(*mock_service_, OnGetNextActions(_, _, _, _))
-        .WillOnce(RunOnceCallback<3>(true, ""));
+    controller_->GetClientMemory()->set_selected_card(
+        std::make_unique<autofill::CreditCard>());
+    EXPECT_TRUE(controller_->GetClientMemory()->has_selected_card());
 
-    // 4. The reset action forces a reload of the scripts, even though the URL
-    // hasn't changed. The "reset" script is reported again to UpdateScripts.
-    EXPECT_CALL(*mock_service_, OnGetScriptsForUrl(_, _, _))
-        .WillOnce(RunOnceCallback<2>(true, script_response_str));
+    controller_->SelectChip(0);
 
-    // Reset forces the controller to fetch the scripts twice, even though the
-    // URL doesn't change..
-    EXPECT_CALL(mock_ui_controller_, SetChips(Pointee(SizeIs(1))));
-  }
+    // Resetting should have cleared the client memory
+    EXPECT_FALSE(controller_->GetClientMemory()->has_selected_card());
 
-  // Resetting should clear the client memory
-  controller_->GetClientMemory()->set_selected_card(nullptr);
-
-  SimulateNavigateToUrl(GURL("http://a.example.com/path"));
-
-  EXPECT_FALSE(controller_->GetClientMemory()->selected_card());
+    // The reset script should be available again, even though it's marked
+    // RunOnce, as the script state should have been cleared as well.
+    EXPECT_THAT(controller_->GetChips(),
+                ElementsAre(Field(&Chip::text, StrEq("reset"))));
 }
 
 TEST_F(ControllerTest, RefreshScriptWhenDomainChanges) {
@@ -432,9 +462,12 @@
   RunOnce(autostart);
   SetRepeatedScriptResponse(script_response);
 
-  EXPECT_CALL(mock_ui_controller_, ClearChips()).Times(AtLeast(1));
+  EXPECT_CALL(mock_ui_controller_, OnChipsChanged(SizeIs(0u)))
+      .Times(AnyNumber());
+  EXPECT_CALL(mock_ui_controller_, OnChipsChanged(SizeIs(Gt(0u)))).Times(0);
 
   SimulateNavigateToUrl(GURL("http://a.example.com/path"));
+  EXPECT_THAT(controller_->GetChips(), SizeIs(0));
 }
 
 TEST_F(ControllerTest, LoadProgressChanged) {
@@ -481,6 +514,29 @@
   EXPECT_TRUE(controller_->IsCookieExperimentEnabled());
 }
 
+TEST_F(ControllerTest, ProgressIncreasesAtStart) {
+  EXPECT_EQ(0, controller_->GetProgress());
+  EXPECT_CALL(mock_ui_controller_, OnProgressChanged(10));
+  Start();
+  EXPECT_EQ(10, controller_->GetProgress());
+}
+
+TEST_F(ControllerTest, SetProgress) {
+  Start();
+  EXPECT_CALL(mock_ui_controller_, OnProgressChanged(20));
+  controller_->SetProgress(20);
+  EXPECT_EQ(20, controller_->GetProgress());
+}
+
+TEST_F(ControllerTest, IgnoreProgressDecreases) {
+  Start();
+  EXPECT_CALL(mock_ui_controller_, OnProgressChanged(Not(15)))
+      .Times(AnyNumber());
+  controller_->SetProgress(20);
+  controller_->SetProgress(15);
+  EXPECT_EQ(20, controller_->GetProgress());
+}
+
 TEST_F(ControllerTest, StateChanges) {
   EXPECT_EQ(AutofillAssistantState::INACTIVE, GetUiDelegate()->GetState());
   Start();
@@ -495,7 +551,8 @@
 
   SimulateNavigateToUrl(GURL("http://a.example.com/path"));
 
-  EXPECT_EQ(AutofillAssistantState::PROMPT, GetUiDelegate()->GetState());
+  EXPECT_EQ(AutofillAssistantState::AUTOSTART_FALLBACK_PROMPT,
+            GetUiDelegate()->GetState());
 
   // Run script1: State should become RUNNING, as there's another script, then
   // go back to prompt to propose that script.
@@ -516,4 +573,5 @@
                                    AutofillAssistantState::PROMPT,
                                    AutofillAssistantState::STOPPED));
 }
+
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/element_area.cc b/components/autofill_assistant/browser/element_area.cc
index 33046ff..28fec361 100644
--- a/components/autofill_assistant/browser/element_area.cc
+++ b/components/autofill_assistant/browser/element_area.cc
@@ -30,19 +30,11 @@
 ElementArea::~ElementArea() = default;
 
 void ElementArea::Clear() {
-  cover_viewport_ = false;
-  rectangles_.clear();
-  ReportUpdate();
-}
-
-void ElementArea::CoverViewport() {
-  cover_viewport_ = true;
   rectangles_.clear();
   ReportUpdate();
 }
 
 void ElementArea::SetFromProto(const ElementAreaProto& proto) {
-  cover_viewport_ = false;
   rectangles_.clear();
   for (const auto& rectangle_proto : proto.rectangles()) {
     rectangles_.emplace_back();
@@ -90,9 +82,6 @@
 }
 
 bool ElementArea::IsEmpty() const {
-  if (cover_viewport_)
-    return false;
-
   for (const auto& rectangle : rectangles_) {
     for (const auto& position : rectangle.positions) {
       if (!position.rect.empty()) {
@@ -103,6 +92,15 @@
   return true;
 }
 
+void ElementArea::GetArea(std::vector<RectF>* area) {
+  for (auto& rectangle : rectangles_) {
+    RectF rect;
+    if (rectangle.FillRect(&rect)) {
+      area->emplace_back(rect);
+    }
+  }
+}
+
 ElementArea::ElementPosition::ElementPosition() = default;
 ElementArea::ElementPosition::ElementPosition(const ElementPosition& orig) =
     default;
@@ -185,18 +183,6 @@
   if (!on_update_)
     return;
 
-  if (cover_viewport_) {
-    std::vector<RectF> areas;
-    areas.emplace_back();
-    RectF& rect = areas.back();
-    rect.left = 0.0;
-    rect.top = 0.0;
-    rect.right = 1.0;
-    rect.bottom = 1.0;
-    on_update_.Run(true, areas);
-    return;
-  }
-
   for (const auto& rectangle : rectangles_) {
     if (rectangle.IsPending()) {
       // We don't have everything we need yet
@@ -204,14 +190,9 @@
     }
   }
 
-  std::vector<RectF> areas;
-  for (auto& rectangle : rectangles_) {
-    RectF rect;
-    if (rectangle.FillRect(&rect)) {
-      areas.emplace_back(rect);
-    }
-  }
-  on_update_.Run(!rectangles_.empty(), areas);
+  std::vector<RectF> area;
+  GetArea(&area);
+  on_update_.Run(area);
 }
 
 }  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/element_area.h b/components/autofill_assistant/browser/element_area.h
index 7b4e996..35b39ca 100644
--- a/components/autofill_assistant/browser/element_area.h
+++ b/components/autofill_assistant/browser/element_area.h
@@ -28,10 +28,6 @@
   // Clears the area. Stops scheduled updates.
   void Clear();
 
-  // Defines the area as covering the whole viewport. This is equivalent to
-  // defining an area that corresponds to the document body.
-  void CoverViewport();
-
   // Updates the area and keep checking for the element position and reporting
   // it until the area is cleared.
   //
@@ -52,19 +48,23 @@
   bool IsEmpty() const;
 
   // Returns true if there are elements to check.
-  bool HasElements() const { return cover_viewport_ || !rectangles_.empty(); }
+  bool HasElements() const { return !rectangles_.empty(); }
 
   // Defines a callback that'll be run every time the set of element coordinates
   // changes.
   //
-  // The first argument is true if there are any elements in the area. The
-  // second reports the areas that corresponds to currently known elements,
-  // which might be empty.
+  // The argument reports the areas that corresponds to currently known
+  // elements, which might be empty.
   void SetOnUpdate(
-      base::RepeatingCallback<void(bool, const std::vector<RectF>& areas)> cb) {
+      base::RepeatingCallback<void(const std::vector<RectF>& areas)> cb) {
     on_update_ = cb;
   }
 
+  // Adds the current touchable area to the vector, if any are available.
+  //
+  // Note that the vector is not cleared before rectangles are added.
+  void GetArea(std::vector<RectF>* area);
+
  private:
   // A rectangle that corresponds to the area of the visual viewport covered by
   // an element. Coordinates are values between 0 and 1, relative to the size of
@@ -109,13 +109,11 @@
 
   ScriptExecutorDelegate* const delegate_;
   std::vector<Rectangle> rectangles_;
-  bool cover_viewport_ = false;
 
   // If true, regular updates are currently scheduled.
   bool scheduled_update_;
 
-  base::RepeatingCallback<void(bool, const std::vector<RectF>& areas)>
-      on_update_;
+  base::RepeatingCallback<void(const std::vector<RectF>& areas)> on_update_;
 
   base::WeakPtrFactory<ElementArea> weak_ptr_factory_;
 
diff --git a/components/autofill_assistant/browser/element_area_unittest.cc b/components/autofill_assistant/browser/element_area_unittest.cc
index c258735..8ad4115 100644
--- a/components/autofill_assistant/browser/element_area_unittest.cc
+++ b/components/autofill_assistant/browser/element_area_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/strings/stringprintf.h"
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
+#include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
 #include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_web_controller.h"
 #include "components/autofill_assistant/browser/script_executor_delegate.h"
@@ -41,85 +42,54 @@
 
 ACTION(DoNothing) {}
 
-class ElementAreaTest : public testing::Test, public ScriptExecutorDelegate {
+class ElementAreaTest : public testing::Test {
  protected:
   ElementAreaTest()
       : scoped_task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
-        element_area_(this) {
+        element_area_(&delegate_) {
+    delegate_.SetWebController(&mock_web_controller_);
     ON_CALL(mock_web_controller_, OnGetElementPosition(_, _))
         .WillByDefault(RunOnceCallback<1>(false, RectF()));
     element_area_.SetOnUpdate(base::BindRepeating(&ElementAreaTest::OnUpdate,
                                                   base::Unretained(this)));
   }
 
-  // Overrides ScriptTrackerDelegate
-  Service* GetService() override { return nullptr; }
-
-  UiController* GetUiController() override { return nullptr; }
-
-  WebController* GetWebController() override { return &mock_web_controller_; }
-
-  ClientMemory* GetClientMemory() override { return nullptr; }
-
-  void EnterState(AutofillAssistantState state) override {}
-
-  const std::map<std::string, std::string>& GetParameters() override {
-    return parameters_;
-  }
-
-  autofill::PersonalDataManager* GetPersonalDataManager() override {
-    return nullptr;
-  }
-
-  content::WebContents* GetWebContents() override { return nullptr; }
-
-  void SetTouchableElementArea(const ElementAreaProto& element_area) override {}
-
-  void SetStatusMessage(const std::string& status_message) override {}
-  std::string GetStatusMessage() const override { return std::string(); }
-
-  void SetDetails(const Details& details) override {}
-
-  void ClearDetails() override {}
-
   void SetElement(const std::string& selector) {
     ElementAreaProto area;
     area.add_rectangles()->add_elements()->add_selectors(selector);
     element_area_.SetFromProto(area);
   }
 
-  void OnUpdate(bool success, const std::vector<RectF>& area) {
-    highlighted_area_ = area;
-  }
+  void OnUpdate(const std::vector<RectF>& area) { reported_area_ = area; }
 
   // scoped_task_environment_ must be first to guarantee other field
   // creation run in that environment.
   base::test::ScopedTaskEnvironment scoped_task_environment_;
 
   MockWebController mock_web_controller_;
-  std::map<std::string, std::string> parameters_;
+  FakeScriptExecutorDelegate delegate_;
   ElementArea element_area_;
-  std::vector<RectF> highlighted_area_;
+  std::vector<RectF> reported_area_;
 };
 
 TEST_F(ElementAreaTest, Empty) {
   EXPECT_TRUE(element_area_.IsEmpty());
-  EXPECT_THAT(highlighted_area_, IsEmpty());
+  EXPECT_THAT(reported_area_, IsEmpty());
+
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, IsEmpty());
 }
 
 TEST_F(ElementAreaTest, ElementNotFound) {
   SetElement("#not_found");
   EXPECT_TRUE(element_area_.IsEmpty());
-  EXPECT_THAT(highlighted_area_, IsEmpty());
-}
+  EXPECT_THAT(reported_area_, IsEmpty());
 
-TEST_F(ElementAreaTest, CoverViewport) {
-  element_area_.CoverViewport();
-  EXPECT_TRUE(element_area_.HasElements());
-  EXPECT_FALSE(element_area_.IsEmpty());
-  EXPECT_THAT(highlighted_area_,
-              ElementsAre(MatchingRectF(0.0f, 0.0f, 1.0f, 1.0f)));
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, IsEmpty());
 }
 
 TEST_F(ElementAreaTest, OneRectangle) {
@@ -129,7 +99,20 @@
 
   SetElement("#found");
   EXPECT_FALSE(element_area_.IsEmpty());
-  EXPECT_THAT(highlighted_area_,
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles,
+              ElementsAre(MatchingRectF(0.25f, 0.25f, 0.75f, 0.75f)));
+}
+
+TEST_F(ElementAreaTest, CallOnUpdate) {
+  EXPECT_CALL(mock_web_controller_,
+              OnGetElementPosition(Eq(Selector({"#found"})), _))
+      .WillOnce(RunOnceCallback<1>(true, RectF(0.25f, 0.25f, 0.75f, 0.75f)));
+
+  SetElement("#found");
+  EXPECT_FALSE(element_area_.IsEmpty());
+  EXPECT_THAT(reported_area_,
               ElementsAre(MatchingRectF(0.25f, 0.25f, 0.75f, 0.75f)));
 }
 
@@ -147,9 +130,10 @@
   element_area_.SetFromProto(area_proto);
 
   EXPECT_FALSE(element_area_.IsEmpty());
-  EXPECT_THAT(highlighted_area_,
-              ElementsAre(MatchingRectF(0.0f, 0.0f, 0.25f, 0.25f),
-                          MatchingRectF(0.25f, 0.25f, 1.0f, 1.0f)));
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0.0f, 0.0f, 0.25f, 0.25f),
+                                      MatchingRectF(0.25f, 0.25f, 1.0f, 1.0f)));
 }
 
 TEST_F(ElementAreaTest, OneRectangleTwoElements) {
@@ -167,8 +151,9 @@
   element_area_.SetFromProto(area_proto);
 
   EXPECT_FALSE(element_area_.IsEmpty());
-  EXPECT_THAT(highlighted_area_,
-              ElementsAre(MatchingRectF(0.1f, 0.2f, 0.6f, 0.5f)));
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0.1f, 0.2f, 0.6f, 0.5f)));
 }
 
 TEST_F(ElementAreaTest, DoNotReportIncompleteRectangles) {
@@ -190,7 +175,11 @@
 
   EXPECT_TRUE(element_area_.HasElements());
   EXPECT_FALSE(element_area_.IsEmpty());
-  EXPECT_THAT(highlighted_area_, IsEmpty());
+  EXPECT_THAT(reported_area_, IsEmpty());
+
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0.1f, 0.3f, 0.2f, 0.4f)));
 }
 
 TEST_F(ElementAreaTest, OneRectangleFourElements) {
@@ -215,11 +204,12 @@
   rectangle_proto->add_elements()->add_selectors("#element4");
   element_area_.SetFromProto(area_proto);
 
-  EXPECT_THAT(highlighted_area_,
-              ElementsAre(MatchingRectF(0.0f, 0.0f, 1.0f, 1.0f)));
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0.0f, 0.0f, 1.0f, 1.0f)));
 }
 
-TEST_F(ElementAreaTest, OneRectangleMissingElements) {
+TEST_F(ElementAreaTest, OneRectangleMissingElementsReported) {
   EXPECT_CALL(mock_web_controller_,
               OnGetElementPosition(Eq(Selector({"#element1"})), _))
       .WillOnce(RunOnceCallback<1>(true, RectF(0.1f, 0.1f, 0.2f, 0.2f)));
@@ -233,7 +223,11 @@
   rectangle_proto->add_elements()->add_selectors("#element2");
   element_area_.SetFromProto(area_proto);
 
-  EXPECT_THAT(highlighted_area_,
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0.1f, 0.1f, 0.2f, 0.2f)));
+
+  EXPECT_THAT(reported_area_,
               ElementsAre(MatchingRectF(0.1f, 0.1f, 0.2f, 0.2f)));
 }
 
@@ -252,8 +246,9 @@
   rectangle_proto->set_full_width(true);
   element_area_.SetFromProto(area_proto);
 
-  EXPECT_THAT(highlighted_area_,
-              ElementsAre(MatchingRectF(0.0f, 0.3f, 1.0f, 0.8f)));
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0.0f, 0.3f, 1.0f, 0.8f)));
 }
 
 TEST_F(ElementAreaTest, ElementMovesAfterUpdate) {
@@ -265,12 +260,18 @@
 
   SetElement("#element");
 
-  EXPECT_THAT(highlighted_area_,
+  EXPECT_THAT(reported_area_,
               ElementsAre(MatchingRectF(0.0f, 0.25f, 1.0f, 0.5f)));
 
   element_area_.UpdatePositions();
 
-  EXPECT_THAT(highlighted_area_,
+  // Updated area is available
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0.0f, 0.5f, 1.0f, 0.75f)));
+
+  // Updated area is reported
+  EXPECT_THAT(reported_area_,
               ElementsAre(MatchingRectF(0.0f, 0.5f, 1.0f, 0.75f)));
 }
 
@@ -283,13 +284,19 @@
 
   SetElement("#element");
 
-  EXPECT_THAT(highlighted_area_,
+  EXPECT_THAT(reported_area_,
               ElementsAre(MatchingRectF(0.0f, 0.25f, 1.0f, 0.5f)));
 
   scoped_task_environment_.FastForwardBy(
       base::TimeDelta::FromMilliseconds(100));
 
-  EXPECT_THAT(highlighted_area_,
+  // Updated area is available
+  std::vector<RectF> rectangles;
+  element_area_.GetArea(&rectangles);
+  EXPECT_THAT(rectangles, ElementsAre(MatchingRectF(0.0f, 0.5f, 1.0f, 0.75f)));
+
+  // Updated area is reported
+  EXPECT_THAT(reported_area_,
               ElementsAre(MatchingRectF(0.0f, 0.5f, 1.0f, 0.75f)));
 }
 }  // namespace
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.cc b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
new file mode 100644
index 0000000..8b0cd877
--- /dev/null
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.cc
@@ -0,0 +1,73 @@
+// 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/autofill_assistant/browser/fake_script_executor_delegate.h"
+
+#include <utility>
+
+namespace autofill_assistant {
+
+FakeScriptExecutorDelegate::FakeScriptExecutorDelegate() = default;
+FakeScriptExecutorDelegate::~FakeScriptExecutorDelegate() = default;
+
+Service* FakeScriptExecutorDelegate::GetService() {
+  return service_;
+}
+
+UiController* FakeScriptExecutorDelegate::GetUiController() {
+  return ui_controller_;
+}
+
+WebController* FakeScriptExecutorDelegate::GetWebController() {
+  return web_controller_;
+}
+
+ClientMemory* FakeScriptExecutorDelegate::GetClientMemory() {
+  return &memory_;
+}
+
+const std::map<std::string, std::string>&
+FakeScriptExecutorDelegate::GetParameters() {
+  return parameters_;
+}
+
+autofill::PersonalDataManager*
+FakeScriptExecutorDelegate::GetPersonalDataManager() {
+  return nullptr;
+}
+
+content::WebContents* FakeScriptExecutorDelegate::GetWebContents() {
+  return nullptr;
+}
+
+void FakeScriptExecutorDelegate::EnterState(AutofillAssistantState state) {
+  state_ = state;
+}
+
+void FakeScriptExecutorDelegate::SetTouchableElementArea(
+    const ElementAreaProto& element) {}
+
+void FakeScriptExecutorDelegate::SetStatusMessage(const std::string& message) {
+  status_message_ = message;
+}
+
+std::string FakeScriptExecutorDelegate::GetStatusMessage() const {
+  return status_message_;
+}
+
+void FakeScriptExecutorDelegate::SetDetails(const Details& details) {
+  details_ = std::make_unique<Details>(details);
+}
+
+void FakeScriptExecutorDelegate::ClearDetails() {
+  details_ = nullptr;
+}
+
+void FakeScriptExecutorDelegate::SetProgress(int progress) {}
+
+void FakeScriptExecutorDelegate::SetChips(
+    std::unique_ptr<std::vector<Chip>> chips) {
+  chips_ = std::move(chips);
+}
+}  // namespace autofill_assistant
diff --git a/components/autofill_assistant/browser/fake_script_executor_delegate.h b/components/autofill_assistant/browser/fake_script_executor_delegate.h
new file mode 100644
index 0000000..fa9c5ec
--- /dev/null
+++ b/components/autofill_assistant/browser/fake_script_executor_delegate.h
@@ -0,0 +1,77 @@
+// 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_AUTOFILL_ASSISTANT_BROWSER_FAKE_SCRIPT_EXECUTOR_DELEGATE_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_FAKE_SCRIPT_EXECUTOR_DELEGATE_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "components/autofill_assistant/browser/client_memory.h"
+#include "components/autofill_assistant/browser/script_executor_delegate.h"
+
+namespace autofill_assistant {
+
+// Implementation of ScriptExecutorDelegate that's convenient to use in
+// unittests.
+class FakeScriptExecutorDelegate : public ScriptExecutorDelegate {
+ public:
+  FakeScriptExecutorDelegate();
+  ~FakeScriptExecutorDelegate() override;
+
+  Service* GetService() override;
+  UiController* GetUiController() override;
+  WebController* GetWebController() override;
+  ClientMemory* GetClientMemory() override;
+  const std::map<std::string, std::string>& GetParameters() override;
+  autofill::PersonalDataManager* GetPersonalDataManager() override;
+  content::WebContents* GetWebContents() override;
+  void EnterState(AutofillAssistantState state) override;
+  void SetTouchableElementArea(const ElementAreaProto& element) override;
+  void SetStatusMessage(const std::string& message) override;
+  std::string GetStatusMessage() const override;
+  void SetDetails(const Details& details) override;
+  void ClearDetails() override;
+  void SetProgress(int progress) override;
+  void SetChips(std::unique_ptr<std::vector<Chip>> chips) override;
+
+  void SetService(Service* service) { service_ = service; }
+
+  void SetUiController(UiController* ui_controller) {
+    ui_controller_ = ui_controller;
+  }
+
+  void SetWebController(WebController* web_controller) {
+    web_controller_ = web_controller;
+  }
+
+  std::map<std::string, std::string>* GetMutableParameters() {
+    return &parameters_;
+  }
+
+  AutofillAssistantState GetState() { return state_; }
+
+  Details* GetDetails() { return details_.get(); }
+
+  std::vector<Chip>* GetChips() { return chips_.get(); }
+
+ private:
+  Service* service_ = nullptr;
+  UiController* ui_controller_ = nullptr;
+  WebController* web_controller_ = nullptr;
+  ClientMemory memory_;
+  std::map<std::string, std::string> parameters_;
+  AutofillAssistantState state_ = AutofillAssistantState::INACTIVE;
+  std::string status_message_;
+  std::unique_ptr<Details> details_;
+  std::unique_ptr<std::vector<Chip>> chips_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeScriptExecutorDelegate);
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_FAKE_SCRIPT_EXECUTOR_DELEGATE_H_
diff --git a/components/autofill_assistant/browser/mock_ui_controller.h b/components/autofill_assistant/browser/mock_ui_controller.h
index fc481073..1777834 100644
--- a/components/autofill_assistant/browser/mock_ui_controller.h
+++ b/components/autofill_assistant/browser/mock_ui_controller.h
@@ -26,8 +26,7 @@
   MOCK_METHOD1(OnStateChanged, void(AutofillAssistantState));
   MOCK_METHOD1(Shutdown, void(Metrics::DropOutReason));
   MOCK_METHOD0(Close, void());
-  MOCK_METHOD1(SetChips, void(std::unique_ptr<std::vector<Chip>> chips));
-  MOCK_METHOD0(ClearChips, void());
+  MOCK_METHOD1(OnChipsChanged, void(const std::vector<Chip>& chips));
   MOCK_METHOD3(
       GetPaymentInformation,
       void(payments::mojom::PaymentOptionsPtr payment_options,
@@ -35,12 +34,9 @@
                callback,
            const std::vector<std::string>& supported_basic_card_networks));
   MOCK_METHOD1(OnDetailsChanged, void(const Details* details));
-  MOCK_METHOD1(ShowProgressBar, void(int progress));
-  MOCK_METHOD0(HideProgressBar, void());
-  MOCK_METHOD2(UpdateTouchableArea,
-               void(bool enabled, const std::vector<RectF>& areas));
+  MOCK_METHOD1(OnProgressChanged, void(int progress));
+  MOCK_METHOD1(OnTouchableAreaChanged, void(const std::vector<RectF>& areas));
   MOCK_CONST_METHOD0(Terminate, bool());
-  MOCK_METHOD0(ExpandBottomSheet, void());
   MOCK_CONST_METHOD0(GetDropOutReason, Metrics::DropOutReason());
 };
 
diff --git a/components/autofill_assistant/browser/overlay_state.h b/components/autofill_assistant/browser/overlay_state.h
new file mode 100644
index 0000000..0c22d1c5
--- /dev/null
+++ b/components/autofill_assistant/browser/overlay_state.h
@@ -0,0 +1,31 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_OVERLAY_STATE_H_
+#define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_OVERLAY_STATE_H_
+
+#include <string>
+
+#include "base/callback.h"
+
+namespace autofill_assistant {
+
+// GENERATED_JAVA_ENUM_PACKAGE: (
+// org.chromium.chrome.browser.autofill_assistant.overlay)
+// GENERATED_JAVA_CLASS_NAME_OVERRIDE: AssistantOverlayState
+enum OverlayState {
+  // The overlay is completely hidden.
+  HIDDEN = 0,
+
+  // The overlay is enabled and covers the whole web page.
+  FULL = 1,
+
+  // The overlay is enabled but some portions of the web page might still be
+  // accessible.
+  PARTIAL = 2,
+};
+
+}  // namespace autofill_assistant
+
+#endif  // COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_OVERLAY_STATE_H_
diff --git a/components/autofill_assistant/browser/script_executor.cc b/components/autofill_assistant/browser/script_executor.cc
index 80be670..a21b94cd 100644
--- a/components/autofill_assistant/browser/script_executor.cc
+++ b/components/autofill_assistant/browser/script_executor.cc
@@ -188,11 +188,11 @@
   }
 
   delegate_->EnterState(AutofillAssistantState::PROMPT);
-  delegate_->GetUiController()->SetChips(std::move(chips));
+  delegate_->SetChips(std::move(chips));
 }
 
 void ScriptExecutor::CancelPrompt() {
-  delegate_->GetUiController()->ClearChips();
+  delegate_->SetChips(nullptr);
   CleanUpAfterPrompt();
 }
 
@@ -246,12 +246,8 @@
       std::make_unique<ElementAreaProto>(touchable_element_area);
 }
 
-void ScriptExecutor::ShowProgressBar(int progress) {
-  delegate_->GetUiController()->ShowProgressBar(progress);
-}
-
-void ScriptExecutor::HideProgressBar() {
-  delegate_->GetUiController()->HideProgressBar();
+void ScriptExecutor::SetProgress(int progress) {
+  delegate_->SetProgress(progress);
 }
 
 void ScriptExecutor::SetFieldValue(const Selector& selector,
diff --git a/components/autofill_assistant/browser/script_executor.h b/components/autofill_assistant/browser/script_executor.h
index 07ac54a..e342375 100644
--- a/components/autofill_assistant/browser/script_executor.h
+++ b/components/autofill_assistant/browser/script_executor.h
@@ -157,8 +157,7 @@
   void StopCurrentScriptAndShutdown(const std::string& message) override;
   void ClearDetails() override;
   void SetDetails(const Details& details) override;
-  void ShowProgressBar(int progress) override;
-  void HideProgressBar() override;
+  void SetProgress(int progress) override;
 
  private:
   // Helper for WaitForElementVisible that keeps track of the state required to
diff --git a/components/autofill_assistant/browser/script_executor_delegate.h b/components/autofill_assistant/browser/script_executor_delegate.h
index b728f774..c7c2a54a 100644
--- a/components/autofill_assistant/browser/script_executor_delegate.h
+++ b/components/autofill_assistant/browser/script_executor_delegate.h
@@ -6,8 +6,11 @@
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_SCRIPT_EXECUTOR_DELEGATE_H_
 
 #include <map>
+#include <memory>
 #include <string>
+#include <vector>
 
+#include "components/autofill_assistant/browser/chip.h"
 #include "components/autofill_assistant/browser/details.h"
 #include "components/autofill_assistant/browser/state.h"
 
@@ -51,6 +54,8 @@
   virtual std::string GetStatusMessage() const = 0;
   virtual void SetDetails(const Details& details) = 0;
   virtual void ClearDetails() = 0;
+  virtual void SetProgress(int progress) = 0;
+  virtual void SetChips(std::unique_ptr<std::vector<Chip>> chips) = 0;
 
   // Makes no area of the screen touchable.
   void ClearTouchableElementArea() {
diff --git a/components/autofill_assistant/browser/script_executor_unittest.cc b/components/autofill_assistant/browser/script_executor_unittest.cc
index 9a4b391..f954e1dc 100644
--- a/components/autofill_assistant/browser/script_executor_unittest.cc
+++ b/components/autofill_assistant/browser/script_executor_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/test/mock_callback.h"
 #include "base/test/scoped_task_environment.h"
 #include "components/autofill_assistant/browser/client_memory.h"
+#include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
 #include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_service.h"
 #include "components/autofill_assistant/browser/mock_ui_controller.h"
@@ -44,16 +45,19 @@
 const char* kScriptPath = "script_path";
 
 class ScriptExecutorTest : public testing::Test,
-                           public ScriptExecutorDelegate,
                            public ScriptExecutor::Listener {
  public:
   void SetUp() override {
+    delegate_.SetService(&mock_service_);
+    delegate_.SetUiController(&mock_ui_controller_);
+    delegate_.SetWebController(&mock_web_controller_);
+
     executor_ = std::make_unique<ScriptExecutor>(
         kScriptPath,
         /* global_payload= */ "initial global payload",
         /* script_payload= */ "initial payload",
         /* listener= */ this, &scripts_state_, &ordered_interrupts_,
-        /* delegate= */ this);
+        /* delegate= */ &delegate_);
     url_ = GURL("http://example.com/");
 
     // In this test, "tell" actions always succeed and "click" actions always
@@ -73,39 +77,6 @@
       : scoped_task_environment_(
             base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {}
 
-  // Implements ScriptExecutorDelegate
-  Service* GetService() override { return &mock_service_; }
-
-  UiController* GetUiController() override { return &mock_ui_controller_; }
-
-  WebController* GetWebController() override { return &mock_web_controller_; }
-
-  ClientMemory* GetClientMemory() override { return &memory_; }
-
-  void SetTouchableElementArea(const ElementAreaProto& area) {}
-
-  void SetStatusMessage(const std::string& status_message) {
-    status_message_ = status_message;
-  }
-
-  std::string GetStatusMessage() const override { return status_message_; }
-
-  void ClearDetails() override { cleared_details_ = true; }
-
-  void SetDetails(const Details& details) override {}
-
-  void EnterState(AutofillAssistantState state) {}
-
-  const std::map<std::string, std::string>& GetParameters() override {
-    return parameters_;
-  }
-
-  autofill::PersonalDataManager* GetPersonalDataManager() override {
-    return nullptr;
-  }
-
-  content::WebContents* GetWebContents() override { return nullptr; }
-
   // Implements ScriptExecutor::Listener
   void OnServerPayloadChanged(const std::string& global_payload,
                               const std::string& script_payload) override {
@@ -175,8 +146,8 @@
   // scoped_task_environment_ must be first to guarantee other field
   // creation run in that environment.
   base::test::ScopedTaskEnvironment scoped_task_environment_;
+  FakeScriptExecutorDelegate delegate_;
   Script script_;
-  ClientMemory memory_;
   StrictMock<MockService> mock_service_;
   NiceMock<MockWebController> mock_web_controller_;
   NiceMock<MockUiController> mock_ui_controller_;
@@ -191,12 +162,9 @@
   std::vector<std::unique_ptr<Script>> scripts_update_;
   int scripts_update_count_ = 0;
   std::unique_ptr<ScriptExecutor> executor_;
-  std::map<std::string, std::string> parameters_;
   StrictMock<base::MockCallback<ScriptExecutor::RunScriptCallback>>
       executor_callback_;
   GURL url_;
-  std::string status_message_;
-  bool cleared_details_ = false;
 };
 
 TEST_F(ScriptExecutorTest, GetActionsFails) {
@@ -210,8 +178,9 @@
 }
 
 TEST_F(ScriptExecutorTest, ForwardParameters) {
-  parameters_["param1"] = "value1";
-  parameters_["param2"] = "value2";
+  auto* parameters = delegate_.GetMutableParameters();
+  (*parameters)["param1"] = "value1";
+  (*parameters)["param2"] = "value2";
   EXPECT_CALL(mock_service_,
               OnGetActions(StrEq(kScriptPath), _,
                            AllOf(Contains(Pair("param1", "value1")),
@@ -405,8 +374,10 @@
       .WillOnce(RunOnceCallback<3>(true, ""));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
+
+  delegate_.SetDetails(Details());  // empty, but not null
   executor_->Run(executor_callback_.Get());
-  EXPECT_TRUE(cleared_details_);
+  EXPECT_EQ(nullptr, delegate_.GetDetails());
 }
 
 TEST_F(ScriptExecutorTest, DontClearDetailsIfOtherActionsAreLeft) {
@@ -424,9 +395,9 @@
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
 
+  delegate_.SetDetails(Details());  // empty, but not null
   executor_->Run(executor_callback_.Get());
-
-  EXPECT_FALSE(cleared_details_);
+  EXPECT_NE(nullptr, delegate_.GetDetails());
 }
 
 TEST_F(ScriptExecutorTest, ClearDetailsOnError) {
@@ -438,9 +409,9 @@
       .WillOnce(RunOnceCallback<3>(false, ""));
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, false)));
-
+  delegate_.SetDetails(Details());  // empty, but not null
   executor_->Run(executor_callback_.Get());
-  EXPECT_TRUE(cleared_details_);
+  EXPECT_EQ(nullptr, delegate_.GetDetails());
 }
 
 TEST_F(ScriptExecutorTest, UpdateScriptStateWhileRunning) {
@@ -917,9 +888,9 @@
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
 
-  status_message_ = "pre-run status";
+  delegate_.SetStatusMessage("pre-run status");
   executor_->Run(executor_callback_.Get());
-  EXPECT_EQ("pre-interrupt status", status_message_);
+  EXPECT_EQ("pre-interrupt status", delegate_.GetStatusMessage());
 }
 
 TEST_F(ScriptExecutorTest, KeepStatusMessageWhenNotInterrupted) {
@@ -938,9 +909,9 @@
   EXPECT_CALL(executor_callback_,
               Run(Field(&ScriptExecutor::Result::success, true)));
 
-  status_message_ = "pre-run status";
+  delegate_.SetStatusMessage("pre-run status");
   executor_->Run(executor_callback_.Get());
-  EXPECT_EQ("pre-interrupt status", status_message_);
+  EXPECT_EQ("pre-interrupt status", delegate_.GetStatusMessage());
 }
 
 }  // namespace
diff --git a/components/autofill_assistant/browser/script_tracker_unittest.cc b/components/autofill_assistant/browser/script_tracker_unittest.cc
index 4784a1fac..4f53a47 100644
--- a/components/autofill_assistant/browser/script_tracker_unittest.cc
+++ b/components/autofill_assistant/browser/script_tracker_unittest.cc
@@ -7,7 +7,7 @@
 #include <utility>
 
 #include "base/test/mock_callback.h"
-#include "components/autofill_assistant/browser/client_memory.h"
+#include "components/autofill_assistant/browser/fake_script_executor_delegate.h"
 #include "components/autofill_assistant/browser/mock_run_once_callback.h"
 #include "components/autofill_assistant/browser/mock_service.h"
 #include "components/autofill_assistant/browser/mock_ui_controller.h"
@@ -30,9 +30,7 @@
 using ::testing::StrictMock;
 using ::testing::UnorderedElementsAre;
 
-class ScriptTrackerTest : public testing::Test,
-                          public ScriptTracker::Listener,
-                          public ScriptExecutorDelegate {
+class ScriptTrackerTest : public testing::Test, public ScriptTracker::Listener {
  public:
   void SetUp() override {
     ON_CALL(mock_web_controller_,
@@ -53,40 +51,12 @@
   ScriptTrackerTest()
       : no_runnable_scripts_anymore_(0),
         runnable_scripts_changed_(0),
-        tracker_(this, this) {}
-
-  // Overrides ScriptTrackerDelegate
-  Service* GetService() override { return &mock_service_; }
-
-  UiController* GetUiController() override { return &mock_ui_controller_; }
-
-  WebController* GetWebController() override { return &mock_web_controller_; }
-
-  ClientMemory* GetClientMemory() override { return &client_memory_; }
-
-  void EnterState(AutofillAssistantState state) override {}
-
-  const std::map<std::string, std::string>& GetParameters() override {
-    return parameters_;
+        tracker_(&delegate_, /* listener=*/this) {
+    delegate_.SetService(&mock_service_);
+    delegate_.SetUiController(&mock_ui_controller_);
+    delegate_.SetWebController(&mock_web_controller_);
   }
 
-  autofill::PersonalDataManager* GetPersonalDataManager() override {
-    return nullptr;
-  }
-
-  content::WebContents* GetWebContents() override { return nullptr; }
-
-  void SetTouchableElementArea(const ElementAreaProto& element_area) override {}
-
-  void SetStatusMessage(const std::string& status_message) override {
-    status_message_ = status_message;
-  }
-  std::string GetStatusMessage() const { return status_message_; }
-
-  void SetDetails(const Details& details) override {}
-
-  void ClearDetails() override {}
-
   // Overrides ScriptTracker::Listener
   void OnRunnableScriptsChanged(
       const std::vector<ScriptHandle>& runnable_scripts) override {
@@ -150,16 +120,14 @@
   NiceMock<MockService> mock_service_;
   NiceMock<MockWebController> mock_web_controller_;
   NiceMock<MockUiController> mock_ui_controller_;
-  ClientMemory client_memory_;
-  std::map<std::string, std::string> parameters_;
 
   // Number of times NoRunnableScriptsAnymore was called.
   int no_runnable_scripts_anymore_;
   // Number of times OnRunnableScriptsChanged was called.
   int runnable_scripts_changed_;
   std::vector<ScriptHandle> runnable_scripts_;
+  FakeScriptExecutorDelegate delegate_;
   ScriptTracker tracker_;
-  std::string status_message_;
 };
 
 TEST_F(ScriptTrackerTest, NoScripts) {
diff --git a/components/autofill_assistant/browser/service.proto b/components/autofill_assistant/browser/service.proto
index 843a939..e718ff9 100644
--- a/components/autofill_assistant/browser/service.proto
+++ b/components/autofill_assistant/browser/service.proto
@@ -515,9 +515,6 @@
 
 // Shows the progress bar.
 message ShowProgressBarProto {
-  // Specifies whether the progress is done and should be removed.
-  optional bool done = 2;
-
   // Message to show on the progress bar while loading.
   optional string message = 3;
 
diff --git a/components/autofill_assistant/browser/state.h b/components/autofill_assistant/browser/state.h
index 6573f5ce..e6a770f 100644
--- a/components/autofill_assistant/browser/state.h
+++ b/components/autofill_assistant/browser/state.h
@@ -31,6 +31,12 @@
   // the data for a payment request.
   PROMPT,
 
+  // Autofill assistant is waiting for the user to make the first choice.
+  //
+  // When autostartable scripts are expected, this is only triggered as a
+  // fallback if there are non-autostartable scripts to choose from instead.
+  AUTOSTART_FALLBACK_PROMPT,
+
   // Autofill assistant is expecting a modal dialog, such as the one asking for
   // CVC.
   MODAL_DIALOG,
diff --git a/components/autofill_assistant/browser/ui_controller.h b/components/autofill_assistant/browser/ui_controller.h
index 4e59f38..d15be9e3 100644
--- a/components/autofill_assistant/browser/ui_controller.h
+++ b/components/autofill_assistant/browser/ui_controller.h
@@ -40,11 +40,8 @@
   // Shuts down Autofill Assistant and closes Chrome.
   virtual void Close() = 0;
 
-  // Show UI to ask user to make a choice between different chips.
-  virtual void SetChips(std::unique_ptr<std::vector<Chip>> chips) = 0;
-
-  // Remove all chips from the UI.
-  virtual void ClearChips() = 0;
+  // Report that the set of chips has changed.
+  virtual void OnChipsChanged(const std::vector<Chip>& chips) = 0;
 
   // Get payment information (through similar to payment request UX) to fill
   // forms.
@@ -57,20 +54,16 @@
   // cleared.
   virtual void OnDetailsChanged(const Details* details) = 0;
 
-  // Show the progress bar and set it at |progress|%.
-  virtual void ShowProgressBar(int progress) = 0;
+  // Called when the current progress has changed. Progress, is expressed as a
+  // percentage.
+  virtual void OnProgressChanged(int progress) = 0;
 
-  // Hide the progress bar.
-  virtual void HideProgressBar() = 0;
-
-  // Updates the area of the visible viewport that is accessible.
-  //
-  // If |enabled| is false, the visible viewport is accessible.
+  // Updates the area of the visible viewport that is accessible when the
+  // overlay state is OverlayState::PARTIAL.
   //
   // |areas| is expressed in coordinates relative to the width or height of the
   // visible viewport, as a number between 0 and 1. It can be empty.
-  virtual void UpdateTouchableArea(bool enabled,
-                                   const std::vector<RectF>& areas) = 0;
+  virtual void OnTouchableAreaChanged(const std::vector<RectF>& areas) = 0;
 
  protected:
   UiController() = default;
diff --git a/components/autofill_assistant/browser/ui_delegate.h b/components/autofill_assistant/browser/ui_delegate.h
index 62e67bd..a881872 100644
--- a/components/autofill_assistant/browser/ui_delegate.h
+++ b/components/autofill_assistant/browser/ui_delegate.h
@@ -6,8 +6,11 @@
 #define COMPONENTS_AUTOFILL_ASSISTANT_BROWSER_UI_DELEGATE_H_
 
 #include <string>
+#include <vector>
 
+#include "base/optional.h"
 #include "components/autofill_assistant/browser/metrics.h"
+#include "components/autofill_assistant/browser/rectf.h"
 #include "components/autofill_assistant/browser/state.h"
 
 namespace autofill_assistant {
@@ -39,9 +42,27 @@
   // Returns the current contextual information. May be null if empty.
   virtual const Details* GetDetails() const = 0;
 
+  // Returns the current progress; a percentage.
+  virtual int GetProgress() const = 0;
+
+  // Returns the current set of chips.
+  virtual const std::vector<Chip>& GetChips() const = 0;
+
+  // Selects a chip, from the set of chips returned by GetChips().
+  virtual void SelectChip(int chip) = 0;
+
   // Returns the drop out reason for the last state transition to STOPPED.
   virtual Metrics::DropOutReason GetDropOutReason() const = 0;
 
+  // Adds the rectangles that correspond to the current touchable area to the
+  // given vector.
+  //
+  // |areas| is expressed in coordinates relative to the width or height of the
+  // visible viewport, as a number between 0 and 1. It can be empty.
+  //
+  // Note that the vector is not cleared before rectangles are added.
+  virtual void GetTouchableArea(std::vector<RectF>* area) const = 0;
+
  protected:
   UiDelegate() = default;
 };
diff --git a/components/browser_sync/BUILD.gn b/components/browser_sync/BUILD.gn
index ce5383c..fef1dd0 100644
--- a/components/browser_sync/BUILD.gn
+++ b/components/browser_sync/BUILD.gn
@@ -16,8 +16,6 @@
     "signin_confirmation_helper.h",
     "sync_auth_manager.cc",
     "sync_auth_manager.h",
-    "sync_user_settings_impl.cc",
-    "sync_user_settings_impl.h",
   ]
 
   public_deps = [
@@ -105,8 +103,6 @@
     "profile_sync_service_mock.h",
     "profile_sync_test_util.cc",
     "profile_sync_test_util.h",
-    "sync_user_settings_mock.cc",
-    "sync_user_settings_mock.h",
     "test_http_bridge_factory.cc",
     "test_http_bridge_factory.h",
     "test_profile_sync_service.cc",
diff --git a/components/browser_sync/profile_sync_service.cc b/components/browser_sync/profile_sync_service.cc
index 4baaf2a..8d29299c 100644
--- a/components/browser_sync/profile_sync_service.cc
+++ b/components/browser_sync/profile_sync_service.cc
@@ -260,7 +260,7 @@
   data_type_controllers_ =
       BuildDataTypeControllerMap(sync_client_->CreateDataTypeControllers(this));
 
-  user_settings_ = std::make_unique<SyncUserSettingsImpl>(
+  user_settings_ = std::make_unique<syncer::SyncUserSettingsImpl>(
       &crypto_, &sync_prefs_, GetRegisteredDataTypes(),
       base::BindRepeating(&ProfileSyncService::SyncAllowedByPlatformChanged,
                           base::Unretained(this)));
diff --git a/components/browser_sync/profile_sync_service.h b/components/browser_sync/profile_sync_service.h
index 710e157..18cf930d 100644
--- a/components/browser_sync/profile_sync_service.h
+++ b/components/browser_sync/profile_sync_service.h
@@ -20,7 +20,6 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread.h"
 #include "base/time/time.h"
-#include "components/browser_sync/sync_user_settings_impl.h"
 #include "components/invalidation/public/identity_provider.h"
 #include "components/sync/base/experiments.h"
 #include "components/sync/base/model_type.h"
@@ -35,6 +34,7 @@
 #include "components/sync/driver/sync_service.h"
 #include "components/sync/driver/sync_service_crypto.h"
 #include "components/sync/driver/sync_stopped_reporter.h"
+#include "components/sync/driver/sync_user_settings_impl.h"
 #include "components/sync/engine/configure_reason.h"
 #include "components/sync/engine/events/protocol_event_observer.h"
 #include "components/sync/engine/net/network_time_update_callback.h"
@@ -507,7 +507,7 @@
   // email address and sign-out upon error.
   identity::IdentityManager* const identity_manager_;
 
-  std::unique_ptr<SyncUserSettingsImpl> user_settings_;
+  std::unique_ptr<syncer::SyncUserSettingsImpl> user_settings_;
 
   // Handles tracking of the authenticated account and acquiring access tokens.
   // Only null after Shutdown().
diff --git a/components/browser_sync/profile_sync_service_mock.cc b/components/browser_sync/profile_sync_service_mock.cc
index 2e2c1d4..c0692f3 100644
--- a/components/browser_sync/profile_sync_service_mock.cc
+++ b/components/browser_sync/profile_sync_service_mock.cc
@@ -15,7 +15,7 @@
 
 ProfileSyncServiceMock::~ProfileSyncServiceMock() {}
 
-SyncUserSettingsMock* ProfileSyncServiceMock::GetUserSettingsMock() {
+syncer::SyncUserSettingsMock* ProfileSyncServiceMock::GetUserSettingsMock() {
   return &user_settings_;
 }
 
diff --git a/components/browser_sync/profile_sync_service_mock.h b/components/browser_sync/profile_sync_service_mock.h
index 5ac4ce0..5516713e 100644
--- a/components/browser_sync/profile_sync_service_mock.h
+++ b/components/browser_sync/profile_sync_service_mock.h
@@ -10,8 +10,8 @@
 
 #include "base/memory/weak_ptr.h"
 #include "components/browser_sync/profile_sync_service.h"
-#include "components/browser_sync/sync_user_settings_mock.h"
 #include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_user_settings_mock.h"
 #include "components/sync/protocol/sync_protocol_error.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -23,7 +23,7 @@
   explicit ProfileSyncServiceMock(InitParams init_params);
   ~ProfileSyncServiceMock() override;
 
-  SyncUserSettingsMock* GetUserSettingsMock();
+  syncer::SyncUserSettingsMock* GetUserSettingsMock();
 
   // SyncService overrides.
   syncer::SyncUserSettings* GetUserSettings() override;
@@ -94,7 +94,7 @@
   GetSetupInProgressHandleConcrete();
 
  private:
-  testing::NiceMock<SyncUserSettingsMock> user_settings_;
+  testing::NiceMock<syncer::SyncUserSettingsMock> user_settings_;
 };
 
 }  // namespace browser_sync
diff --git a/components/browser_sync/sync_auth_manager_unittest.cc b/components/browser_sync/sync_auth_manager_unittest.cc
index 945f7c2..129472e4 100644
--- a/components/browser_sync/sync_auth_manager_unittest.cc
+++ b/components/browser_sync/sync_auth_manager_unittest.cc
@@ -17,6 +17,7 @@
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "net/base/net_errors.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
diff --git a/components/browser_sync/sync_user_settings_impl.cc b/components/browser_sync/sync_user_settings_impl.cc
deleted file mode 100644
index e01353a..0000000
--- a/components/browser_sync/sync_user_settings_impl.cc
+++ /dev/null
@@ -1,161 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/browser_sync/sync_user_settings_impl.h"
-
-#include "base/metrics/histogram_macros.h"
-#include "components/sync/base/sync_prefs.h"
-#include "components/sync/driver/sync_service_crypto.h"
-
-namespace browser_sync {
-
-SyncUserSettingsImpl::SyncUserSettingsImpl(
-    syncer::SyncServiceCrypto* crypto,
-    syncer::SyncPrefs* prefs,
-    syncer::ModelTypeSet registered_types,
-    const base::RepeatingCallback<void(bool)>& sync_allowed_by_platform_changed)
-    : crypto_(crypto),
-      prefs_(prefs),
-      registered_types_(registered_types),
-      sync_allowed_by_platform_changed_cb_(sync_allowed_by_platform_changed) {
-  DCHECK(crypto_);
-  DCHECK(prefs_);
-}
-
-SyncUserSettingsImpl::~SyncUserSettingsImpl() = default;
-
-bool SyncUserSettingsImpl::IsSyncRequested() const {
-  return prefs_->IsSyncRequested();
-}
-
-void SyncUserSettingsImpl::SetSyncRequested(bool requested) {
-  prefs_->SetSyncRequested(requested);
-}
-
-bool SyncUserSettingsImpl::IsSyncAllowedByPlatform() const {
-  return sync_allowed_by_platform_;
-}
-
-void SyncUserSettingsImpl::SetSyncAllowedByPlatform(bool allowed) {
-  if (sync_allowed_by_platform_ == allowed) {
-    return;
-  }
-
-  sync_allowed_by_platform_ = allowed;
-
-  sync_allowed_by_platform_changed_cb_.Run(sync_allowed_by_platform_);
-}
-
-bool SyncUserSettingsImpl::IsFirstSetupComplete() const {
-  return prefs_->IsFirstSetupComplete();
-}
-
-void SyncUserSettingsImpl::SetFirstSetupComplete() {
-  prefs_->SetFirstSetupComplete();
-}
-
-bool SyncUserSettingsImpl::IsSyncEverythingEnabled() const {
-  return prefs_->HasKeepEverythingSynced();
-}
-
-syncer::ModelTypeSet SyncUserSettingsImpl::GetChosenDataTypes() const {
-  syncer::ModelTypeSet types = GetPreferredDataTypes();
-  types.RetainAll(syncer::UserSelectableTypes());
-  return types;
-}
-
-void SyncUserSettingsImpl::SetChosenDataTypes(bool sync_everything,
-                                              syncer::ModelTypeSet types) {
-  DCHECK(syncer::UserSelectableTypes().HasAll(types));
-
-  prefs_->SetDataTypesConfiguration(sync_everything, registered_types_, types);
-}
-
-bool SyncUserSettingsImpl::IsEncryptEverythingAllowed() const {
-  return crypto_->IsEncryptEverythingAllowed();
-}
-
-void SyncUserSettingsImpl::SetEncryptEverythingAllowed(bool allowed) {
-  crypto_->SetEncryptEverythingAllowed(allowed);
-}
-
-bool SyncUserSettingsImpl::IsEncryptEverythingEnabled() const {
-  return crypto_->IsEncryptEverythingEnabled();
-}
-
-void SyncUserSettingsImpl::EnableEncryptEverything() {
-  crypto_->EnableEncryptEverything();
-}
-
-bool SyncUserSettingsImpl::IsPassphraseRequired() const {
-  return crypto_->passphrase_required_reason() !=
-         syncer::REASON_PASSPHRASE_NOT_REQUIRED;
-}
-
-bool SyncUserSettingsImpl::IsPassphraseRequiredForDecryption() const {
-  // If there is an encrypted datatype enabled and we don't have the proper
-  // passphrase, we must prompt the user for a passphrase. The only way for the
-  // user to avoid entering their passphrase is to disable the encrypted types.
-  return IsEncryptedDatatypeEnabled() && IsPassphraseRequired();
-}
-
-bool SyncUserSettingsImpl::IsUsingSecondaryPassphrase() const {
-  return crypto_->IsUsingSecondaryPassphrase();
-}
-
-base::Time SyncUserSettingsImpl::GetExplicitPassphraseTime() const {
-  return crypto_->GetExplicitPassphraseTime();
-}
-
-syncer::PassphraseType SyncUserSettingsImpl::GetPassphraseType() const {
-  return crypto_->GetPassphraseType();
-}
-
-void SyncUserSettingsImpl::SetEncryptionPassphrase(
-    const std::string& passphrase) {
-  crypto_->SetEncryptionPassphrase(passphrase);
-}
-
-bool SyncUserSettingsImpl::SetDecryptionPassphrase(
-    const std::string& passphrase) {
-  DCHECK(IsPassphraseRequired())
-      << "SetDecryptionPassphrase must not be called when "
-         "IsPassphraseRequired() is false.";
-
-  DVLOG(1) << "Setting passphrase for decryption.";
-
-  bool result = crypto_->SetDecryptionPassphrase(passphrase);
-  UMA_HISTOGRAM_BOOLEAN("Sync.PassphraseDecryptionSucceeded", result);
-  return result;
-}
-
-syncer::ModelTypeSet SyncUserSettingsImpl::GetPreferredDataTypes() const {
-  syncer::ModelTypeSet types = Union(
-      prefs_->GetPreferredDataTypes(registered_types_), syncer::ControlTypes());
-  if (prefs_->IsLocalSyncEnabled()) {
-    types.Remove(syncer::APP_LIST);
-    types.Remove(syncer::USER_CONSENTS);
-    types.Remove(syncer::USER_EVENTS);
-  }
-  return types;
-}
-
-syncer::ModelTypeSet SyncUserSettingsImpl::GetEncryptedDataTypes() const {
-  return crypto_->GetEncryptedDataTypes();
-}
-
-bool SyncUserSettingsImpl::IsEncryptedDatatypeEnabled() const {
-  if (IsEncryptionPending())
-    return true;
-  const syncer::ModelTypeSet preferred_types = GetPreferredDataTypes();
-  const syncer::ModelTypeSet encrypted_types = GetEncryptedDataTypes();
-  DCHECK(encrypted_types.Has(syncer::PASSWORDS));
-  return !Intersection(preferred_types, encrypted_types).Empty();
-}
-
-bool SyncUserSettingsImpl::IsEncryptionPending() const {
-  return crypto_->encryption_pending();
-}
-
-}  // namespace browser_sync
diff --git a/components/browser_sync/sync_user_settings_impl.h b/components/browser_sync/sync_user_settings_impl.h
deleted file mode 100644
index f92028bb..0000000
--- a/components/browser_sync/sync_user_settings_impl.h
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_BROWSER_SYNC_SYNC_USER_SETTINGS_IMPL_H_
-#define COMPONENTS_BROWSER_SYNC_SYNC_USER_SETTINGS_IMPL_H_
-
-#include <string>
-
-#include "base/callback.h"
-#include "components/sync/base/model_type.h"
-#include "components/sync/driver/sync_user_settings.h"
-
-namespace syncer {
-class SyncPrefs;
-class SyncServiceCrypto;
-}
-
-namespace browser_sync {
-
-class SyncUserSettingsImpl : public syncer::SyncUserSettings {
- public:
-  // Both |crypto| and |prefs| must not be null, and must outlive this object.
-  SyncUserSettingsImpl(syncer::SyncServiceCrypto* crypto,
-                       syncer::SyncPrefs* prefs,
-                       syncer::ModelTypeSet registered_types,
-                       const base::RepeatingCallback<void(bool)>&
-                           sync_allowed_by_platform_changed);
-  ~SyncUserSettingsImpl() override;
-
-  bool IsSyncRequested() const override;
-  void SetSyncRequested(bool requested) override;
-
-  bool IsSyncAllowedByPlatform() const override;
-  void SetSyncAllowedByPlatform(bool allowed) override;
-
-  bool IsFirstSetupComplete() const override;
-  void SetFirstSetupComplete() override;
-
-  bool IsSyncEverythingEnabled() const override;
-  syncer::ModelTypeSet GetChosenDataTypes() const override;
-  void SetChosenDataTypes(bool sync_everything,
-                          syncer::ModelTypeSet types) override;
-
-  bool IsEncryptEverythingAllowed() const override;
-  void SetEncryptEverythingAllowed(bool allowed) override;
-  bool IsEncryptEverythingEnabled() const override;
-  void EnableEncryptEverything() override;
-
-  syncer::ModelTypeSet GetEncryptedDataTypes() const override;
-  bool IsPassphraseRequired() const override;
-  bool IsPassphraseRequiredForDecryption() const override;
-  bool IsUsingSecondaryPassphrase() const override;
-  base::Time GetExplicitPassphraseTime() const override;
-  syncer::PassphraseType GetPassphraseType() const override;
-
-  void SetEncryptionPassphrase(const std::string& passphrase) override;
-  bool SetDecryptionPassphrase(const std::string& passphrase) override;
-
-  syncer::ModelTypeSet GetPreferredDataTypes() const;
-  bool IsEncryptedDatatypeEnabled() const;
-  bool IsEncryptionPending() const;
-
- private:
-  syncer::SyncServiceCrypto* const crypto_;
-  syncer::SyncPrefs* const prefs_;
-  const syncer::ModelTypeSet registered_types_;
-  base::RepeatingCallback<void(bool)> sync_allowed_by_platform_changed_cb_;
-
-  // Whether sync is currently allowed on this platform.
-  bool sync_allowed_by_platform_ = true;
-};
-
-}  // namespace browser_sync
-
-#endif  // COMPONENTS_BROWSER_SYNC_SYNC_USER_SETTINGS_IMPL_H_
diff --git a/components/browser_sync/sync_user_settings_mock.cc b/components/browser_sync/sync_user_settings_mock.cc
deleted file mode 100644
index 6a5c199..0000000
--- a/components/browser_sync/sync_user_settings_mock.cc
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/browser_sync/sync_user_settings_mock.h"
-
-namespace browser_sync {
-
-SyncUserSettingsMock::SyncUserSettingsMock() = default;
-
-SyncUserSettingsMock::~SyncUserSettingsMock() = default;
-
-}  // namespace browser_sync
diff --git a/components/browser_sync/sync_user_settings_mock.h b/components/browser_sync/sync_user_settings_mock.h
deleted file mode 100644
index 8d98ff1..0000000
--- a/components/browser_sync/sync_user_settings_mock.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef COMPONENTS_BROWSER_SYNC_SYNC_USER_SETTINGS_MOCK_H_
-#define COMPONENTS_BROWSER_SYNC_SYNC_USER_SETTINGS_MOCK_H_
-
-#include <string>
-
-#include "components/sync/driver/sync_user_settings.h"
-#include "testing/gmock/include/gmock/gmock.h"
-
-namespace browser_sync {
-
-class SyncUserSettingsMock : public syncer::SyncUserSettings {
- public:
-  SyncUserSettingsMock();
-  ~SyncUserSettingsMock() override;
-
-  MOCK_CONST_METHOD0(IsSyncRequested, bool());
-  MOCK_METHOD1(SetSyncRequested, void(bool));
-
-  MOCK_CONST_METHOD0(IsSyncAllowedByPlatform, bool());
-  MOCK_METHOD1(SetSyncAllowedByPlatform, void(bool));
-
-  MOCK_CONST_METHOD0(IsFirstSetupComplete, bool());
-  MOCK_METHOD0(SetFirstSetupComplete, void());
-
-  MOCK_CONST_METHOD0(IsSyncEverythingEnabled, bool());
-  MOCK_CONST_METHOD0(GetChosenDataTypes, syncer::ModelTypeSet());
-  MOCK_METHOD2(SetChosenDataTypes, void(bool, syncer::ModelTypeSet));
-
-  MOCK_CONST_METHOD0(IsEncryptEverythingAllowed, bool());
-  MOCK_METHOD1(SetEncryptEverythingAllowed, void(bool));
-  MOCK_CONST_METHOD0(IsEncryptEverythingEnabled, bool());
-  MOCK_METHOD0(EnableEncryptEverything, void());
-
-  MOCK_CONST_METHOD0(GetEncryptedDataTypes, syncer::ModelTypeSet());
-  MOCK_CONST_METHOD0(IsPassphraseRequired, bool());
-  MOCK_CONST_METHOD0(IsPassphraseRequiredForDecryption, bool());
-  MOCK_CONST_METHOD0(IsUsingSecondaryPassphrase, bool());
-  MOCK_CONST_METHOD0(GetExplicitPassphraseTime, base::Time());
-  MOCK_CONST_METHOD0(GetPassphraseType, syncer::PassphraseType());
-
-  MOCK_METHOD1(SetEncryptionPassphrase, void(const std::string&));
-  MOCK_METHOD1(SetDecryptionPassphrase, bool(const std::string&));
-};
-
-}  // namespace browser_sync
-
-#endif  // COMPONENTS_BROWSER_SYNC_SYNC_USER_SETTINGS_MOCK_H_
diff --git a/components/cast_channel/cast_socket_unittest.cc b/components/cast_channel/cast_socket_unittest.cc
index 69300b4e..adf7686 100644
--- a/components/cast_channel/cast_socket_unittest.cc
+++ b/components/cast_channel/cast_socket_unittest.cc
@@ -234,6 +234,7 @@
   DISALLOW_COPY_AND_ASSIGN(MockTestCastSocket);
 };
 
+// TODO(https://crbug.com/928467):  Remove this class.
 class TestSocketFactory : public net::ClientSocketFactory {
  public:
   explicit TestSocketFactory(net::IPEndPoint ip) : ip_(ip) {}
@@ -327,16 +328,35 @@
     }
     ssl_socket_data_provider_ = std::make_unique<net::SSLSocketDataProvider>(
         ssl_connect_data_->mode, ssl_connect_data_->result);
-    //  auto client_handle = std::make_unique<net::ClientSocketHandle>();
 
     if (tls_socket_created_)
       std::move(tls_socket_created_).Run();
 
-    // client_handle->SetSocket(std::move(tcp_socket));
     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,
+      const net::SSLClientSocketContext& context) override {
+    if (!ssl_connect_data_) {
+      // Test isn't overriding SSL socket creation.
+      return net::ClientSocketFactory::GetDefaultFactory()
+          ->CreateSSLClientSocket(std::move(nested_socket), 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(nested_socket), net::HostPortPair(), net::SSLConfig(),
+        ssl_socket_data_provider_.get());
+  }
   std::unique_ptr<net::ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<net::ClientSocketHandle> transport_socket,
       const std::string& user_agent,
diff --git a/components/crash/content/app/breakpad_linux.cc b/components/crash/content/app/breakpad_linux.cc
index a7378204..ecc7d66 100644
--- a/components/crash/content/app/breakpad_linux.cc
+++ b/components/crash/content/app/breakpad_linux.cc
@@ -2122,7 +2122,7 @@
   return g_is_crash_reporter_enabled;
 }
 
-void SetFirstChanceExceptionHandler(bool (*handler)(int, void*, void*)) {
+void SetFirstChanceExceptionHandler(bool (*handler)(int, siginfo_t*, void*)) {
   google_breakpad::SetFirstChanceExceptionHandler(handler);
 }
 
diff --git a/components/crash/content/app/breakpad_linux.h b/components/crash/content/app/breakpad_linux.h
index 9ee8555..6e95af6 100644
--- a/components/crash/content/app/breakpad_linux.h
+++ b/components/crash/content/app/breakpad_linux.h
@@ -7,6 +7,7 @@
 #ifndef COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
 #define COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
 
+#include <signal.h>
 #include <string>
 
 #include "build/build_config.h"
@@ -67,7 +68,7 @@
 
 // Install a handler that gets a change to handle faults before Breakpad does
 // any processing. This is used by V8 for trap-based bounds checks.
-void SetFirstChanceExceptionHandler(bool (*handler)(int, void*, void*));
+void SetFirstChanceExceptionHandler(bool (*handler)(int, siginfo_t*, void*));
 }  // namespace breakpad
 
 #endif  // COMPONENTS_CRASH_CONTENT_APP_BREAKPAD_LINUX_H_
diff --git a/components/cronet/stale_host_resolver.cc b/components/cronet/stale_host_resolver.cc
index abbe8af..ef09344 100644
--- a/components/cronet/stale_host_resolver.cc
+++ b/components/cronet/stale_host_resolver.cc
@@ -4,14 +4,19 @@
 
 #include "components/cronet/stale_host_resolver.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/callback_helpers.h"
+#include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/stl_util.h"
 #include "base/timer/timer.h"
 #include "base/values.h"
+#include "net/base/host_port_pair.h"
 #include "net/base/net_errors.h"
 #include "net/dns/dns_util.h"
+#include "net/dns/host_resolver_source.h"
 
 namespace cronet {
 
@@ -108,8 +113,8 @@
   // |addresses| must remain valid until the Request completes (synchronously or
   // via |result_callback|) or is canceled by destroying the Request.
   int Start(net::HostResolverImpl* resolver,
-            const RequestInfo& info,
-            net::RequestPriority priority,
+            const net::HostPortPair& host,
+            const net::HostResolver::ResolveHostParameters& parameters,
             net::AddressList* addresses,
             net::CompletionOnceCallback result_callback,
             std::unique_ptr<net::HostResolver::Request>* out_req,
@@ -122,7 +127,7 @@
  private:
   class Handle : public net::HostResolver::Request {
    public:
-    Handle(RequestImpl* request) : request_(request) {}
+    explicit Handle(RequestImpl* request) : request_(request) {}
     ~Handle() override { request_->OnHandleDestroyed(); }
 
     void ChangeRequestPriority(net::RequestPriority priority) override {
@@ -221,8 +226,8 @@
 
 int StaleHostResolver::RequestImpl::Start(
     net::HostResolverImpl* resolver,
-    const RequestInfo& info,
-    net::RequestPriority priority,
+    const net::HostPortPair& host,
+    const net::HostResolver::ResolveHostParameters& parameters,
     net::AddressList* addresses,
     net::CompletionOnceCallback result_callback,
     std::unique_ptr<net::HostResolver::Request>* out_req,
@@ -239,39 +244,54 @@
   restore_size_ = resolver->LastRestoredCacheSize();
   current_size_ = resolver->CacheSize();
 
-  net::AddressList cache_addresses;
-  net::HostCache::EntryStaleness stale_info;
-  int cache_rv = resolver->ResolveStaleFromCache(info, &cache_addresses,
-                                                 &stale_info, net_log);
-  // If it's a fresh cache hit (or literal), return it synchronously.
-  if (cache_rv != net::ERR_DNS_CACHE_MISS && !stale_info.is_stale()) {
-    RecordSynchronousRequest();
-    return HandleResult(cache_rv, cache_addresses);
-  }
+  {
+    net::HostResolver::ResolveHostParameters cache_parameters = parameters;
+    cache_parameters.cache_usage =
+        net::HostResolver::ResolveHostParameters::CacheUsage::STALE_ALLOWED;
+    cache_parameters.source = net::HostResolverSource::LOCAL_ONLY;
+    std::unique_ptr<net::HostResolver::ResolveHostRequest> cache_request =
+        resolver->CreateRequest(host, net_log, cache_parameters);
+    int cache_rv =
+        cache_request->Start(base::BindOnce([](int error) { NOTREACHED(); }));
+    DCHECK_NE(net::ERR_IO_PENDING, cache_rv);
+    // If it's a fresh cache hit (or literal), return it synchronously.
+    if (cache_rv != net::ERR_DNS_CACHE_MISS &&
+        (!cache_request->GetStaleInfo() ||
+         !cache_request->GetStaleInfo().value().is_stale())) {
+      RecordSynchronousRequest();
+      return HandleResult(cache_rv, cache_request->GetAddressResults().value_or(
+                                        net::AddressList()));
+    }
 
-  result_callback_ = std::move(result_callback);
-  handle_ = new Handle(this);
-  *out_req = std::unique_ptr<net::HostResolver::Request>(handle_);
+    result_callback_ = std::move(result_callback);
+    handle_ = new Handle(this);
+    *out_req = base::WrapUnique(handle_);
 
-  if (cache_rv == net::OK && usable_callback.Run(stale_info)) {
-    stale_error_ = cache_rv;
-    stale_addresses_ = cache_addresses;
-    // |stale_timer_| is deleted when the Request is deleted, so it's safe to
-    // use Unretained here.
-    base::Callback<void()> stale_callback =
-        base::Bind(&StaleHostResolver::RequestImpl::OnStaleDelayElapsed,
-                   base::Unretained(this));
-    stale_timer_.Start(FROM_HERE, stale_delay, stale_callback);
+    if (cache_rv == net::OK &&
+        usable_callback.Run(cache_request->GetStaleInfo().value())) {
+      stale_error_ = cache_rv;
+      stale_addresses_ = cache_request->GetAddressResults().value();
+      // |stale_timer_| is deleted when the Request is deleted, so it's safe to
+      // use Unretained here.
+      base::Callback<void()> stale_callback =
+          base::Bind(&StaleHostResolver::RequestImpl::OnStaleDelayElapsed,
+                     base::Unretained(this));
+      stale_timer_.Start(FROM_HERE, stale_delay, stale_callback);
+    }
   }
 
   // Don't check the cache again.
-  net::HostResolver::RequestInfo no_cache_info(info);
-  no_cache_info.set_allow_cached_response(false);
-  int network_rv = resolver->Resolve(
-      no_cache_info, priority, &network_addresses_,
+  net::HostResolver::ResolveHostParameters no_cache_parameters = parameters;
+  no_cache_parameters.cache_usage =
+      net::HostResolver::ResolveHostParameters::CacheUsage::DISALLOWED;
+  std::unique_ptr<net::HostResolver::ResolveHostRequest> network_request =
+      resolver->CreateRequest(host, net_log, no_cache_parameters);
+  int network_rv = net::HostResolver::LegacyResolve(
+      std::move(network_request), no_cache_parameters.is_speculative,
+      &network_addresses_,
       base::BindOnce(&StaleHostResolver::RequestImpl::OnNetworkRequestComplete,
                      base::Unretained(this)),
-      &network_request_, net_log);
+      &network_request_);
   // Network resolver has returned synchronously (for example by resolving from
   // /etc/hosts).
   if (network_rv != net::ERR_IO_PENDING) {
@@ -436,9 +456,11 @@
       base::Bind(&StaleEntryIsUsable, options_);
   RequestImpl* request =
       new RequestImpl(tick_clock_, options_.use_stale_on_name_not_resolved);
-  int rv = request->Start(inner_resolver_.get(), info, priority, addresses,
-                          std::move(callback), out_req, net_log,
-                          usable_callback, options_.delay);
+  net::HostResolver::ResolveHostParameters parameters =
+      net::HostResolver::RequestInfoToResolveHostParameters(info, priority);
+  int rv = request->Start(inner_resolver_.get(), info.host_port_pair(),
+                          parameters, addresses, std::move(callback), out_req,
+                          net_log, usable_callback, options_.delay);
   if (rv != net::ERR_IO_PENDING)
     delete request;
 
@@ -485,4 +507,4 @@
   inner_resolver_->SetTickClockForTesting(tick_clock);
 }
 
-}  // namespace net
+}  // namespace cronet
diff --git a/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc b/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc
index 200e1b9..bbbc51ad 100644
--- a/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc
+++ b/components/data_reduction_proxy/content/browser/data_reduction_proxy_pingback_client_impl.cc
@@ -429,9 +429,9 @@
                                  net::LOAD_DO_NOT_SAVE_COOKIES;
   resource_request->method = "POST";
   // Attach variations headers.
-  variations::AppendVariationHeaders(
+  variations::AppendVariationsHeader(
       pingback_url_, variations::InIncognito::kNo, variations::SignedIn::kNo,
-      &resource_request->headers);
+      resource_request.get());
   // TODO(https://crbug.com/808498): Re-add data use measurement once
   // SimpleURLLoader supports it.
   // ID=data_use_measurement::DataUseUserData::DATA_REDUCTION_PROXY
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
index 3113154..941b3c8 100644
--- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
+++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_config_service_client.cc
@@ -468,7 +468,7 @@
                                  net::LOAD_DO_NOT_SEND_COOKIES |
                                  net::LOAD_DO_NOT_SAVE_COOKIES;
   // Attach variations headers.
-  url_loader_ = variations::CreateSimpleURLLoaderWithVariationsHeaders(
+  url_loader_ = variations::CreateSimpleURLLoaderWithVariationsHeader(
       std::move(resource_request), variations::InIncognito::kNo,
       variations::SignedIn::kNo, traffic_annotation);
 
diff --git a/components/domain_reliability/quic_error_mapping.cc b/components/domain_reliability/quic_error_mapping.cc
index 42eedc82..7aaff70 100644
--- a/components/domain_reliability/quic_error_mapping.cc
+++ b/components/domain_reliability/quic_error_mapping.cc
@@ -304,12 +304,13 @@
      "quic.stream.id.in.stream_id_blocked.frame"},
     {quic::QUIC_MAX_STREAM_ID_ERROR, "quic.stream.id.in.max_stream_id.frame"},
     {quic::QUIC_HTTP_DECODER_ERROR, "quic.http.decoder.error"},
+    {quic::QUIC_STALE_CONNECTION_CANCELLED, "quic.stale.connection.cancelled"},
 
     // No error. Used as bound while iterating.
     {quic::QUIC_LAST_ERROR, "quic.last_error"}};
 
 // Must be updated any time a quic::QuicErrorCode is deprecated in
-// net/quic/core/quic_packets.h.
+// net/third_party/quic/core/quic_error_codes.h.
 const int kDeprecatedQuicErrorCount = 5;
 const int kActiveQuicErrorCount =
     quic::QUIC_LAST_ERROR - kDeprecatedQuicErrorCount;
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc
index 4b536f5..3298533 100644
--- a/components/download/internal/common/download_item_impl.cc
+++ b/components/download/internal/common/download_item_impl.cc
@@ -1122,17 +1122,11 @@
   // Record some stats. If the precondition failed (the server returned
   // HTTP_PRECONDITION_FAILED), then the download will automatically retried as
   // a full request rather than a partial. Full restarts clobber validators.
-  int origin_state = 0;
-  if (chain_iter != new_create_info.url_chain.end())
-    origin_state |= ORIGIN_STATE_ON_RESUMPTION_ADDITIONAL_REDIRECTS;
   if (etag_ != new_create_info.etag ||
       last_modified_time_ != new_create_info.last_modified) {
     received_slices_.clear();
     destination_info_.received_bytes = 0;
-    origin_state |= ORIGIN_STATE_ON_RESUMPTION_VALIDATORS_CHANGED;
   }
-  if (content_disposition_ != new_create_info.content_disposition)
-    origin_state |= ORIGIN_STATE_ON_RESUMPTION_CONTENT_DISPOSITION_CHANGED;
 
   request_info_.url_chain.insert(request_info_.url_chain.end(), chain_iter,
                                  new_create_info.url_chain.end());
diff --git a/components/download/public/common/download_stats.h b/components/download/public/common/download_stats.h
index c77dffde0..017084c 100644
--- a/components/download/public/common/download_stats.h
+++ b/components/download/public/common/download_stats.h
@@ -360,13 +360,6 @@
 
 COMPONENTS_DOWNLOAD_EXPORT void RecordSavePackageEvent(SavePackageEvent event);
 
-enum OriginStateOnResumption {
-  ORIGIN_STATE_ON_RESUMPTION_ADDITIONAL_REDIRECTS = 1 << 0,
-  ORIGIN_STATE_ON_RESUMPTION_VALIDATORS_CHANGED = 1 << 1,
-  ORIGIN_STATE_ON_RESUMPTION_CONTENT_DISPOSITION_CHANGED = 1 << 2,
-  ORIGIN_STATE_ON_RESUMPTION_MAX = 1 << 3
-};
-
 // Enumeration for histogramming purposes.
 // These values are written to logs.  New enum values can be added, but existing
 // enums must never be renumbered or deleted and reused.
diff --git a/components/download/quarantine/quarantine_mac.mm b/components/download/quarantine/quarantine_mac.mm
index c43cac8..c8626ca 100644
--- a/components/download/quarantine/quarantine_mac.mm
+++ b/components/download/quarantine/quarantine_mac.mm
@@ -10,7 +10,6 @@
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
-#include "base/mac/availability.h"
 #include "base/mac/foundation_util.h"
 #include "base/mac/mac_logging.h"
 #include "base/mac/mac_util.h"
@@ -68,10 +67,16 @@
 // There is no documented API to set metadata on a file directly as of the
 // 10.5 SDK.  The MDSetItemAttribute function does exist to perform this task,
 // but it's undocumented.
+//
+// Note that the Metadata.framework in CoreServices has been superseded by the
+// NSMetadata API (e.g. kMDItemWhereFroms -> NSMetadataItemWhereFromsKey, etc).
+// The NSMetadata API still is a query-only interface, with no way to set
+// attributes, so we continue to use the original API.
 bool AddOriginMetadataToFile(const base::FilePath& file,
                              const GURL& source,
                              const GURL& referrer) {
   base::ScopedBlockingCall scoped_blocking_call(base::BlockingType::MAY_BLOCK);
+
   // There's no declaration for MDItemSetAttribute in any known public SDK.
   // It exists in the 10.4 and 10.5 runtimes.  To play it safe, do the lookup
   // at runtime instead of declaring it ourselves and linking against what's
@@ -81,22 +86,19 @@
   //  - If Apple removes or renames the function in a future runtime, the
   //    loader won't refuse to let the application launch.  Instead, we'll
   //    silently fail to set any metadata.
-  typedef OSStatus (*MDItemSetAttribute_type)(MDItemRef, CFStringRef,
-                                              CFTypeRef);
-  static MDItemSetAttribute_type md_item_set_attribute_func = NULL;
-
-  static bool did_symbol_lookup = false;
-  if (!did_symbol_lookup) {
-    did_symbol_lookup = true;
+  using MDItemSetAttribute_type =
+      OSStatus (*)(MDItemRef, CFStringRef, CFTypeRef);
+  static MDItemSetAttribute_type md_item_set_attribute_func =
+      []() -> MDItemSetAttribute_type {
     CFBundleRef metadata_bundle =
         CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Metadata"));
     if (!metadata_bundle)
-      return false;
+      return nullptr;
 
-    md_item_set_attribute_func =
-        (MDItemSetAttribute_type)CFBundleGetFunctionPointerForName(
-            metadata_bundle, CFSTR("MDItemSetAttribute"));
-  }
+    return reinterpret_cast<MDItemSetAttribute_type>(
+        CFBundleGetFunctionPointerForName(metadata_bundle,
+                                          CFSTR("MDItemSetAttribute")));
+  }();
   if (!md_item_set_attribute_func)
     return false;
 
@@ -105,7 +107,7 @@
     return false;
 
   base::ScopedCFTypeRef<MDItemRef> md_item(
-      MDItemCreate(NULL, base::mac::NSToCFCast(file_path)));
+      MDItemCreate(kCFAllocatorDefault, base::mac::NSToCFCast(file_path)));
   if (!md_item) {
     LOG(WARNING) << "MDItemCreate failed for path " << file.value();
     return false;
@@ -114,17 +116,21 @@
   // We won't put any more than 2 items into the attribute.
   NSMutableArray* list = [NSMutableArray arrayWithCapacity:2];
 
-  // Follow Safari's lead: the first item in the list is the source URL of
-  // the downloaded file. If the referrer is known, store that, too.
+  // Follow Safari's lead: the first item in the list is the source URL of the
+  // downloaded file. If the referrer is known, store that, too. The URLs may be
+  // empty (e.g. files downloaded in Incognito mode); don't add empty URLs.
   NSString* origin_url = base::SysUTF8ToNSString(source.spec());
-  if (origin_url)
+  if (origin_url && [origin_url length])
     [list addObject:origin_url];
   NSString* referrer_url = base::SysUTF8ToNSString(referrer.spec());
-  if (referrer_url)
+  if (referrer_url && [referrer_url length])
     [list addObject:referrer_url];
 
-  md_item_set_attribute_func(md_item, kMDItemWhereFroms,
-                             base::mac::NSToCFCast(list));
+  if ([list count]) {
+    md_item_set_attribute_func(md_item, kMDItemWhereFroms,
+                               base::mac::NSToCFCast(list));
+  }
+
   return true;
 }
 
diff --git a/components/download/quarantine/quarantine_mac_unittest.mm b/components/download/quarantine/quarantine_mac_unittest.mm
index 7730710..d37d5ee 100644
--- a/components/download/quarantine/quarantine_mac_unittest.mm
+++ b/components/download/quarantine/quarantine_mac_unittest.mm
@@ -6,14 +6,15 @@
 
 #include <sys/xattr.h>
 
+#import <ApplicationServices/ApplicationServices.h>
 #import <Foundation/Foundation.h>
 
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/logging.h"
-#include "base/mac/availability.h"
-#include "base/mac/mac_util.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/scoped_cftyperef.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/strings/sys_string_conversions.h"
 #include "components/download/quarantine/test_support.h"
@@ -32,28 +33,24 @@
 
  protected:
   void SetUp() override {
-    if (@available(macos 10.10, *)) {
-      ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
-      ASSERT_TRUE(
-          base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &test_file_));
-      file_url_.reset([[NSURL alloc]
-          initFileURLWithPath:base::SysUTF8ToNSString(test_file_.value())]);
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    ASSERT_TRUE(
+        base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &test_file_));
+    file_url_.reset([[NSURL alloc]
+        initFileURLWithPath:base::SysUTF8ToNSString(test_file_.value())]);
 
-      base::scoped_nsobject<NSMutableDictionary> properties(
-          [[NSMutableDictionary alloc] init]);
-      [properties setValue:@"com.google.Chrome"
-                    forKey:static_cast<NSString*>(
-                               kLSQuarantineAgentBundleIdentifierKey)];
-      [properties setValue:@"Google Chrome.app"
-                    forKey:static_cast<NSString*>(kLSQuarantineAgentNameKey)];
-      [properties setValue:@(1) forKey:@"kLSQuarantineIsOwnedByCurrentUserKey"];
-      bool success = [file_url_ setResourceValue:properties
-                                          forKey:NSURLQuarantinePropertiesKey
-                                           error:nullptr];
-      ASSERT_TRUE(success);
-    } else {
-      LOG(WARNING) << "Test suite requires Mac OS X 10.10 or later";
-    }
+    base::scoped_nsobject<NSMutableDictionary> properties(
+        [[NSMutableDictionary alloc] init]);
+    [properties
+        setValue:@"com.google.Chrome"
+          forKey:static_cast<NSString*>(kLSQuarantineAgentBundleIdentifierKey)];
+    [properties setValue:@"Google Chrome.app"
+                  forKey:static_cast<NSString*>(kLSQuarantineAgentNameKey)];
+    [properties setValue:@(1) forKey:@"kLSQuarantineIsOwnedByCurrentUserKey"];
+    bool success = [file_url_ setResourceValue:properties
+                                        forKey:NSURLQuarantinePropertiesKey
+                                         error:nullptr];
+    ASSERT_TRUE(success);
   }
 
   base::ScopedTempDir temp_dir_;
@@ -64,16 +61,12 @@
 };
 
 TEST_F(QuarantineMacTest, CheckMetadataSetCorrectly) {
-  if (base::mac::IsAtMostOS10_9())
-    return;
   EXPECT_EQ(QuarantineFileResult::OK,
             QuarantineFile(test_file_, source_url_, referrer_url_, ""));
   EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, referrer_url_));
 }
 
 TEST_F(QuarantineMacTest, SetMetadataMultipleTimes) {
-  if (base::mac::IsAtMostOS10_9())
-    return;
   GURL dummy_url("http://www.dummy.example.com");
   EXPECT_EQ(QuarantineFileResult::OK,
             QuarantineFile(test_file_, source_url_, referrer_url_, ""));
@@ -83,21 +76,15 @@
 }
 
 TEST_F(QuarantineMacTest, IsFileQuarantined_NoFile) {
-  if (base::mac::IsAtMostOS10_9())
-    return;
   base::FilePath does_not_exist = temp_dir_.GetPath().AppendASCII("a.jar");
   EXPECT_FALSE(IsFileQuarantined(does_not_exist, GURL(), GURL()));
 }
 
 TEST_F(QuarantineMacTest, IsFileQuarantined_NoAnnotationsOnFile) {
-  if (base::mac::IsAtMostOS10_9())
-    return;
   EXPECT_FALSE(IsFileQuarantined(test_file_, GURL(), GURL()));
 }
 
 TEST_F(QuarantineMacTest, IsFileQuarantined_SourceUrlOnly) {
-  if (base::mac::IsAtMostOS10_9())
-    return;
   ASSERT_EQ(QuarantineFileResult::OK,
             QuarantineFile(test_file_, source_url_, GURL(), std::string()));
   EXPECT_TRUE(IsFileQuarantined(test_file_, source_url_, GURL()));
@@ -107,8 +94,6 @@
 }
 
 TEST_F(QuarantineMacTest, IsFileQuarantined_FullMetadata) {
-  if (base::mac::IsAtMostOS10_9())
-    return;
   ASSERT_EQ(
       QuarantineFileResult::OK,
       QuarantineFile(test_file_, source_url_, referrer_url_, std::string()));
@@ -120,5 +105,25 @@
   EXPECT_FALSE(IsFileQuarantined(test_file_, referrer_url_, referrer_url_));
 }
 
+TEST_F(QuarantineMacTest, NoWhereFromsKeyIfNoURLs) {
+  ASSERT_EQ(QuarantineFileResult::OK,
+            QuarantineFile(test_file_, GURL(), GURL(), std::string()));
+
+  NSString* file_path = base::mac::FilePathToNSString(test_file_);
+  ASSERT_NE(nullptr, file_path);
+  base::ScopedCFTypeRef<MDItemRef> md_item(
+      MDItemCreate(kCFAllocatorDefault, base::mac::NSToCFCast(file_path)));
+  if (!md_item) {
+    // The quarantine code ignores it if adding origin metadata fails. If for
+    // some reason MDItemCreate fails (which it seems to do on the bots, not
+    // sure why) just stop on the test.
+    return;
+  }
+
+  base::ScopedCFTypeRef<CFTypeRef> attr(
+      MDItemCopyAttribute(md_item, kMDItemWhereFroms));
+  EXPECT_FALSE(attr);
+}
+
 }  // namespace
-}  // namespace downlod
+}  // namespace download
diff --git a/components/feed/core/feed_networking_host.cc b/components/feed/core/feed_networking_host.cc
index a9598ee3..6bc4c4a 100644
--- a/components/feed/core/feed_networking_host.cc
+++ b/components/feed/core/feed_networking_host.cc
@@ -70,7 +70,7 @@
                                 identity::AccessTokenInfo access_token_info);
   void StartLoader();
   std::unique_ptr<network::SimpleURLLoader> MakeLoader();
-  net::HttpRequestHeaders MakeHeaders(const std::string& auth_header) const;
+  void SetRequestHeaders(network::ResourceRequest* request) const;
   void PopulateRequestBody(network::SimpleURLLoader* loader);
   void OnSimpleLoaderComplete(std::unique_ptr<std::string> response);
 
@@ -145,12 +145,6 @@
 }
 
 std::unique_ptr<network::SimpleURLLoader> NetworkFetch::MakeLoader() {
-  std::string auth_header =
-      access_token_.empty()
-          ? std::string()
-          : base::StringPrintf(kAuthorizationRequestHeaderFormat,
-                               access_token_.c_str());
-  net::HttpRequestHeaders headers = MakeHeaders(auth_header);
   // TODO(pnoland): Add data use measurement once it's supported for simple
   // url loader.
   net::NetworkTrafficAnnotationTag traffic_annotation =
@@ -187,8 +181,8 @@
 
   resource_request->load_flags = net::LOAD_BYPASS_CACHE;
   resource_request->allow_credentials = false;
-  resource_request->headers = headers;
   resource_request->method = request_type_;
+  SetRequestHeaders(resource_request.get());
 
   auto simple_loader = network::SimpleURLLoader::Create(
       std::move(resource_request), traffic_annotation);
@@ -199,22 +193,23 @@
   return simple_loader;
 }
 
-net::HttpRequestHeaders NetworkFetch::MakeHeaders(
-    const std::string& auth_header) const {
-  net::HttpRequestHeaders headers;
-  headers.SetHeader(net::HttpRequestHeaders::kContentType, kContentType);
-  headers.SetHeader(kContentEncoding, kGzip);
+void NetworkFetch::SetRequestHeaders(network::ResourceRequest* request) const {
+  request->headers.SetHeader(net::HttpRequestHeaders::kContentType,
+                             kContentType);
+  request->headers.SetHeader(kContentEncoding, kGzip);
 
-  bool is_authorized = !auth_header.empty();
-  if (is_authorized)
-    headers.SetHeader(net::HttpRequestHeaders::kAuthorization, auth_header);
+  variations::SignedIn signed_in_status = variations::SignedIn::kNo;
+  if (!access_token_.empty()) {
+    std::string auth_header = base::StringPrintf(
+        kAuthorizationRequestHeaderFormat, access_token_.c_str());
+    request->headers.SetHeader(net::HttpRequestHeaders::kAuthorization,
+                               auth_header);
+    signed_in_status = variations::SignedIn::kYes;
+  }
 
-  variations::SignedIn signed_in_status =
-      is_authorized ? variations::SignedIn::kYes : variations::SignedIn::kNo;
   // Add X-Client-Data header with experiment IDs from field trials.
-  variations::AppendVariationHeaders(url_, variations::InIncognito::kNo,
-                                     signed_in_status, &headers);
-  return headers;
+  variations::AppendVariationsHeader(url_, variations::InIncognito::kNo,
+                                     signed_in_status, request);
 }
 
 void NetworkFetch::PopulateRequestBody(network::SimpleURLLoader* loader) {
diff --git a/components/feedback/feedback_data.cc b/components/feedback/feedback_data.cc
index cbdc281..238c25b 100644
--- a/components/feedback/feedback_data.cc
+++ b/components/feedback/feedback_data.cc
@@ -34,7 +34,9 @@
       context_(nullptr),
       trace_id_(0),
       pending_op_count_(1),
-      report_sent_(false) {
+      report_sent_(false),
+      from_assistant_(false),
+      assistant_debug_info_allowed_(false) {
   CHECK(uploader_);
 }
 
diff --git a/components/feedback/feedback_data.h b/components/feedback/feedback_data.h
index ab3085e..4dda76a 100644
--- a/components/feedback/feedback_data.h
+++ b/components/feedback/feedback_data.h
@@ -50,6 +50,10 @@
   content::BrowserContext* context() const { return context_; }
   const std::string& attached_file_uuid() const { return attached_file_uuid_; }
   const std::string& screenshot_uuid() const { return screenshot_uuid_; }
+  bool from_assistant() const { return from_assistant_; }
+  bool assistant_debug_info_allowed() const {
+    return assistant_debug_info_allowed_;
+  }
 
   // Setters
   void set_context(content::BrowserContext* context) { context_ = context; }
@@ -63,6 +67,12 @@
     screenshot_uuid_ = uuid;
   }
   void set_trace_id(int trace_id) { trace_id_ = trace_id; }
+  void set_from_assistant(bool from_assistant) {
+    from_assistant_ = from_assistant;
+  }
+  void set_assistant_debug_info_allowed(bool assistant_debug_info_allowed) {
+    assistant_debug_info_allowed_ = assistant_debug_info_allowed;
+  }
 
  private:
   ~FeedbackData() override;
@@ -85,6 +95,8 @@
 
   int pending_op_count_;
   bool report_sent_;
+  bool from_assistant_;
+  bool assistant_debug_info_allowed_;
 
   DISALLOW_COPY_AND_ASSIGN(FeedbackData);
 };
diff --git a/components/feedback/feedback_uploader.cc b/components/feedback/feedback_uploader.cc
index b6d47d4..90a5b3fd 100644
--- a/components/feedback/feedback_uploader.cc
+++ b/components/feedback/feedback_uploader.cc
@@ -169,11 +169,11 @@
   resource_request->method = "POST";
 
   // Tell feedback server about the variation state of this install.
-  variations::AppendVariationHeadersUnknownSignedIn(
+  variations::AppendVariationsHeaderUnknownSignedIn(
       feedback_post_url_,
       context_->IsOffTheRecord() ? variations::InIncognito::kYes
                                  : variations::InIncognito::kNo,
-      &resource_request->headers);
+      resource_request.get());
 
   AppendExtraHeadersToUploadRequest(resource_request.get());
 
diff --git a/components/feedback/feedback_uploader_dispatch_unittest.cc b/components/feedback/feedback_uploader_dispatch_unittest.cc
index 7e5186d..00977f6 100644
--- a/components/feedback/feedback_uploader_dispatch_unittest.cc
+++ b/components/feedback/feedback_uploader_dispatch_unittest.cc
@@ -13,6 +13,7 @@
 #include "base/task/task_traits.h"
 #include "base/test/bind_test_util.h"
 #include "components/feedback/feedback_uploader_factory.h"
+#include "components/variations/net/variations_http_headers.h"
 #include "components/variations/variations_associated_data.h"
 #include "components/variations/variations_http_header_provider.h"
 #include "content/public/test/test_browser_context.h"
@@ -97,16 +98,12 @@
   net::HttpRequestHeaders headers;
   test_url_loader_factory()->SetInterceptor(
       base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
-        headers = request.headers;
+        EXPECT_TRUE(variations::HasVariationsHeader(request));
       }));
 
   QueueReport(&uploader, "test");
   base::RunLoop().RunUntilIdle();
 
-  std::string value;
-  EXPECT_TRUE(headers.GetHeader("X-Client-Data", &value));
-  EXPECT_FALSE(value.empty());
-
   variations::VariationsHttpHeaderProvider::GetInstance()->ResetForTesting();
 }
 
diff --git a/components/flags_ui/resources/flags.css b/components/flags_ui/resources/flags.css
index 5ccff67..18bcec2 100644
--- a/components/flags_ui/resources/flags.css
+++ b/components/flags_ui/resources/flags.css
@@ -90,12 +90,12 @@
 
 #header .flex-container .flex:first-child,
 [dir='rtl'] #header .flex-container .flex:last-child {
-  text-align: left;
+  text-align: left; /* csschecker-disable-line left-right */
 }
 
 #header .flex-container .flex:last-child,
 [dir='rtl'] #header .flex-container .flex:first-child {
-  text-align: right;
+  text-align: right; /* csschecker-disable-line left-right */
 }
 
 .hidden {
@@ -207,7 +207,7 @@
 }
 
 [dir='rtl'] .experiment .flex-container .flex:first-child  {
-  text-align: right;
+  text-align: right; /* csschecker-disable-line left-right */
 }
 
 .experiment p {
@@ -245,7 +245,7 @@
 .experiment-actions {
   flex: 0 0 auto;
   padding-inline-start: 5px;
-  text-align: right;
+  text-align: right; /* csschecker-disable-line left-right */
   width: 150px;
 }
 
@@ -292,7 +292,7 @@
   margin: 0;
   padding: 0;
   position: relative;
-  text-align: left;
+  text-align: left; /* csschecker-disable-line left-right */
   width: 100%;
 }
 
@@ -349,7 +349,8 @@
   margin-inline-start: -100%;
 }
 
-.selected .tab-content {
+.selected .tab-content,
+.selected .tab-content .template {
   display: block;
 }
 
@@ -371,11 +372,11 @@
 }
 
 #needs-restart .flex:last-child {
-  text-align: right;
+  text-align: right; /* csschecker-disable-line left-right */
 }
 
 [dir='rtl'] #needs-restart .flex:last-child {
-  text-align: left;
+  text-align: left; /* csschecker-disable-line left-right */
 }
 
 #needs-restart.show {
@@ -414,11 +415,11 @@
 
 #version {
   color: var(--color-light-gray);
-  text-align: right;
+  text-align: right; /* csschecker-disable-line left-right */
 }
 
 [dir='rtl'] #version {
-  text-align: left;
+  text-align: left; /* csschecker-disable-line left-right */
 }
 
 @media (max-width: 360px) {
@@ -468,7 +469,7 @@
   .experiment .experiment-actions {
     max-width: 100%;
     padding-top: 12px;
-    text-align: left;
+    text-align: left; /* csschecker-disable-line left-right */
     width: 100%;
   }
 
@@ -478,11 +479,11 @@
 
   #flagsTemplate > .flex-container:first-child:not('.version') {
     flex-direction: column;
-    text-align: left;
+    text-align: left; /* csschecker-disable-line left-right */
   }
 
   [dir='rtl'] #flagsTemplate > .flex-container:first-child {
-    text-align: right;
+    text-align: right; /* csschecker-disable-line left-right */
   }
 
   #flagsTemplate > .flex-container:first-child .flex {
@@ -490,7 +491,7 @@
   }
 
   [dir='rtl'] #flagsTemplate > .flex-container #version {
-    text-align: right;
+    text-align: right; /* csschecker-disable-line left-right */
   }
 
   #needs-restart {
diff --git a/components/flags_ui/resources/flags.html b/components/flags_ui/resources/flags.html
index 91e4ea7..1ce6c97 100644
--- a/components/flags_ui/resources/flags.html
+++ b/components/flags_ui/resources/flags.html
@@ -76,6 +76,8 @@
             tabindex="4">Available</a>
         <div id="tab-content-available" class="tab-content"
             role="tabpanel" aria-labelledby="tab-available" aria-hidden="false">
+        </div>
+        <div id="tab-content-available-template" class="template hidden">
           <!-- Non default experiments. -->
           <div jsselect="supportedFeatures"
               jsvalues="id:internal_name; class: is_default ? 'hidden' : 'experiment'"
diff --git a/components/flags_ui/resources/flags.js b/components/flags_ui/resources/flags.js
index dea68e8..adbd575 100644
--- a/components/flags_ui/resources/flags.js
+++ b/components/flags_ui/resources/flags.js
@@ -15,8 +15,29 @@
  *     See returnFlagsExperiments() for the structure of this object.
  */
 function renderTemplate(experimentalFeaturesData) {
-  // This is the javascript code that processes the template:
-  jstProcess(new JsEvalContext(experimentalFeaturesData), $('flagsTemplate'));
+  var templateToProcess = jstGetTemplate('tab-content-available-template');
+  var content = $('tab-content-available');
+
+  if (content.childNodes > 0) {
+    // Already processed, use the internal content area template.
+    templateToProcess =  content;
+  } else {
+    // Duplicate the template into the content area.
+    // This prevents the misrendering of available flags when the template
+    // is rerendered. Example - resetting flags.
+    content.textContent = '';
+    content.appendChild(templateToProcess);
+  }
+
+  // Process the templates: available / unavailable flags.
+  jstProcess(new JsEvalContext(experimentalFeaturesData), templateToProcess);
+
+  // Unavailable flags are not shown on iOS.
+  var unavailableTemplate = $('tab-content-unavailable');
+  if (unavailableTemplate) {
+    jstProcess(new JsEvalContext(experimentalFeaturesData),
+        $('tab-content-unavailable'));
+  }
 
   // Add handlers to dynamically created HTML elements.
   var elements = document.getElementsByClassName('experiment-select');
diff --git a/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc b/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
index e2a1da8..72c97f5 100644
--- a/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
+++ b/components/gwp_asan/common/pack_stack_trace_differential_fuzzer.cc
@@ -24,23 +24,24 @@
 
   // We don't need a buffer large than Size*10 as the longest variable length
   // encoding of a 64-bit integer is 10 bytes long.)
-  size_t array_size = std::min(Size * 10, packed_max_size);
-  std::unique_ptr<uint8_t[]> packed(new uint8_t[array_size]);
-  size_t packed_size =
+  size_t packed_array_size = std::min(Size * 10, packed_max_size);
+  std::unique_ptr<uint8_t[]> packed(new uint8_t[packed_array_size]);
+  size_t packed_out =
       gwp_asan::internal::Pack(reinterpret_cast<const uintptr_t*>(Data),
                                entries, packed.get(), packed_max_size);
-  if (packed_size > array_size)
+  if (packed_out > packed_array_size)
     __builtin_trap();
 
-  uintptr_t unpacked[std::min(unpacked_max_size, entries)];
-  size_t unpacked_size = gwp_asan::internal::Unpack(
-      packed.get(), packed_size, unpacked, unpacked_max_size);
+  size_t unpacked_array_size = std::min(unpacked_max_size, entries);
+  std::unique_ptr<uintptr_t[]> unpacked(new uintptr_t[unpacked_array_size]);
+  size_t unpacked_out = gwp_asan::internal::Unpack(
+      packed.get(), packed_out, unpacked.get(), unpacked_max_size);
   // We can only be sure there was enough room to pack the entire input when
-  // packed_max_size was larger than Size*10.
-  if (packed_max_size > array_size &&
-      unpacked_size != std::min(entries, unpacked_max_size))
+  // packed_max_size was larger than the space allocated for packed data.
+  if (packed_max_size > packed_array_size &&
+      unpacked_out != unpacked_array_size)
     __builtin_trap();
-  if (memcmp(Data, unpacked, unpacked_size * sizeof(uintptr_t)))
+  if (memcmp(Data, unpacked.get(), unpacked_out * sizeof(uintptr_t)))
     __builtin_trap();
   return 0;
 }
diff --git a/components/gwp_asan/common/pack_stack_trace_unpack_fuzzer.cc b/components/gwp_asan/common/pack_stack_trace_unpack_fuzzer.cc
index c7f807f..1f24fad 100644
--- a/components/gwp_asan/common/pack_stack_trace_unpack_fuzzer.cc
+++ b/components/gwp_asan/common/pack_stack_trace_unpack_fuzzer.cc
@@ -5,6 +5,7 @@
 #include "components/gwp_asan/common/pack_stack_trace.h"
 
 #include <algorithm>
+#include <memory>
 
 // Tests that Unpack() correctly handles arbitrary inputs.
 
@@ -19,7 +20,8 @@
   Size -= sizeof(size_t);
 
   // This should always be large enough to hold the output.
-  uintptr_t unpacked[std::min(Size, unpacked_max_size)];
-  gwp_asan::internal::Unpack(Data, Size, unpacked, unpacked_max_size);
+  size_t unpacked_array_size = std::min(Size, unpacked_max_size);
+  std::unique_ptr<uintptr_t[]> unpacked(new uintptr_t[unpacked_array_size]);
+  gwp_asan::internal::Unpack(Data, Size, unpacked.get(), unpacked_max_size);
   return 0;
 }
diff --git a/components/heap_profiling/supervisor.cc b/components/heap_profiling/supervisor.cc
index 987902b3..54d2ef0 100644
--- a/components/heap_profiling/supervisor.cc
+++ b/components/heap_profiling/supervisor.cc
@@ -60,10 +60,9 @@
 
 void Supervisor::Start(content::ServiceManagerConnection* connection,
                        base::OnceClosure closure) {
-  // TODO(alph): Obtain stream_samples from the command line / Finch.
+  bool stream_samples = !IsInProcessModeEnabled();
   Start(connection, GetModeForStartup(), GetStackModeForStartup(),
-        true /* stream_samples */, GetSamplingRateForStartup(),
-        std::move(closure));
+        stream_samples, GetSamplingRateForStartup(), std::move(closure));
 }
 
 void Supervisor::Start(content::ServiceManagerConnection* connection,
diff --git a/components/journey/journey_info_json_request.cc b/components/journey/journey_info_json_request.cc
index 50ef874..5ec8f85 100644
--- a/components/journey/journey_info_json_request.cc
+++ b/components/journey/journey_info_json_request.cc
@@ -145,8 +145,6 @@
   if (!auth_header_.empty()) {
     headers.SetHeader("Authorization", auth_header_);
   }
-  variations::AppendVariationHeaders(url_, variations::InIncognito::kNo,
-                                     variations::SignedIn::kNo, &headers);
   return headers;
 }
 
@@ -161,6 +159,9 @@
   resource_request->load_flags =
       net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES;
   resource_request->headers = BuildSimpleURLLoaderHeaders();
+  variations::AppendVariationsHeader(url_, variations::InIncognito::kNo,
+                                     variations::SignedIn::kNo,
+                                     resource_request.get());
   resource_request->method = "POST";
 
   auto simple_loader = network::SimpleURLLoader::Create(
diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn
index 70e8b1b..f593d4ec 100644
--- a/components/metrics/BUILD.gn
+++ b/components/metrics/BUILD.gn
@@ -118,6 +118,7 @@
     "//crypto",
     "//extensions/buildflags",
     "//third_party/zlib/google:compression_utils",
+    "//url",
   ]
 
   if (is_chromeos) {
diff --git a/components/metrics/DEPS b/components/metrics/DEPS
index 0d4fca0..e12ab69f 100644
--- a/components/metrics/DEPS
+++ b/components/metrics/DEPS
@@ -17,4 +17,5 @@
   "+third_party/metrics_proto",
   "+third_party/zlib/google",
   "-net",
+  "+url"
 ]
diff --git a/components/metrics/metrics_reporting_service.cc b/components/metrics/metrics_reporting_service.cc
index 56d763f..2df0013 100644
--- a/components/metrics/metrics_reporting_service.cc
+++ b/components/metrics/metrics_reporting_service.cc
@@ -46,11 +46,11 @@
   return &metrics_log_store_;
 }
 
-std::string MetricsReportingService::GetUploadUrl() const {
+GURL MetricsReportingService::GetUploadUrl() const {
   return client()->GetMetricsServerUrl();
 }
 
-std::string MetricsReportingService::GetInsecureUploadUrl() const {
+GURL MetricsReportingService::GetInsecureUploadUrl() const {
   return client()->GetInsecureMetricsServerUrl();
 }
 
diff --git a/components/metrics/metrics_reporting_service.h b/components/metrics/metrics_reporting_service.h
index 38961b27..3304b045 100644
--- a/components/metrics/metrics_reporting_service.h
+++ b/components/metrics/metrics_reporting_service.h
@@ -46,8 +46,8 @@
  private:
   // ReportingService:
   LogStore* log_store() override;
-  std::string GetUploadUrl() const override;
-  std::string GetInsecureUploadUrl() const override;
+  GURL GetUploadUrl() const override;
+  GURL GetInsecureUploadUrl() const override;
   base::StringPiece upload_mime_type() const override;
   MetricsLogUploader::MetricServiceType service_type() const override;
   void LogActualUploadInterval(base::TimeDelta interval) override;
diff --git a/components/metrics/metrics_service_client.cc b/components/metrics/metrics_service_client.cc
index 9719e204..a4e7bc0 100644
--- a/components/metrics/metrics_service_client.cc
+++ b/components/metrics/metrics_service_client.cc
@@ -31,12 +31,12 @@
   return false;
 }
 
-std::string MetricsServiceClient::GetMetricsServerUrl() {
-  return kNewMetricsServerUrl;
+GURL MetricsServiceClient::GetMetricsServerUrl() {
+  return GURL(kNewMetricsServerUrl);
 }
 
-std::string MetricsServiceClient::GetInsecureMetricsServerUrl() {
-  return kNewMetricsServerUrlInsecure;
+GURL MetricsServiceClient::GetInsecureMetricsServerUrl() {
+  return GURL(kNewMetricsServerUrlInsecure);
 }
 
 bool MetricsServiceClient::SyncStateAllowsUkm() {
diff --git a/components/metrics/metrics_service_client.h b/components/metrics/metrics_service_client.h
index 621d2d5..91e11c7 100644
--- a/components/metrics/metrics_service_client.h
+++ b/components/metrics/metrics_service_client.h
@@ -16,6 +16,7 @@
 #include "components/metrics/metrics_log_uploader.h"
 #include "components/metrics/metrics_reporting_default_state.h"
 #include "third_party/metrics_proto/system_profile.pb.h"
+#include "url/gurl.h"
 
 namespace base {
 class FilePath;
@@ -84,16 +85,16 @@
       const base::Closure& done_callback) = 0;
 
   // Get the URL of the metrics server.
-  virtual std::string GetMetricsServerUrl();
+  virtual GURL GetMetricsServerUrl();
 
   // Get the fallback HTTP URL of the metrics server.
-  virtual std::string GetInsecureMetricsServerUrl();
+  virtual GURL GetInsecureMetricsServerUrl();
 
   // Creates a MetricsLogUploader with the specified parameters (see comments on
   // MetricsLogUploader for details).
   virtual std::unique_ptr<MetricsLogUploader> CreateUploader(
-      base::StringPiece server_url,
-      base::StringPiece insecure_server_url,
+      const GURL& server_url,
+      const GURL& insecure_server_url,
       base::StringPiece mime_type,
       metrics::MetricsLogUploader::MetricServiceType service_type,
       const MetricsLogUploader::UploadCallback& on_upload_complete) = 0;
diff --git a/components/metrics/net/net_metrics_log_uploader.cc b/components/metrics/net/net_metrics_log_uploader.cc
index e1b3892..9f208fe 100644
--- a/components/metrics/net/net_metrics_log_uploader.cc
+++ b/components/metrics/net/net_metrics_log_uploader.cc
@@ -162,7 +162,7 @@
 
 NetMetricsLogUploader::NetMetricsLogUploader(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    base::StringPiece server_url,
+    const GURL& server_url,
     base::StringPiece mime_type,
     MetricsLogUploader::MetricServiceType service_type,
     const MetricsLogUploader::UploadCallback& on_upload_complete)
@@ -174,8 +174,8 @@
 
 NetMetricsLogUploader::NetMetricsLogUploader(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-    base::StringPiece server_url,
-    base::StringPiece insecure_server_url,
+    const GURL& server_url,
+    const GURL& insecure_server_url,
     base::StringPiece mime_type,
     MetricsLogUploader::MetricServiceType service_type,
     const MetricsLogUploader::UploadCallback& on_upload_complete)
diff --git a/components/metrics/net/net_metrics_log_uploader.h b/components/metrics/net/net_metrics_log_uploader.h
index c3d889f4..c7439d7 100644
--- a/components/metrics/net/net_metrics_log_uploader.h
+++ b/components/metrics/net/net_metrics_log_uploader.h
@@ -30,7 +30,7 @@
   // be called with the HTTP response code of the upload or with -1 on an error.
   NetMetricsLogUploader(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      base::StringPiece server_url,
+      const GURL& server_url,
       base::StringPiece mime_type,
       MetricsLogUploader::MetricServiceType service_type,
       const MetricsLogUploader::UploadCallback& on_upload_complete);
@@ -40,8 +40,8 @@
   // to |server_url| fails, requests are encrypted when sent to an HTTP URL.
   NetMetricsLogUploader(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
-      base::StringPiece server_url,
-      base::StringPiece insecure_server_url,
+      const GURL& server_url,
+      const GURL& insecure_server_url,
       base::StringPiece mime_type,
       MetricsLogUploader::MetricServiceType service_type,
       const MetricsLogUploader::UploadCallback& on_upload_complete);
diff --git a/components/metrics/net/net_metrics_log_uploader_unittest.cc b/components/metrics/net/net_metrics_log_uploader_unittest.cc
index bf4f6a3..484f490 100644
--- a/components/metrics/net/net_metrics_log_uploader_unittest.cc
+++ b/components/metrics/net/net_metrics_log_uploader_unittest.cc
@@ -41,8 +41,8 @@
     ReportingInfo reporting_info;
     reporting_info.set_attempt_count(10);
     uploader_.reset(new NetMetricsLogUploader(
-        test_shared_url_loader_factory_, "https://dummy_server", "dummy_mime",
-        MetricsLogUploader::UMA,
+        test_shared_url_loader_factory_, GURL("https://dummy_server"),
+        "dummy_mime", MetricsLogUploader::UMA,
         base::Bind(&NetMetricsLogUploaderTest::OnUploadCompleteReuseUploader,
                    base::Unretained(this))));
     uploader_->UploadLog("initial_dummy_data", "initial_dummy_hash",
@@ -52,7 +52,7 @@
   void CreateUploaderAndUploadToSecureURL(const std::string& url) {
     ReportingInfo dummy_reporting_info;
     uploader_.reset(new NetMetricsLogUploader(
-        test_shared_url_loader_factory_, url, "dummy_mime",
+        test_shared_url_loader_factory_, GURL(url), "dummy_mime",
         MetricsLogUploader::UMA,
         base::Bind(&NetMetricsLogUploaderTest::DummyOnUploadComplete,
                    base::Unretained(this))));
@@ -63,7 +63,7 @@
   void CreateUploaderAndUploadToInsecureURL() {
     ReportingInfo dummy_reporting_info;
     uploader_.reset(new NetMetricsLogUploader(
-        test_shared_url_loader_factory_, "http://dummy_insecure_server",
+        test_shared_url_loader_factory_, GURL("http://dummy_insecure_server"),
         "dummy_mime", MetricsLogUploader::UMA,
         base::Bind(&NetMetricsLogUploaderTest::DummyOnUploadComplete,
                    base::Unretained(this))));
diff --git a/components/metrics/reporting_service.h b/components/metrics/reporting_service.h
index 6094fc3..be7c3f6 100644
--- a/components/metrics/reporting_service.h
+++ b/components/metrics/reporting_service.h
@@ -17,6 +17,7 @@
 #include "components/metrics/data_use_tracker.h"
 #include "components/metrics/metrics_log_uploader.h"
 #include "third_party/metrics_proto/reporting_info.pb.h"
+#include "url/gurl.h"
 
 class PrefService;
 class PrefRegistrySimple;
@@ -78,8 +79,8 @@
   virtual LogStore* log_store() = 0;
 
   // Getters for MetricsLogUploader parameters.
-  virtual std::string GetUploadUrl() const = 0;
-  virtual std::string GetInsecureUploadUrl() const = 0;
+  virtual GURL GetUploadUrl() const = 0;
+  virtual GURL GetInsecureUploadUrl() const = 0;
   virtual base::StringPiece upload_mime_type() const = 0;
   virtual MetricsLogUploader::MetricServiceType service_type() const = 0;
 
diff --git a/components/metrics/reporting_service_unittest.cc b/components/metrics/reporting_service_unittest.cc
index 7198ef2..31c6f88a 100644
--- a/components/metrics/reporting_service_unittest.cc
+++ b/components/metrics/reporting_service_unittest.cc
@@ -77,8 +77,8 @@
  private:
   // ReportingService:
   LogStore* log_store() override { return &log_store_; }
-  std::string GetUploadUrl() const override { return kTestUploadUrl; }
-  std::string GetInsecureUploadUrl() const override { return kTestUploadUrl; }
+  GURL GetUploadUrl() const override { return GURL(kTestUploadUrl); }
+  GURL GetInsecureUploadUrl() const override { return GURL(kTestUploadUrl); }
   base::StringPiece upload_mime_type() const override { return kTestMimeType; }
   MetricsLogUploader::MetricServiceType service_type() const override {
     return MetricsLogUploader::MetricServiceType::UMA;
diff --git a/components/metrics/test_metrics_service_client.cc b/components/metrics/test_metrics_service_client.cc
index 4859b4e..a34c5d0e 100644
--- a/components/metrics/test_metrics_service_client.cc
+++ b/components/metrics/test_metrics_service_client.cc
@@ -60,8 +60,8 @@
 }
 
 std::unique_ptr<MetricsLogUploader> TestMetricsServiceClient::CreateUploader(
-    base::StringPiece server_url,
-    base::StringPiece insecure_server_url,
+    const GURL& server_url,
+    const GURL& insecure_server_url,
     base::StringPiece mime_type,
     MetricsLogUploader::MetricServiceType service_type,
     const MetricsLogUploader::UploadCallback& on_upload_complete) {
diff --git a/components/metrics/test_metrics_service_client.h b/components/metrics/test_metrics_service_client.h
index c495536..79eadb5 100644
--- a/components/metrics/test_metrics_service_client.h
+++ b/components/metrics/test_metrics_service_client.h
@@ -35,8 +35,8 @@
   std::string GetVersionString() override;
   void CollectFinalMetricsForLog(const base::Closure& done_callback) override;
   std::unique_ptr<MetricsLogUploader> CreateUploader(
-      base::StringPiece server_url,
-      base::StringPiece insecure_server_url,
+      const GURL& server_url,
+      const GURL& insecure_server_url,
       base::StringPiece mime_type,
       MetricsLogUploader::MetricServiceType service_type,
       const MetricsLogUploader::UploadCallback& on_upload_complete) override;
diff --git a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java
index ff4879c..6fc616c3 100644
--- a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java
+++ b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploadCallable.java
@@ -24,6 +24,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.net.HttpURLConnection;
 import java.util.Locale;
 import java.util.concurrent.Callable;
@@ -50,17 +52,15 @@
     @VisibleForTesting
     protected static final String CONTENT_TYPE_TMPL = "multipart/form-data; boundary=%s";
 
-    @IntDef({
-        UPLOAD_SUCCESS,
-        UPLOAD_FAILURE,
-        UPLOAD_USER_DISABLED,
-        UPLOAD_DISABLED_BY_SAMPLING
-    })
-    public @interface MinidumpUploadStatus {}
-    public static final int UPLOAD_SUCCESS = 0;
-    public static final int UPLOAD_FAILURE = 1;
-    public static final int UPLOAD_USER_DISABLED = 2;
-    public static final int UPLOAD_DISABLED_BY_SAMPLING = 3;
+    @IntDef({MinidumpUploadStatus.SUCCESS, MinidumpUploadStatus.FAILURE,
+            MinidumpUploadStatus.USER_DISABLED, MinidumpUploadStatus.DISABLED_BY_SAMPLING})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MinidumpUploadStatus {
+        int SUCCESS = 0;
+        int FAILURE = 1;
+        int USER_DISABLED = 2;
+        int DISABLED_BY_SAMPLING = 3;
+    }
 
     private final File mFileToUpload;
     private final File mLogfile;
@@ -83,7 +83,7 @@
     }
 
     @Override
-    public Integer call() {
+    public @MinidumpUploadStatus Integer call() {
         if (mPermManager.isUploadEnabledForTests()) {
             Log.i(TAG, "Minidump upload enabled for tests, skipping other checks.");
         } else if (!CrashFileManager.isForcedUpload(mFileToUpload)) {
@@ -91,44 +91,44 @@
                 Log.i(TAG, "Minidump upload is not permitted by user. Marking file as skipped for "
                                 + "cleanup to prevent future uploads.");
                 CrashFileManager.markUploadSkipped(mFileToUpload);
-                return UPLOAD_USER_DISABLED;
+                return MinidumpUploadStatus.USER_DISABLED;
             }
 
             if (!mPermManager.isClientInMetricsSample()) {
                 Log.i(TAG, "Minidump upload skipped due to sampling.  Marking file as skipped for "
                                 + "cleanup to prevent future uploads.");
                 CrashFileManager.markUploadSkipped(mFileToUpload);
-                return UPLOAD_DISABLED_BY_SAMPLING;
+                return MinidumpUploadStatus.DISABLED_BY_SAMPLING;
             }
 
             if (!mPermManager.isNetworkAvailableForCrashUploads()) {
                 Log.i(TAG, "Minidump cannot currently be uploaded due to network constraints.");
-                return UPLOAD_FAILURE;
+                return MinidumpUploadStatus.FAILURE;
             }
         }
 
         HttpURLConnection connection =
                 mHttpURLConnectionFactory.createHttpURLConnection(CRASH_URL_STRING);
         if (connection == null) {
-            return UPLOAD_FAILURE;
+            return MinidumpUploadStatus.FAILURE;
         }
 
         FileInputStream minidumpInputStream = null;
         try {
             if (!configureConnectionForHttpPost(connection)) {
-                return UPLOAD_FAILURE;
+                return MinidumpUploadStatus.FAILURE;
             }
             minidumpInputStream = new FileInputStream(mFileToUpload);
             streamCopy(minidumpInputStream, new GZIPOutputStream(connection.getOutputStream()));
             boolean success = handleExecutionResponse(connection);
 
-            return success ? UPLOAD_SUCCESS : UPLOAD_FAILURE;
+            return success ? MinidumpUploadStatus.SUCCESS : MinidumpUploadStatus.FAILURE;
         } catch (IOException | ArrayIndexOutOfBoundsException e) {
             // ArrayIndexOutOfBoundsException due to bad GZIPOutputStream implementation on some
             // old sony devices.
             // For now just log the stack trace.
             Log.w(TAG, "Error while uploading " + mFileToUpload.getName(), e);
-            return UPLOAD_FAILURE;
+            return MinidumpUploadStatus.FAILURE;
         } finally {
             connection.disconnect();
 
diff --git a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploaderImpl.java b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploaderImpl.java
index 7275b671..bf228d11 100644
--- a/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploaderImpl.java
+++ b/components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/MinidumpUploaderImpl.java
@@ -7,6 +7,7 @@
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.VisibleForTesting;
+import org.chromium.components.minidump_uploader.MinidumpUploadCallable.MinidumpUploadStatus;
 
 import java.io.File;
 
@@ -103,12 +104,13 @@
                 Log.i(TAG, "Attempting to upload " + minidump.getName());
                 MinidumpUploadCallable uploadCallable =
                         createMinidumpUploadCallable(minidump, fileManager.getCrashUploadLogFile());
+                @MinidumpUploadStatus
                 int uploadResult = uploadCallable.call();
 
                 // Record metrics about the upload.
-                if (uploadResult == MinidumpUploadCallable.UPLOAD_SUCCESS) {
+                if (uploadResult == MinidumpUploadStatus.SUCCESS) {
                     mDelegate.recordUploadSuccess(minidump);
-                } else if (uploadResult == MinidumpUploadCallable.UPLOAD_FAILURE) {
+                } else if (uploadResult == MinidumpUploadStatus.FAILURE) {
                     // Only record a failure after we have maxed out the allotted tries.
                     // Note: Add 1 to include the most recent failure, since the minidump's filename
                     // is from before the failure.
@@ -135,7 +137,7 @@
                 // incremented, even if the upload failed. This is because a common reason for
                 // cancelation is loss of network connectivity, which does result in a failure, but
                 // it's a transient failure rather than something non-recoverable.
-                if (uploadResult == MinidumpUploadCallable.UPLOAD_FAILURE) {
+                if (uploadResult == MinidumpUploadStatus.FAILURE) {
                     String newName = CrashFileManager.tryIncrementAttemptNumber(minidump);
                     if (newName == null) {
                         Log.w(TAG, "Failed to increment attempt number of " + minidump);
diff --git a/components/minidump_uploader/android/javatests/src/org/chromium/components/minidump_uploader/MinidumpUploadCallableTest.java b/components/minidump_uploader/android/javatests/src/org/chromium/components/minidump_uploader/MinidumpUploadCallableTest.java
index f17c05b..4ed9c0a8 100644
--- a/components/minidump_uploader/android/javatests/src/org/chromium/components/minidump_uploader/MinidumpUploadCallableTest.java
+++ b/components/minidump_uploader/android/javatests/src/org/chromium/components/minidump_uploader/MinidumpUploadCallableTest.java
@@ -16,6 +16,7 @@
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
 import org.chromium.components.minidump_uploader.CrashTestRule.MockCrashReportingPermissionManager;
+import org.chromium.components.minidump_uploader.MinidumpUploadCallable.MinidumpUploadStatus;
 import org.chromium.components.minidump_uploader.util.CrashReportingPermissionManager;
 import org.chromium.components.minidump_uploader.util.HttpURLConnectionFactory;
 
@@ -217,8 +218,7 @@
 
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-        Assert.assertEquals(
-                MinidumpUploadCallable.UPLOAD_SUCCESS, minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(MinidumpUploadStatus.SUCCESS, minidumpUploadCallable.call().intValue());
         Assert.assertTrue(mExpectedFileAfterUpload.exists());
         assertValidUploadLogEntry();
     }
@@ -241,8 +241,8 @@
 
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-        Assert.assertEquals(MinidumpUploadCallable.UPLOAD_USER_DISABLED,
-                minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(
+                MinidumpUploadStatus.USER_DISABLED, minidumpUploadCallable.call().intValue());
 
         File expectedSkippedFileAfterUpload = new File(
                 mTestRule.getCrashDir(), mTestUpload.getName().replace(".dmp", ".skipped"));
@@ -268,7 +268,7 @@
 
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-        Assert.assertEquals(MinidumpUploadCallable.UPLOAD_DISABLED_BY_SAMPLING,
+        Assert.assertEquals(MinidumpUploadStatus.DISABLED_BY_SAMPLING,
                 minidumpUploadCallable.call().intValue());
 
         File expectedSkippedFileAfterUpload = new File(
@@ -295,8 +295,7 @@
 
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-        Assert.assertEquals(
-                MinidumpUploadCallable.UPLOAD_FAILURE, minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(MinidumpUploadStatus.FAILURE, minidumpUploadCallable.call().intValue());
         Assert.assertFalse(mExpectedFileAfterUpload.exists());
     }
 
@@ -318,8 +317,7 @@
 
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-        Assert.assertEquals(
-                MinidumpUploadCallable.UPLOAD_SUCCESS, minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(MinidumpUploadStatus.SUCCESS, minidumpUploadCallable.call().intValue());
         Assert.assertTrue(mExpectedFileAfterUpload.exists());
         assertValidUploadLogEntry();
     }
@@ -343,8 +341,7 @@
 
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-        Assert.assertEquals(
-                MinidumpUploadCallable.UPLOAD_SUCCESS, minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(MinidumpUploadStatus.SUCCESS, minidumpUploadCallable.call().intValue());
         Assert.assertTrue(mExpectedFileAfterUpload.exists());
         assertValidUploadLogEntry();
     }
@@ -368,8 +365,7 @@
 
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-        Assert.assertEquals(
-                MinidumpUploadCallable.UPLOAD_SUCCESS, minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(MinidumpUploadStatus.SUCCESS, minidumpUploadCallable.call().intValue());
 
         File expectedSkippedFileAfterUpload = new File(
                 mTestRule.getCrashDir(), mTestUpload.getName().replace(".forced", ".skipped"));
@@ -396,8 +392,7 @@
 
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-        Assert.assertEquals(
-                MinidumpUploadCallable.UPLOAD_SUCCESS, minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(MinidumpUploadStatus.SUCCESS, minidumpUploadCallable.call().intValue());
 
         File expectedSkippedFileAfterUpload = new File(
                 mTestRule.getCrashDir(), mTestUpload.getName().replace(".forced", ".skipped"));
@@ -424,8 +419,7 @@
 
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-        Assert.assertEquals(
-                MinidumpUploadCallable.UPLOAD_SUCCESS, minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(MinidumpUploadStatus.SUCCESS, minidumpUploadCallable.call().intValue());
 
         File expectedSkippedFileAfterUpload = new File(
                 mTestRule.getCrashDir(), mTestUpload.getName().replace(".forced", ".skipped"));
@@ -451,8 +445,7 @@
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
 
-        Assert.assertEquals(
-                MinidumpUploadCallable.UPLOAD_FAILURE, minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(MinidumpUploadStatus.FAILURE, minidumpUploadCallable.call().intValue());
         Assert.assertFalse(mExpectedFileAfterUpload.exists());
     }
 
@@ -477,8 +470,7 @@
         MinidumpUploadCallable minidumpUploadCallable =
                 new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
 
-        Assert.assertEquals(
-                MinidumpUploadCallable.UPLOAD_SUCCESS, minidumpUploadCallable.call().intValue());
+        Assert.assertEquals(MinidumpUploadStatus.SUCCESS, minidumpUploadCallable.call().intValue());
         Assert.assertTrue(mExpectedFileAfterUpload.exists());
         assertValidUploadLogEntry();
     }
@@ -504,8 +496,8 @@
                     new ErrorCodeHttpUrlConnectionFactory(errorCodes[n]);
             MinidumpUploadCallable minidumpUploadCallable =
                     new MockMinidumpUploadCallable(httpURLConnectionFactory, testPermManager);
-            Assert.assertEquals(MinidumpUploadCallable.UPLOAD_FAILURE,
-                    minidumpUploadCallable.call().intValue());
+            Assert.assertEquals(
+                    MinidumpUploadStatus.FAILURE, minidumpUploadCallable.call().intValue());
             // Note that mTestUpload is not renamed on failure - so we can try to upload that file
             // several times during the same test.
         }
diff --git a/components/nacl/renderer/json_manifest.cc b/components/nacl/renderer/json_manifest.cc
index 6bceb54..5fccd15 100644
--- a/components/nacl/renderer/json_manifest.cc
+++ b/components/nacl/renderer/json_manifest.cc
@@ -413,10 +413,9 @@
                         ErrorInfo* error_info) {
   CHECK(error_info);
 
-  base::JSONReader json_reader;
   int json_read_error_code;
   std::string json_read_error_msg;
-  std::unique_ptr<base::Value> json_data(json_reader.ReadAndReturnError(
+  std::unique_ptr<base::Value> json_data(base::JSONReader::ReadAndReturnError(
       manifest_json_data, base::JSON_PARSE_RFC, &json_read_error_code,
       &json_read_error_msg));
   if (!json_data) {
diff --git a/components/ntp_snippets/BUILD.gn b/components/ntp_snippets/BUILD.gn
index c24bf47..2ec8b4f 100644
--- a/components/ntp_snippets/BUILD.gn
+++ b/components/ntp_snippets/BUILD.gn
@@ -266,6 +266,7 @@
     "//components/sync_preferences:test_support",
     "//components/ukm:test_support",
     "//components/variations:test_support",
+    "//components/variations/net",
     "//components/web_resource:web_resource",
     "//google_apis/gcm",
     "//net:test_support",
diff --git a/components/ntp_snippets/breaking_news/subscription_json_request.cc b/components/ntp_snippets/breaking_news/subscription_json_request.cc
index 5f46685..8f7856f4 100644
--- a/components/ntp_snippets/breaking_news/subscription_json_request.cc
+++ b/components/ntp_snippets/breaking_news/subscription_json_request.cc
@@ -170,8 +170,8 @@
   // Add X-Client-Data header with experiment IDs from field trials.
   // TODO: We should call AppendVariationHeaders with explicit
   // variations::SignedIn::kNo If the auth_header_ is empty
-  variations::AppendVariationHeadersUnknownSignedIn(
-      url_, variations::InIncognito::kNo, &resource_request->headers);
+  variations::AppendVariationsHeaderUnknownSignedIn(
+      url_, variations::InIncognito::kNo, resource_request.get());
 
   // Log the request for debugging network issues.
   DVLOG(1) << "Building a subscription request to " << url_ << ":\n"
diff --git a/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc b/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc
index a09c87e..f910e2ce 100644
--- a/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc
+++ b/components/ntp_snippets/contextual/contextual_suggestions_fetch.cc
@@ -292,9 +292,9 @@
                         base::Base64UrlEncodePolicy::INCLUDE_PADDING,
                         &base64_encoded_body);
   headers.SetHeader("X-Protobuffer-Request-Payload", base64_encoded_body);
-  variations::AppendVariationHeaders(resource_request->url,
-                                     variations::InIncognito::kNo,
-                                     variations::SignedIn::kNo, &headers);
+  variations::AppendVariationsHeader(
+      resource_request->url, variations::InIncognito::kNo,
+      variations::SignedIn::kNo, resource_request);
 
   UMA_HISTOGRAM_COUNTS_1M(
       "ContextualSuggestions.FetchRequestProtoSizeKB",
diff --git a/components/ntp_snippets/contextual/contextual_suggestions_fetch_unittest.cc b/components/ntp_snippets/contextual/contextual_suggestions_fetch_unittest.cc
index 1a29fc7..337d1f3 100644
--- a/components/ntp_snippets/contextual/contextual_suggestions_fetch_unittest.cc
+++ b/components/ntp_snippets/contextual/contextual_suggestions_fetch_unittest.cc
@@ -12,6 +12,7 @@
 #include "base/test/test_simple_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/ntp_snippets/contextual/contextual_suggestions_features.h"
+#include "components/variations/net/variations_http_headers.h"
 #include "components/variations/variations_http_header_provider.h"
 #include "services/network/public/cpp/resource_request.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -98,7 +99,7 @@
   std::unique_ptr<network::ResourceRequest> resource_request =
       fetch.MakeResourceRequestForTesting();
 
-  EXPECT_TRUE(resource_request->headers.HasHeader("X-Client-Data"));
+  EXPECT_TRUE(variations::HasVariationsHeader(*resource_request));
 }
 
 TEST(ContextualSuggestionsFetch,
@@ -118,7 +119,7 @@
   std::unique_ptr<network::ResourceRequest> resource_request =
       fetch.MakeResourceRequestForTesting();
 
-  EXPECT_FALSE(resource_request->headers.HasHeader("X-Client-Data"));
+  EXPECT_FALSE(variations::HasVariationsHeader(*resource_request));
 }
 
 }  // namespace
diff --git a/components/ntp_snippets/remote/json_request.cc b/components/ntp_snippets/remote/json_request.cc
index ccc7aec9..f217309 100644
--- a/components/ntp_snippets/remote/json_request.cc
+++ b/components/ntp_snippets/remote/json_request.cc
@@ -289,8 +289,8 @@
   // Add X-Client-Data header with experiment IDs from field trials.
   // TODO: We should call AppendVariationHeaders with explicit
   // variations::SignedIn::kNo If the auth_header_ is empty
-  variations::AppendVariationHeadersUnknownSignedIn(
-      url_, variations::InIncognito::kNo, &resource_request->headers);
+  variations::AppendVariationsHeaderUnknownSignedIn(
+      url_, variations::InIncognito::kNo, resource_request.get());
   return resource_request;
 }
 
diff --git a/components/ntp_snippets/remote/json_request_unittest.cc b/components/ntp_snippets/remote/json_request_unittest.cc
index 5207c2d6..c22066e 100644
--- a/components/ntp_snippets/remote/json_request_unittest.cc
+++ b/components/ntp_snippets/remote/json_request_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/json/json_reader.h"
 #include "base/strings/stringprintf.h"
 #include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
@@ -66,6 +67,8 @@
             {ntp_snippets::kArticleSuggestionsFeature.name}),
         pref_service_(std::make_unique<TestingPrefServiceSimple>()),
         mock_task_runner_(new base::TestMockTimeTaskRunner()),
+        mock_runner_handle_(
+            std::make_unique<base::ThreadTaskRunnerHandle>(mock_task_runner_)),
         test_shared_loader_factory_(
             base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
                 &test_url_loader_factory_)) {
@@ -98,6 +101,7 @@
   variations::testing::VariationParamsManager params_manager_;
   std::unique_ptr<TestingPrefServiceSimple> pref_service_;
   scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_;
+  std::unique_ptr<base::ThreadTaskRunnerHandle> mock_runner_handle_;
   network::TestURLLoaderFactory test_url_loader_factory_;
   scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
 
diff --git a/components/omnibox/browser/base_search_provider.cc b/components/omnibox/browser/base_search_provider.cc
index 65ac462..0f1dbeb 100644
--- a/components/omnibox/browser/base_search_provider.cc
+++ b/components/omnibox/browser/base_search_provider.cc
@@ -104,11 +104,11 @@
         })");
   auto request = std::make_unique<network::ResourceRequest>();
   request->url = url;
-  variations::AppendVariationHeadersUnknownSignedIn(
+  variations::AppendVariationsHeaderUnknownSignedIn(
       request->url,
       client->IsOffTheRecord() ? variations::InIncognito::kYes
                                : variations::InIncognito::kNo,
-      &request->headers);
+      request.get());
   // TODO(https://crbug.com/808498) re-add data use measurement once
   // SimpleURLLoader supports it.
   // data_use_measurement::DataUseUserData::OMNIBOX
diff --git a/components/omnibox/browser/contextual_suggestions_service.cc b/components/omnibox/browser/contextual_suggestions_service.cc
index 65222cc..72fe6018a 100644
--- a/components/omnibox/browser/contextual_suggestions_service.cc
+++ b/components/omnibox/browser/contextual_suggestions_service.cc
@@ -41,8 +41,8 @@
   // Note: It's OK to pass InIncognito::kNo since we are expected to be in
   // non-incognito state here (i.e. contextual sugestions are not served in
   // incognito mode).
-  variations::AppendVariationHeadersUnknownSignedIn(
-      request->url, variations::InIncognito::kNo, &request->headers);
+  variations::AppendVariationsHeaderUnknownSignedIn(
+      request->url, variations::InIncognito::kNo, request);
 }
 
 // Returns API request body. The final result depends on the following input
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index a8f1623..a1fffa3c 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -1135,7 +1135,19 @@
   if (PopupIsOpen()) {
     // The popup is open, so the user should be able to interact with it
     // normally.
-    popup_model()->Move(count);
+
+    // If, as a result of the key press, we would select the first result, then
+    // we should revert the temporary text same as what pressing escape would
+    // have done. In the future, if the selection behavior becomes more involved
+    // (e.g. wrap around), then we should consider creating a helper method
+    // GetNewSelectionLine(int cont) to be compared with 0 here and to be reused
+    // in OmniboxPopupModel::Move.
+    if (has_temporary_text_ && count < 0 &&
+        (unsigned)-count >= popup_model()->selected_line()) {
+      RevertTemporaryText(true);
+    } else {
+      popup_model()->Move(count);
+    }
     return;
   }
 
diff --git a/components/omnibox/browser/search_provider.cc b/components/omnibox/browser/search_provider.cc
index 35a71e7..80f3be9e 100644
--- a/components/omnibox/browser/search_provider.cc
+++ b/components/omnibox/browser/search_provider.cc
@@ -956,11 +956,11 @@
   request->url = suggest_url;
   request->load_flags = net::LOAD_DO_NOT_SAVE_COOKIES;
   // Add Chrome experiment state to the request headers.
-  variations::AppendVariationHeadersUnknownSignedIn(
+  variations::AppendVariationsHeaderUnknownSignedIn(
       request->url,
       client()->IsOffTheRecord() ? variations::InIncognito::kYes
                                  : variations::InIncognito::kNo,
-      &request->headers);
+      request.get());
 
   // TODO(https://crbug.com/808498) re-add data use measurement once
   // SimpleURLLoader supports it.
diff --git a/components/omnibox/bug-triage.md b/components/omnibox/bug-triage.md
index f4d0e34..243a97af 100644
--- a/components/omnibox/bug-triage.md
+++ b/components/omnibox/bug-triage.md
@@ -175,7 +175,7 @@
 | UI>Browser>Omnibox>ZeroSuggest | Suggestions displayed on omnibox focus (both contextual and non-contextual). |
 
 If the bug is extremely low priority, set the **NextAction field** to
-**01/08/2019** and mention that we will "reassess" the bug next year.  This
+**01/07/2020** and mention that we will "reassess" the bug next year.  This
 NextAction field is for Priority-3 bugs that are somehow less important than
 other *priority-3* bugs.  These are bugs we’re sure no one on the team intends
 to fix this year (e.g., really unimportant, or mostly unimportant and hard to
diff --git a/components/page_info_strings.grdp b/components/page_info_strings.grdp
index 7648a4d..583cd115 100644
--- a/components/page_info_strings.grdp
+++ b/components/page_info_strings.grdp
@@ -187,16 +187,13 @@
       <message name="IDS_PAGE_INFO_COOKIES" desc="The label for the Cookies setting in the Page Information Window.">
         Cookies
       </message>
-      <message name="IDS_PAGE_INFO_NUM_COOKIES" desc="The label of the counts for allowed cookies that are in use on the page.">
-        {NUM_COOKIES, plural, =1 {1 in use} other {# in use}}
-      </message>
       <message name="IDS_PAGE_INFO_COOKIES_BUTTON_TEXT" desc="The label on a button in the Page Information Window that, when clicked, opens a dialog where cookies can be managed by the user.">
           Cookies <ph name="NUM_COOKIES">$1<ex>(4 in use)</ex></ph>
       </message>
       <message name="IDS_PAGE_INFO_NUM_COOKIES_PARENTHESIZED" desc="The label of the counts for allowed cookies that are in use on the page. This text will be used in IDS_PAGE_INFO_COOKIES_BUTTON_TEXT.">
         ({NUM_COOKIES, plural, =1 {1 in use} other {# in use}})
       </message>
-      <message name="IDS_PAGE_INFO_COOKIES_TOOLTIP" desc="The text of the tooltip on IDS_PAGE_INFO_NUM_COOKIES.">
+      <message name="IDS_PAGE_INFO_COOKIES_TOOLTIP" desc="The text of the tooltip on IDS_PAGE_INFO_NUM_COOKIES_PARENTHESIZED.">
         Show cookies
       </message>
     </if>
diff --git a/components/password_manager/core/browser/form_parsing/form_parser.cc b/components/password_manager/core/browser/form_parsing/form_parser.cc
index 138640ce..c413b9f 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser.cc
@@ -217,6 +217,15 @@
   }
 };
 
+// Returns true if |field| is in |significant_fields|.
+bool IsFieldInSignificantFields(const SignificantFields& significant_fields,
+                                const FormFieldData* field) {
+  return significant_fields.username == field ||
+         significant_fields.password == field ||
+         significant_fields.new_password == field ||
+         significant_fields.confirmation_password == field;
+}
+
 // Returns the first element of |fields| which has the specified
 // |unique_renderer_id|, or null if there is no such element.
 ProcessedField* FindFieldWithUniqueRendererId(
@@ -372,6 +381,10 @@
   const FormFieldData* field_marked_as_username = nullptr;
   int username_fields_found = 0;
   for (const ProcessedField& processed_field : processed_fields) {
+    if (IsFieldInSignificantFields(*result, processed_field.field)) {
+      // Skip this field because it was already chosen in previous steps.
+      continue;
+    }
     switch (processed_field.autocomplete_flag) {
       case AutocompleteFlag::kUsername:
         if (processed_field.is_password || result->username)
diff --git a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
index 56ae4f7..b83831b 100644
--- a/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
+++ b/components/password_manager/core/browser/form_parsing/form_parser_unittest.cc
@@ -1953,6 +1953,20 @@
                   }}});
 }
 
+TEST(FormParserTest, ContradictingPasswordPredictionAndAutocomplete) {
+  CheckTestData({{"Server data and autocomplete contradics each other",
+                  // On saving, server predictions for passwords are ignored.
+                  // So autocomplete attributes define the role. On filling,
+                  // both server predictions and autocomplete are considered and
+                  // server predictions have higher priority and therefore
+                  // define the role. An autofill attributes cannot override it.
+                  {{.role_filling = ElementRole::CURRENT_PASSWORD,
+                    .role_saving = ElementRole::NEW_PASSWORD,
+                    .form_control_type = "password",
+                    .prediction = {.type = autofill::PASSWORD},
+                    .autocomplete_attribute = "new-password"}}}});
+}
+
 }  // namespace
 
 }  // namespace password_manager
diff --git a/components/password_manager/core/browser/new_password_form_manager.cc b/components/password_manager/core/browser/new_password_form_manager.cc
index 6714108..ff50b4ac 100644
--- a/components/password_manager/core/browser/new_password_form_manager.cc
+++ b/components/password_manager/core/browser/new_password_form_manager.cc
@@ -328,8 +328,19 @@
   parsed_submitted_form_->password_value = new_password;
   parsed_submitted_form_->password_element.clear();
 
-  // TODO(https://crbug.com/831123): Implement processing password editing votes
-  // after implementation of |all_possible_passwords|.
+  // The user updated a password from the prompt. It means that heuristics were
+  // wrong. So clear new password, since it is likely wrong.
+  parsed_submitted_form_->new_password_value.clear();
+  parsed_submitted_form_->new_password_element.clear();
+
+  for (const ValueElementPair& pair :
+       parsed_submitted_form_->all_possible_passwords) {
+    if (pair.first == new_password) {
+      parsed_submitted_form_->password_element = pair.second;
+      break;
+    }
+  }
+
   CreatePendingCredentials();
 }
 
diff --git a/components/password_manager/core/browser/new_password_form_manager_unittest.cc b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
index c4696f3..1b65f68d 100644
--- a/components/password_manager/core/browser/new_password_form_manager_unittest.cc
+++ b/components/password_manager/core/browser/new_password_form_manager_unittest.cc
@@ -1047,7 +1047,7 @@
   EXPECT_TRUE(form_manager_->IsPasswordOverridden());
 }
 
-TEST_F(NewPasswordFormManagerTest, UpdatePasswordEmptyStore) {
+TEST_F(NewPasswordFormManagerTest, UpdatePasswordValueEmptyStore) {
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   fetcher_->SetNonFederated({}, 0u);
 
@@ -1063,9 +1063,16 @@
 
   CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
   EXPECT_TRUE(form_manager_->IsNewLogin());
+
+  // TODO(https://crbug.com/928690): implement not sending incorrect votes and
+  // check that StartUploadRequest is not called.
+  EXPECT_CALL(mock_autofill_download_manager_,
+              StartUploadRequest(_, _, _, _, _, _))
+      .Times(1);
+  form_manager_->Save();
 }
 
-TEST_F(NewPasswordFormManagerTest, UpdatePasswordToAlreadyExisting) {
+TEST_F(NewPasswordFormManagerTest, UpdatePasswordValueToAlreadyExisting) {
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   fetcher_->SetNonFederated({&saved_match_}, 0u);
 
@@ -1083,6 +1090,50 @@
   EXPECT_FALSE(form_manager_->IsPasswordOverridden());
 }
 
+TEST_F(NewPasswordFormManagerTest, UpdatePasswordValueMultiplePasswordFields) {
+  TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
+  FormData form = observed_form_only_password_fields_;
+
+  CreateFormManager(form);
+  fetcher_->SetNonFederated({}, 0u);
+  base::string16 password = ASCIIToUTF16("password1");
+  base::string16 pin = ASCIIToUTF16("pin");
+  form.fields[0].value = password;
+  form.fields[1].value = pin;
+  form_manager_->ProvisionallySave(form, &driver_);
+
+  // Check that a second password field is chosen for saving.
+  EXPECT_EQ(pin, form_manager_->GetPendingCredentials().password_value);
+
+  PasswordForm expected = form_manager_->GetPendingCredentials();
+  expected.password_value = password;
+  expected.password_element = form.fields[0].name;
+
+  // Simulate that the user updates value to save for the first password field.
+  form_manager_->UpdatePasswordValue(password);
+
+  // Check that newly created pending credentials are correct.
+  CheckPendingCredentials(expected, form_manager_->GetPendingCredentials());
+  EXPECT_TRUE(form_manager_->IsNewLogin());
+
+  // Check that a vote is sent for the field with the value which is chosen by
+  // the user.
+  std::map<base::string16, ServerFieldType> expected_types;
+  expected_types[expected.password_element] = autofill::PASSWORD;
+
+  EXPECT_CALL(mock_autofill_download_manager_,
+              StartUploadRequest(UploadedAutofillTypesAre(expected_types),
+                                 false, _, _, true, nullptr));
+
+  // Check that the password which was chosen by the user is saved.
+  MockFormSaver& form_saver = MockFormSaver::Get(form_manager_.get());
+  PasswordForm saved_form;
+  EXPECT_CALL(form_saver, Save(_, _)).WillOnce(SaveArg<0>(&saved_form));
+
+  form_manager_->Save();
+  CheckPendingCredentials(expected, saved_form);
+}
+
 TEST_F(NewPasswordFormManagerTest, PermanentlyBlacklist) {
   TestMockTimeTaskRunner::ScopedContext scoped_context(task_runner_.get());
   fetcher_->SetNonFederated({}, 0u);
diff --git a/components/password_manager/core/browser/password_form_metrics_recorder.cc b/components/password_manager/core/browser/password_form_metrics_recorder.cc
index 51910f6..3717a26 100644
--- a/components/password_manager/core/browser/password_form_metrics_recorder.cc
+++ b/components/password_manager/core/browser/password_form_metrics_recorder.cc
@@ -250,10 +250,11 @@
   }
 
   if (submit_result_ == kSubmitResultPassed && filling_assistance_) {
-    // TODO(https://crbug.com/918846): record UKM.
     FillingAssistance filling_assistance = *filling_assistance_;
     UMA_HISTOGRAM_ENUMERATION("PasswordManager.FillingAssistance",
                               filling_assistance);
+    ukm_entry_builder_.SetManagerFill_Assistance(
+        static_cast<int64_t>(filling_assistance));
 
     if (is_main_frame_secure_) {
       UMA_HISTOGRAM_ENUMERATION(
diff --git a/components/password_manager/core/browser/password_manager.cc b/components/password_manager/core/browser/password_manager.cc
index fac1a9a..4b8562f 100644
--- a/components/password_manager/core/browser/password_manager.cc
+++ b/components/password_manager/core/browser/password_manager.cc
@@ -1159,10 +1159,10 @@
   }
 
   if (ShouldPromptUserToSavePassword(*submitted_manager)) {
-    bool empty_password =
+    bool empty_username =
         submitted_manager->GetPendingCredentials().username_value.empty();
     UMA_HISTOGRAM_BOOLEAN("PasswordManager.EmptyUsernames.OfferedToSave",
-                          empty_password);
+                          empty_username);
     if (logger)
       logger->LogMessage(Logger::STRING_DECISION_ASK);
     bool update_password = submitted_manager->IsPasswordUpdate();
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 d6c41f9..09cf825b 100644
--- a/components/password_manager/core/browser/sync/password_sync_bridge.cc
+++ b/components/password_manager/core/browser/sync/password_sync_bridge.cc
@@ -135,7 +135,13 @@
     : ModelTypeSyncBridge(std::move(change_processor)),
       password_store_sync_(password_store_sync) {
   DCHECK(password_store_sync_);
-  DCHECK(password_store_sync_->GetMetadataStore());
+  // The metadata store could be null if the login database initialization
+  // fails.
+  if (!password_store_sync_->GetMetadataStore()) {
+    this->change_processor()->ReportError(
+        {FROM_HERE, "Password metadata store isn't available."});
+    return;
+  }
   std::unique_ptr<syncer::MetadataBatch> batch =
       password_store_sync_->GetMetadataStore()->GetAllSyncMetadata();
   if (!batch) {
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc
index 0f6fda0..c037288 100644
--- a/components/payments/content/payment_request_state.cc
+++ b/components/payments/content/payment_request_state.cc
@@ -250,8 +250,15 @@
 }
 
 void PaymentRequestState::CheckHasEnrolledInstrument(StatusCallback callback) {
-  // TODO(https://crbug.com/915907): Implement hasEnrolledInstrument.
-  NOTREACHED();
+  DCHECK(get_all_instruments_finished_);
+  bool has_enrolled_instrument_value = false;
+  for (const auto& instrument : available_instruments_) {
+    if (instrument->IsValidForCanMakePayment()) {
+      has_enrolled_instrument_value = true;
+      break;
+    }
+  }
+  std::move(callback).Run(has_enrolled_instrument_value);
 }
 
 void PaymentRequestState::AreRequestedMethodsSupported(
diff --git a/components/payments/content/payment_request_state_unittest.cc b/components/payments/content/payment_request_state_unittest.cc
index ffbecc82..f0d0910 100644
--- a/components/payments/content/payment_request_state_unittest.cc
+++ b/components/payments/content/payment_request_state_unittest.cc
@@ -150,6 +150,10 @@
       /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) {
         EXPECT_TRUE(can_make_payment);
       }));
+  state()->HasEnrolledInstrument(
+      base::BindOnce([](bool has_enrolled_instrument) {
+        EXPECT_TRUE(has_enrolled_instrument);
+      }));
 
   // CanMakePayment returns true because the requested method is supported.
   state()->CanMakePayment(/*legacy_mode=*/false,
@@ -174,6 +178,10 @@
       /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) {
         EXPECT_FALSE(can_make_payment);
       }));
+  state()->HasEnrolledInstrument(
+      base::BindOnce([](bool has_enrolled_instrument) {
+        EXPECT_FALSE(has_enrolled_instrument);
+      }));
 
   // CanMakePayment returns true because the requested method is supported, even
   // though the payment instrument is not ready to pay.
@@ -198,6 +206,10 @@
       /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) {
         EXPECT_FALSE(can_make_payment);
       }));
+  state()->HasEnrolledInstrument(
+      base::BindOnce([](bool has_enrolled_instrument) {
+        EXPECT_FALSE(has_enrolled_instrument);
+      }));
 
   // CanMakePayment returns true because the requested method is supported, even
   // though the payment instrument is not ready to pay.
@@ -223,6 +235,10 @@
       /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) {
         EXPECT_TRUE(can_make_payment);
       }));
+  state()->HasEnrolledInstrument(
+      base::BindOnce([](bool has_enrolled_instrument) {
+        EXPECT_TRUE(has_enrolled_instrument);
+      }));
 
   // CanMakePayment returns true because the requested method is supported.
   state()->CanMakePayment(
@@ -248,6 +264,10 @@
       /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) {
         EXPECT_TRUE(can_make_payment);
       }));
+  state()->HasEnrolledInstrument(
+      base::BindOnce([](bool has_enrolled_instrument) {
+        EXPECT_TRUE(has_enrolled_instrument);
+      }));
 
   // CanMakePayment returns true because the requested method is supported.
   state()->CanMakePayment(
@@ -274,6 +294,10 @@
       /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) {
         EXPECT_FALSE(can_make_payment);
       }));
+  state()->HasEnrolledInstrument(
+      base::BindOnce([](bool has_enrolled_instrument) {
+        EXPECT_FALSE(has_enrolled_instrument);
+      }));
 
   // CanMakePayment returns true because the requested method is supported, even
   // though there is no enrolled instrument.
@@ -300,6 +324,10 @@
       /*legacy_mode=*/true, base::BindOnce([](bool can_make_payment) {
         EXPECT_FALSE(can_make_payment);
       }));
+  state()->HasEnrolledInstrument(
+      base::BindOnce([](bool has_enrolled_instrument) {
+        EXPECT_FALSE(has_enrolled_instrument);
+      }));
 
   // CanMakePayment returns true because the requested method is supported, even
   // though there is no enrolled instrument.
diff --git a/components/payments/content/service_worker_payment_instrument.cc b/components/payments/content/service_worker_payment_instrument.cc
index 7f3fa5b4..24976c0 100644
--- a/components/payments/content/service_worker_payment_instrument.cc
+++ b/components/payments/content/service_worker_payment_instrument.cc
@@ -4,6 +4,8 @@
 
 #include "components/payments/content/service_worker_payment_instrument.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/stl_util.h"
@@ -13,6 +15,7 @@
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/payment_app_provider.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/content_features.h"
 #include "ui/gfx/image/image_skia.h"
 #include "url/origin.h"
 
@@ -36,6 +39,7 @@
       delegate_(nullptr),
       payment_request_delegate_(payment_request_delegate),
       can_make_payment_result_(false),
+      has_enrolled_instrument_result_(false),
       needs_installation_(false),
       weak_ptr_factory_(this) {
   DCHECK(browser_context_);
@@ -70,6 +74,7 @@
       delegate_(nullptr),
       payment_request_delegate_(payment_request_delegate),
       can_make_payment_result_(false),
+      has_enrolled_instrument_result_(false),
       needs_installation_(true),
       web_contents_(web_contents),
       installable_web_app_info_(std::move(installable_payment_app_info)),
@@ -107,21 +112,21 @@
     ValidateCanMakePaymentCallback callback) {
   // Returns true for payment app that needs installation.
   if (needs_installation_) {
-    OnCanMakePayment(std::move(callback), true);
+    OnCanMakePaymentEventSkipped(std::move(callback));
     return;
   }
 
   // Returns true if we are in incognito (avoiding sending the event to the
   // payment handler).
   if (payment_request_delegate_->IsIncognito()) {
-    OnCanMakePayment(std::move(callback), true);
+    OnCanMakePaymentEventSkipped(std::move(callback));
     return;
   }
 
   // Do not send CanMakePayment event to payment apps that have not been
   // explicitly verified.
   if (!stored_payment_app_info_->has_explicitly_verified_methods) {
-    OnCanMakePayment(std::move(callback), true);
+    OnCanMakePaymentEventSkipped(std::move(callback));
     return;
   }
 
@@ -131,15 +136,16 @@
     // This could only happen if this instrument only supports non-url based
     // payment methods of the payment request, then return true
     // and do not send CanMakePaymentEvent to the payment app.
-    OnCanMakePayment(std::move(callback), true);
+    OnCanMakePaymentEventSkipped(std::move(callback));
     return;
   }
 
   content::PaymentAppProvider::GetInstance()->CanMakePayment(
       browser_context_, stored_payment_app_info_->registration_id,
       std::move(event_data),
-      base::BindOnce(&ServiceWorkerPaymentInstrument::OnCanMakePayment,
-                     weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+      base::BindOnce(
+          &ServiceWorkerPaymentInstrument::OnCanMakePaymentEventResponded,
+          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 }
 
 mojom::CanMakePaymentEventDataPtr
@@ -184,13 +190,31 @@
   return event_data;
 }
 
-void ServiceWorkerPaymentInstrument::OnCanMakePayment(
+void ServiceWorkerPaymentInstrument::OnCanMakePaymentEventSkipped(
+    ValidateCanMakePaymentCallback callback) {
+  can_make_payment_result_ = true;
+  has_enrolled_instrument_result_ = false;
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE,
+      base::BindOnce(std::move(callback), this, can_make_payment_result_));
+}
+
+void ServiceWorkerPaymentInstrument::OnCanMakePaymentEventResponded(
     ValidateCanMakePaymentCallback callback,
     bool result) {
-  can_make_payment_result_ = result;
-
+  // If hasEnrolledInstrument is supported, always return true for
+  // canMakePayment for any matching payment handler.
+  if (base::FeatureList::IsEnabled(
+          ::features::kPaymentRequestHasEnrolledInstrument)) {
+    can_make_payment_result_ = true;
+    has_enrolled_instrument_result_ = result;
+  } else {
+    can_make_payment_result_ = result;
+    has_enrolled_instrument_result_ = result;
+  }
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(std::move(callback), this, result));
+      FROM_HERE,
+      base::BindOnce(std::move(callback), this, can_make_payment_result_));
 }
 
 void ServiceWorkerPaymentInstrument::InvokePaymentApp(Delegate* delegate) {
@@ -283,6 +307,10 @@
   // This instrument should not be used when can_make_payment_result_ is false,
   // so this interface should not be invoked.
   DCHECK(can_make_payment_result_);
+  if (base::FeatureList::IsEnabled(
+          ::features::kPaymentRequestHasEnrolledInstrument)) {
+    return has_enrolled_instrument_result_;
+  }
   return true;
 }
 
diff --git a/components/payments/content/service_worker_payment_instrument.h b/components/payments/content/service_worker_payment_instrument.h
index e8ca8b4..2d8559c 100644
--- a/components/payments/content/service_worker_payment_instrument.h
+++ b/components/payments/content/service_worker_payment_instrument.h
@@ -55,7 +55,7 @@
 
   // Validates whether this payment instrument can be used for this payment
   // request. It fires CanMakePaymentEvent to the payment app to do validation.
-  // The result is returned through callback.If the returned result is false,
+  // The result is returned through callback. If the returned result is false,
   // then this instrument should not be used for this payment request. This
   // interface must be called before any other interfaces in this class.
   void ValidateCanMakePayment(ValidateCanMakePaymentCallback callback);
@@ -84,7 +84,9 @@
   mojom::PaymentRequestEventDataPtr CreatePaymentRequestEventData();
 
   mojom::CanMakePaymentEventDataPtr CreateCanMakePaymentEventData();
-  void OnCanMakePayment(ValidateCanMakePaymentCallback callback, bool result);
+  void OnCanMakePaymentEventSkipped(ValidateCanMakePaymentCallback callback);
+  void OnCanMakePaymentEventResponded(ValidateCanMakePaymentCallback callback,
+                                      bool result);
 
   content::BrowserContext* browser_context_;
   GURL top_origin_;
@@ -102,6 +104,7 @@
 
   // PaymentAppProvider::CanMakePayment result of this payment instrument.
   bool can_make_payment_result_;
+  bool has_enrolled_instrument_result_;
 
   // Below variables are used for installable ServiceWorkerPaymentInstrument
   // specifically.
diff --git a/components/payments/content/service_worker_payment_instrument_unittest.cc b/components/payments/content/service_worker_payment_instrument_unittest.cc
index 17f4b83..76d9f623 100644
--- a/components/payments/content/service_worker_payment_instrument_unittest.cc
+++ b/components/payments/content/service_worker_payment_instrument_unittest.cc
@@ -8,8 +8,10 @@
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/test/scoped_feature_list.h"
 #include "components/payments/core/payment_request_delegate.h"
 #include "content/public/browser/stored_payment_app.h"
+#include "content/public/common/content_features.h"
 #include "content/public/test/test_browser_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -239,6 +241,23 @@
             "https://bobpay.com");
 }
 
+// Test the case when CanMakePaymentEvent cannot be fired. The instrument should
+// be considered valid, but not ready for payment.
+TEST_F(ServiceWorkerPaymentInstrumentTest, ValidateCanMakePayment) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(
+      features::kPaymentRequestHasEnrolledInstrument);
+
+  // CanMakePaymentEvent is not fired because this test instrument does not have
+  // any explicitly verified methods.
+  CreateServiceWorkerPaymentInstrument(/*with_url_method=*/true);
+  GetInstrument()->ValidateCanMakePayment(
+      base::BindOnce([](ServiceWorkerPaymentInstrument*, bool result) {
+        EXPECT_TRUE(result);
+      }));
+  EXPECT_FALSE(GetInstrument()->IsValidForCanMakePayment());
+}
+
 // Test modifiers can be matched based on capabilities.
 TEST_F(ServiceWorkerPaymentInstrumentTest, IsValidForModifier) {
   CreateServiceWorkerPaymentInstrument(true);
diff --git a/components/payments/core/payment_instrument.h b/components/payments/core/payment_instrument.h
index c444def..1098e345 100644
--- a/components/payments/core/payment_instrument.h
+++ b/components/payments/core/payment_instrument.h
@@ -52,6 +52,7 @@
   virtual base::string16 GetMissingInfoLabel() const = 0;
   // Returns whether the instrument is valid for the purposes of responding to
   // canMakePayment.
+  // TODO(crbug.com/915907): rename to IsValidForHasEnrolledInstrument.
   virtual bool IsValidForCanMakePayment() const = 0;
   // Records the use of this payment instrument.
   virtual void RecordUse() = 0;
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 11285421..52068e4 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -2071,7 +2071,9 @@
       'id': 506,
       'caption': '''Send username and filename to native printers''',
       'tags': [],
-      'desc': '''Send username and filename to native printers server with every print job. The default is not to send.''',
+      'desc': '''Send username and filename to native printers server with every print job. The default is not to send.
+
+      Setting this policy to true also disables printers that use protocols other than IPPS, USB, or IPP-over-USB since username and filename shouldn't be sent over the network openly.''',
     },
     {
       'name': 'ForceSafeSearch',
@@ -14480,7 +14482,47 @@
       When this policy is set Parent Access Code can be verified on child user's device.
       When this policy is unset it is not possible to verify Parent Access Code on child user's device.'''
     },
+    {
+      'name': 'CertificateManagementAllowed',
+      'type': 'int-enum',
+      'schema': {
+        'type': 'integer',
+        'enum': [ 0, 1, 2 ],
+      },
+      'items': [
+        {
+          'name': 'All',
+          'value': 0,
+          'caption': '''Allow users to manage all certificates''',
+        },
+        {
+          'name': 'UserOnly',
+          'value': 1,
+          'caption': '''Allow users to manage user certificates''',
+        },
+        {
+          'name': 'None',
+          'value': 2,
+          'caption': '''Disallow users from managing certificates''',
+        },
+      ],
+      'supported_on': ['chrome_os:74-'],
+      'tags' : [],
+      'features': {
+        'dynamic_refresh': True,
+        'per_profile': True
+      },
+      'example_value': 1,
+      'id': 518,
+      'caption': '''Allow users to manage installed certificates.''',
+      'desc': '''This policy controls whether user are able to import and remove certificates via Certificate Manager.
 
+      If this policy is set to ''Allow users to manage all certificates'' or left not set, users will be able to manage certificates.
+
+      If this policy is set to ''Allow users to manage user certificates'', users will be able to manage user certificates, but not device-wide certificates.
+
+      If this policy is set to ''Disallow users to manage certificates'', users will not be able to manage certificates, they can only view certificates.''',
+    },
   ],
 
   'messages': {
@@ -14646,5 +14688,5 @@
   },
   'placeholders': [],
   'deleted_policy_ids': [412],
-  'highest_id_currently_used':  517
+  'highest_id_currently_used':  518
 }
diff --git a/components/policy/tools/generate_policy_source.py b/components/policy/tools/generate_policy_source.py
index 31f3105..d89a8da 100755
--- a/components/policy/tools/generate_policy_source.py
+++ b/components/policy/tools/generate_policy_source.py
@@ -354,6 +354,7 @@
   f.write('#ifndef CHROME_COMMON_POLICY_CONSTANTS_H_\n'
           '#define CHROME_COMMON_POLICY_CONSTANTS_H_\n'
           '\n'
+          '#include <cstdint>\n'
           '#include <string>\n'
           '\n'
           '#include "base/values.h"\n'
@@ -405,6 +406,9 @@
   for protobuf_type in protobuf_types:
     _WriteChromePolicyAccessHeader(f, protobuf_type)
 
+  f.write('constexpr int64_t kDevicePolicyExternalDataResourceCacheSize = %d;\n'
+          % _ComputeTotalDevicePolicyExternalDataMaxSize(policies))
+
   f.write('\n}  // namespace policy\n\n'
           '#endif  // CHROME_COMMON_POLICY_CONSTANTS_H_\n')
 
@@ -426,6 +430,14 @@
           (protobuf_type, protobuf_type))
 
 
+def _ComputeTotalDevicePolicyExternalDataMaxSize(policies):
+  total_device_policy_external_data_max_size = 0
+  for policy in policies:
+    if policy.is_device_only and policy.policy_type == 'TYPE_EXTERNAL':
+      total_device_policy_external_data_max_size += policy.max_size
+  return total_device_policy_external_data_max_size
+
+
 #------------------ policy constants source ------------------------#
 
 SchemaNodeKey = namedtuple('SchemaNodeKey',
diff --git a/components/policy/tools/syntax_check_policy_template_json.py b/components/policy/tools/syntax_check_policy_template_json.py
index 5ddb145e..3871cdd 100755
--- a/components/policy/tools/syntax_check_policy_template_json.py
+++ b/components/policy/tools/syntax_check_policy_template_json.py
@@ -69,6 +69,13 @@
     # complex schemas using stringified JSON - instead, store them as dicts.
 ]
 
+# 100 MiB upper limit on the total device policy external data max size limits
+# due to the security reasons.
+# You can increase this limit if you're introducing new external data type
+# device policy, but be aware that too heavy policies could result in user
+# profiles not having enough space on the device.
+TOTAL_DEVICE_POLICY_EXTERNAL_DATA_MAX_SIZE = 1024 * 1024 * 100
+
 
 class PolicyTemplateChecker(object):
 
@@ -238,6 +245,22 @@
                    'store it in a dict and define it in "schema".') %
                   policy.get('name'))
 
+  def _CheckTotalDevicePolicyExternalDataMaxSize(self, policy_definitions):
+    total_device_policy_external_data_max_size = 0
+    for policy in policy_definitions:
+      if (policy.get('device_only', False) and
+          self._CheckContains(policy, 'type', str) == 'external'):
+        total_device_policy_external_data_max_size += self._CheckContains(
+            policy, 'max_size', int)
+    if (total_device_policy_external_data_max_size >
+        TOTAL_DEVICE_POLICY_EXTERNAL_DATA_MAX_SIZE):
+      self._Error(
+          ('Total sum of device policy external data maximum size limits ' +
+           'should not exceed %d bytes, current sum is %d bytes.') %
+          (TOTAL_DEVICE_POLICY_EXTERNAL_DATA_MAX_SIZE,
+           total_device_policy_external_data_max_size))
+
+
   # Returns True if the example value for a policy seems to contain JSON
   # embedded inside a string. Simply checks if strings start with '{', so it
   # doesn't flag numbers (which are valid JSON) but it does flag both JSON
@@ -697,6 +720,7 @@
       self._CheckPolicyIDs(policy_ids, deleted_policy_ids)
       if highest_id is not None:
         self._CheckHighestId(policy_ids, highest_id)
+      self._CheckTotalDevicePolicyExternalDataMaxSize(policy_definitions)
 
     # Made it as a dict (policy_name -> True) to reuse _CheckContains.
     policy_names = {
diff --git a/components/pref_registry/pref_registry_syncable.cc b/components/pref_registry/pref_registry_syncable.cc
index f458cf7f..a7b4fe9d 100644
--- a/components/pref_registry/pref_registry_syncable.cc
+++ b/components/pref_registry/pref_registry_syncable.cc
@@ -23,7 +23,6 @@
 }
 
 void PrefRegistrySyncable::OnPrefRegistered(const std::string& path,
-                                            base::Value* default_value,
                                             uint32_t flags) {
   // Tests that |flags| does not contain both SYNCABLE_PREF and
   // SYNCABLE_PRIORITY_PREF flags at the same time.
diff --git a/components/pref_registry/pref_registry_syncable.h b/components/pref_registry/pref_registry_syncable.h
index d8e7c457..9533e15 100644
--- a/components/pref_registry/pref_registry_syncable.h
+++ b/components/pref_registry/pref_registry_syncable.h
@@ -14,10 +14,6 @@
 #include "base/macros.h"
 #include "components/prefs/pref_registry_simple.h"
 
-namespace base {
-class Value;
-}
-
 // TODO(tfarina): Change this namespace to pref_registry.
 namespace user_prefs {
 
@@ -89,7 +85,6 @@
 
   // PrefRegistrySimple overrides.
   void OnPrefRegistered(const std::string& path,
-                        base::Value* default_value,
                         uint32_t flags) override;
 
   SyncableRegistrationCallback callback_;
diff --git a/components/prefs/default_pref_store.cc b/components/prefs/default_pref_store.cc
index 25e36b97..d8d13ec0 100644
--- a/components/prefs/default_pref_store.cc
+++ b/components/prefs/default_pref_store.cc
@@ -33,14 +33,13 @@
   return observers_.might_have_observers();
 }
 
-void DefaultPrefStore::SetDefaultValue(const std::string& key,
-                                       std::unique_ptr<Value> value) {
+void DefaultPrefStore::SetDefaultValue(const std::string& key, Value value) {
   DCHECK(!GetValue(key, nullptr));
   prefs_.SetValue(key, std::move(value));
 }
 
 void DefaultPrefStore::ReplaceDefaultValue(const std::string& key,
-                                           std::unique_ptr<Value> value) {
+                                           Value value) {
   DCHECK(GetValue(key, nullptr));
   bool notify = prefs_.SetValue(key, std::move(value));
   if (notify) {
diff --git a/components/prefs/default_pref_store.h b/components/prefs/default_pref_store.h
index 3607a35..17ba3ec 100644
--- a/components/prefs/default_pref_store.h
+++ b/components/prefs/default_pref_store.h
@@ -32,13 +32,11 @@
 
   // Sets a |value| for |key|. Should only be called if a value has not been
   // set yet; otherwise call ReplaceDefaultValue().
-  void SetDefaultValue(const std::string& key,
-                       std::unique_ptr<base::Value> value);
+  void SetDefaultValue(const std::string& key, base::Value value);
 
   // Replaces the the value for |key| with a new value. Should only be called
   // if a value has alreday been set; otherwise call SetDefaultValue().
-  void ReplaceDefaultValue(const std::string& key,
-                           std::unique_ptr<base::Value> value);
+  void ReplaceDefaultValue(const std::string& key, base::Value value);
 
   const_iterator begin() const;
   const_iterator end() const;
diff --git a/components/prefs/default_pref_store_unittest.cc b/components/prefs/default_pref_store_unittest.cc
index 20bbba3..bc40c4ca 100644
--- a/components/prefs/default_pref_store_unittest.cc
+++ b/components/prefs/default_pref_store_unittest.cc
@@ -52,18 +52,15 @@
   std::string kPrefKey("pref_key");
 
   // Setting a default value shouldn't send a change notification.
-  pref_store->SetDefaultValue(kPrefKey,
-                              std::unique_ptr<Value>(new Value("foo")));
+  pref_store->SetDefaultValue(kPrefKey, Value("foo"));
   EXPECT_EQ(0, observer.change_count());
 
   // Replacing the default value should send a change notification...
-  pref_store->ReplaceDefaultValue(kPrefKey,
-                                  std::unique_ptr<Value>(new Value("bar")));
+  pref_store->ReplaceDefaultValue(kPrefKey, Value("bar"));
   EXPECT_EQ(1, observer.change_count());
 
   // But only if the value actually changed.
-  pref_store->ReplaceDefaultValue(kPrefKey,
-                                  std::unique_ptr<Value>(new Value("bar")));
+  pref_store->ReplaceDefaultValue(kPrefKey, Value("bar"));
   EXPECT_EQ(1, observer.change_count());
 }
 
diff --git a/components/prefs/in_memory_pref_store.cc b/components/prefs/in_memory_pref_store.cc
index a0a9c35..2201d24 100644
--- a/components/prefs/in_memory_pref_store.cc
+++ b/components/prefs/in_memory_pref_store.cc
@@ -47,14 +47,15 @@
                                  std::unique_ptr<base::Value> value,
                                  uint32_t flags) {
   DCHECK(value);
-  if (prefs_.SetValue(key, std::move(value)))
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value))))
     ReportValueChanged(key, flags);
 }
 
 void InMemoryPrefStore::SetValueSilently(const std::string& key,
                                       std::unique_ptr<base::Value> value,
                                       uint32_t flags) {
-  prefs_.SetValue(key, std::move(value));
+  DCHECK(value);
+  prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)));
 }
 
 void InMemoryPrefStore::RemoveValue(const std::string& key, uint32_t flags) {
diff --git a/components/prefs/pref_registry.cc b/components/prefs/pref_registry.cc
index 89709c19..9f36c2fa 100644
--- a/components/prefs/pref_registry.cc
+++ b/components/prefs/pref_registry.cc
@@ -44,24 +44,21 @@
   DCHECK(value.type() == current_value->type())
       << "Wrong type for new default: " << pref_name;
 
-  defaults_->ReplaceDefaultValue(
-      pref_name, base::Value::ToUniquePtrValue(std::move(value)));
+  defaults_->ReplaceDefaultValue(pref_name, std::move(value));
 }
 
-void PrefRegistry::SetDefaultForeignPrefValue(
-    const std::string& path,
-    std::unique_ptr<base::Value> default_value,
-    uint32_t flags) {
+void PrefRegistry::SetDefaultForeignPrefValue(const std::string& path,
+                                              base::Value default_value,
+                                              uint32_t flags) {
   auto erased = foreign_pref_keys_.erase(path);
   DCHECK_EQ(1u, erased);
   RegisterPreference(path, std::move(default_value), flags);
 }
 
-void PrefRegistry::RegisterPreference(
-    const std::string& path,
-    std::unique_ptr<base::Value> default_value,
-    uint32_t flags) {
-  base::Value::Type orig_type = default_value->type();
+void PrefRegistry::RegisterPreference(const std::string& path,
+                                      base::Value default_value,
+                                      uint32_t flags) {
+  base::Value::Type orig_type = default_value.type();
   DCHECK(orig_type != base::Value::Type::NONE &&
          orig_type != base::Value::Type::BINARY) <<
          "invalid preference type: " << orig_type;
@@ -70,11 +67,11 @@
   DCHECK(!base::ContainsKey(registration_flags_, path))
       << "Trying to register a previously registered pref: " << path;
 
-  base::Value* default_value_raw = default_value.get();
   defaults_->SetDefaultValue(path, std::move(default_value));
   if (flags != NO_REGISTRATION_FLAGS)
     registration_flags_[path] = flags;
-  OnPrefRegistered(path, default_value_raw, flags);
+
+  OnPrefRegistered(path, flags);
 }
 
 void PrefRegistry::RegisterForeignPref(const std::string& path) {
@@ -83,5 +80,4 @@
 }
 
 void PrefRegistry::OnPrefRegistered(const std::string& path,
-                                    base::Value* default_value,
                                     uint32_t flags) {}
diff --git a/components/prefs/pref_registry.h b/components/prefs/pref_registry.h
index a4d0828..065bd65 100644
--- a/components/prefs/pref_registry.h
+++ b/components/prefs/pref_registry.h
@@ -7,7 +7,6 @@
 
 #include <stdint.h>
 
-#include <memory>
 #include <set>
 #include <unordered_map>
 
@@ -81,7 +80,7 @@
   // Sets the default value and flags of a previously-registered foreign pref
   // value.
   void SetDefaultForeignPrefValue(const std::string& path,
-                                  std::unique_ptr<base::Value> default_value,
+                                  base::Value default_value,
                                   uint32_t flags);
 
   const std::set<std::string>& foreign_pref_keys() const {
@@ -95,12 +94,11 @@
   // Used by subclasses to register a default value and registration flags for
   // a preference. |flags| is a bitmask of |PrefRegistrationFlags|.
   void RegisterPreference(const std::string& path,
-                          std::unique_ptr<base::Value> default_value,
+                          base::Value default_value,
                           uint32_t flags);
 
   // Allows subclasses to hook into pref registration.
   virtual void OnPrefRegistered(const std::string& path,
-                                base::Value* default_value,
                                 uint32_t flags);
 
   scoped_refptr<DefaultPrefStore> defaults_;
diff --git a/components/prefs/pref_registry_simple.cc b/components/prefs/pref_registry_simple.cc
index e0d7a95c..878f0ca 100644
--- a/components/prefs/pref_registry_simple.cc
+++ b/components/prefs/pref_registry_simple.cc
@@ -16,76 +16,86 @@
 void PrefRegistrySimple::RegisterBooleanPref(const std::string& path,
                                              bool default_value,
                                              uint32_t flags) {
-  RegisterPreference(path, std::make_unique<base::Value>(default_value), flags);
+  RegisterPreference(path, base::Value(default_value), flags);
 }
 
 void PrefRegistrySimple::RegisterIntegerPref(const std::string& path,
                                              int default_value,
                                              uint32_t flags) {
-  RegisterPreference(path, std::make_unique<base::Value>(default_value), flags);
+  RegisterPreference(path, base::Value(default_value), flags);
 }
 
 void PrefRegistrySimple::RegisterDoublePref(const std::string& path,
                                             double default_value,
                                             uint32_t flags) {
-  RegisterPreference(path, std::make_unique<base::Value>(default_value), flags);
+  RegisterPreference(path, base::Value(default_value), flags);
 }
 
 void PrefRegistrySimple::RegisterStringPref(const std::string& path,
                                             const std::string& default_value,
                                             uint32_t flags) {
-  RegisterPreference(path, std::make_unique<base::Value>(default_value), flags);
+  RegisterPreference(path, base::Value(default_value), flags);
 }
 
 void PrefRegistrySimple::RegisterFilePathPref(
     const std::string& path,
     const base::FilePath& default_value,
     uint32_t flags) {
-  RegisterPreference(path, std::make_unique<base::Value>(default_value.value()),
-                     flags);
+  RegisterPreference(path, base::Value(default_value.value()), flags);
 }
 
 void PrefRegistrySimple::RegisterListPref(const std::string& path,
                                           uint32_t flags) {
-  RegisterPreference(
-      path, std::make_unique<base::Value>(base::Value::Type::LIST), flags);
+  RegisterPreference(path, base::Value(base::Value::Type::LIST), flags);
+}
+
+void PrefRegistrySimple::RegisterListPref(const std::string& path,
+                                          base::Value default_value,
+                                          uint32_t flags) {
+  RegisterPreference(path, std::move(default_value), flags);
 }
 
 void PrefRegistrySimple::RegisterListPref(
     const std::string& path,
     std::unique_ptr<base::Value> default_value,
     uint32_t flags) {
-  RegisterPreference(path, std::move(default_value), flags);
+  DCHECK(default_value);
+  RegisterListPref(
+      path, base::Value::FromUniquePtrValue(std::move(default_value)), flags);
 }
 
 void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path,
                                                 uint32_t flags) {
-  RegisterPreference(
-      path, std::make_unique<base::Value>(base::Value::Type::DICTIONARY),
-      flags);
+  RegisterPreference(path, base::Value(base::Value::Type::DICTIONARY), flags);
+}
+
+void PrefRegistrySimple::RegisterDictionaryPref(const std::string& path,
+                                                base::Value default_value,
+                                                uint32_t flags) {
+  RegisterPreference(path, std::move(default_value), flags);
 }
 
 void PrefRegistrySimple::RegisterDictionaryPref(
     const std::string& path,
     std::unique_ptr<base::Value> default_value,
     uint32_t flags) {
-  RegisterPreference(path, std::move(default_value), flags);
+  DCHECK(default_value);
+  RegisterDictionaryPref(
+      path, base::Value::FromUniquePtrValue(std::move(default_value)), flags);
 }
 
 void PrefRegistrySimple::RegisterInt64Pref(const std::string& path,
                                            int64_t default_value,
                                            uint32_t flags) {
-  RegisterPreference(
-      path, std::make_unique<base::Value>(base::Int64ToString(default_value)),
-      flags);
+  RegisterPreference(path, base::Value(base::Int64ToString(default_value)),
+                     flags);
 }
 
 void PrefRegistrySimple::RegisterUint64Pref(const std::string& path,
                                             uint64_t default_value,
                                             uint32_t flags) {
-  RegisterPreference(
-      path, std::make_unique<base::Value>(base::NumberToString(default_value)),
-      flags);
+  RegisterPreference(path, base::Value(base::NumberToString(default_value)),
+                     flags);
 }
 
 void PrefRegistrySimple::RegisterTimePref(const std::string& path,
diff --git a/components/prefs/pref_registry_simple.h b/components/prefs/pref_registry_simple.h
index 1f0021d..c1677ce 100644
--- a/components/prefs/pref_registry_simple.h
+++ b/components/prefs/pref_registry_simple.h
@@ -51,6 +51,11 @@
                         uint32_t flags = NO_REGISTRATION_FLAGS);
 
   void RegisterListPref(const std::string& path,
+                        base::Value default_value,
+                        uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  // Deprecated: prefer to use the version that takes a base::Value by value.
+  void RegisterListPref(const std::string& path,
                         std::unique_ptr<base::Value> default_value,
                         uint32_t flags = NO_REGISTRATION_FLAGS);
 
@@ -58,6 +63,11 @@
                               uint32_t flags = NO_REGISTRATION_FLAGS);
 
   void RegisterDictionaryPref(const std::string& path,
+                              base::Value default_value,
+                              uint32_t flags = NO_REGISTRATION_FLAGS);
+
+  // Deprecated: prefer to use the version that takes a base::Value by value.
+  void RegisterDictionaryPref(const std::string& path,
                               std::unique_ptr<base::Value> default_value,
                               uint32_t flags = NO_REGISTRATION_FLAGS);
 
diff --git a/components/prefs/pref_value_map.cc b/components/prefs/pref_value_map.cc
index 6ff02b6..b932eef5 100644
--- a/components/prefs/pref_value_map.cc
+++ b/components/prefs/pref_value_map.cc
@@ -38,12 +38,6 @@
   return true;
 }
 
-bool PrefValueMap::SetValue(const std::string& key,
-                            std::unique_ptr<base::Value> value) {
-  DCHECK(value);
-  return SetValue(key, base::Value::FromUniquePtrValue(std::move(value)));
-}
-
 bool PrefValueMap::SetValue(const std::string& key, base::Value value) {
   base::Value& existing_value = prefs_[key];
   if (value == existing_value)
diff --git a/components/prefs/pref_value_map.h b/components/prefs/pref_value_map.h
index 8188cb1a..6a9b4b65 100644
--- a/components/prefs/pref_value_map.h
+++ b/components/prefs/pref_value_map.h
@@ -34,11 +34,6 @@
   bool GetValue(const std::string& key, const base::Value** value) const;
   bool GetValue(const std::string& key, base::Value** value);
 
-  // Sets a new |value| for |key|. |value| must be non-null. Returns true if the
-  // value changed.
-  // DEPRECATED. Use |SetValue(const std::string&, base::Value)| instead.
-  bool SetValue(const std::string& key, std::unique_ptr<base::Value> value);
-
   // Sets a new |value| for |key|. Returns true if the value changed.
   bool SetValue(const std::string& key, base::Value value);
 
diff --git a/components/prefs/pref_value_map_unittest.cc b/components/prefs/pref_value_map_unittest.cc
index f3b05f7a..7d2f05c0 100644
--- a/components/prefs/pref_value_map_unittest.cc
+++ b/components/prefs/pref_value_map_unittest.cc
@@ -16,9 +16,9 @@
   EXPECT_FALSE(map.GetValue("key", &result));
   EXPECT_FALSE(result);
 
-  EXPECT_TRUE(map.SetValue("key", std::make_unique<Value>("test")));
-  EXPECT_FALSE(map.SetValue("key", std::make_unique<Value>("test")));
-  EXPECT_TRUE(map.SetValue("key", std::make_unique<Value>("hi mom!")));
+  EXPECT_TRUE(map.SetValue("key", Value("test")));
+  EXPECT_FALSE(map.SetValue("key", Value("test")));
+  EXPECT_TRUE(map.SetValue("key", Value("hi mom!")));
 
   EXPECT_TRUE(map.GetValue("key", &result));
   EXPECT_TRUE(Value("hi mom!").Equals(result));
@@ -26,7 +26,7 @@
 
 TEST(PrefValueMapTest, GetAndSetIntegerValue) {
   PrefValueMap map;
-  ASSERT_TRUE(map.SetValue("key", std::make_unique<Value>(5)));
+  ASSERT_TRUE(map.SetValue("key", Value(5)));
 
   int int_value = 0;
   EXPECT_TRUE(map.GetInteger("key", &int_value));
@@ -39,7 +39,7 @@
 
 TEST(PrefValueMapTest, SetDoubleValue) {
   PrefValueMap map;
-  ASSERT_TRUE(map.SetValue("key", std::make_unique<Value>(5.5)));
+  ASSERT_TRUE(map.SetValue("key", Value(5.5)));
 
   const Value* result = nullptr;
   ASSERT_TRUE(map.GetValue("key", &result));
@@ -52,7 +52,7 @@
   PrefValueMap map;
   EXPECT_FALSE(map.RemoveValue("key"));
 
-  EXPECT_TRUE(map.SetValue("key", std::make_unique<Value>("test")));
+  EXPECT_TRUE(map.SetValue("key", Value("test")));
   EXPECT_TRUE(map.GetValue("key", nullptr));
 
   EXPECT_TRUE(map.RemoveValue("key"));
@@ -63,7 +63,7 @@
 
 TEST(PrefValueMapTest, Clear) {
   PrefValueMap map;
-  EXPECT_TRUE(map.SetValue("key", std::make_unique<Value>("test")));
+  EXPECT_TRUE(map.SetValue("key", Value("test")));
   EXPECT_TRUE(map.GetValue("key", nullptr));
 
   map.Clear();
@@ -73,9 +73,9 @@
 
 TEST(PrefValueMapTest, GetDifferingKeys) {
   PrefValueMap reference;
-  EXPECT_TRUE(reference.SetValue("b", std::make_unique<Value>("test")));
-  EXPECT_TRUE(reference.SetValue("c", std::make_unique<Value>("test")));
-  EXPECT_TRUE(reference.SetValue("e", std::make_unique<Value>("test")));
+  EXPECT_TRUE(reference.SetValue("b", Value("test")));
+  EXPECT_TRUE(reference.SetValue("c", Value("test")));
+  EXPECT_TRUE(reference.SetValue("e", Value("test")));
 
   PrefValueMap check;
   std::vector<std::string> differing_paths;
@@ -87,9 +87,9 @@
   expected_differing_paths.push_back("e");
   EXPECT_EQ(expected_differing_paths, differing_paths);
 
-  EXPECT_TRUE(check.SetValue("a", std::make_unique<Value>("test")));
-  EXPECT_TRUE(check.SetValue("c", std::make_unique<Value>("test")));
-  EXPECT_TRUE(check.SetValue("d", std::make_unique<Value>("test")));
+  EXPECT_TRUE(check.SetValue("a", Value("test")));
+  EXPECT_TRUE(check.SetValue("c", Value("test")));
+  EXPECT_TRUE(check.SetValue("d", Value("test")));
 
   reference.GetDifferingKeys(&check, &differing_paths);
   expected_differing_paths.clear();
@@ -102,14 +102,14 @@
 
 TEST(PrefValueMapTest, SwapTwoMaps) {
   PrefValueMap first_map;
-  EXPECT_TRUE(first_map.SetValue("a", std::make_unique<Value>("test")));
-  EXPECT_TRUE(first_map.SetValue("b", std::make_unique<Value>("test")));
-  EXPECT_TRUE(first_map.SetValue("c", std::make_unique<Value>("test")));
+  EXPECT_TRUE(first_map.SetValue("a", Value("test")));
+  EXPECT_TRUE(first_map.SetValue("b", Value("test")));
+  EXPECT_TRUE(first_map.SetValue("c", Value("test")));
 
   PrefValueMap second_map;
-  EXPECT_TRUE(second_map.SetValue("d", std::make_unique<Value>("test")));
-  EXPECT_TRUE(second_map.SetValue("e", std::make_unique<Value>("test")));
-  EXPECT_TRUE(second_map.SetValue("f", std::make_unique<Value>("test")));
+  EXPECT_TRUE(second_map.SetValue("d", Value("test")));
+  EXPECT_TRUE(second_map.SetValue("e", Value("test")));
+  EXPECT_TRUE(second_map.SetValue("f", Value("test")));
 
   first_map.Swap(&second_map);
 
diff --git a/components/prefs/testing_pref_store.cc b/components/prefs/testing_pref_store.cc
index 16257da8..43804078 100644
--- a/components/prefs/testing_pref_store.cc
+++ b/components/prefs/testing_pref_store.cc
@@ -54,7 +54,8 @@
 void TestingPrefStore::SetValue(const std::string& key,
                                 std::unique_ptr<base::Value> value,
                                 uint32_t flags) {
-  if (prefs_.SetValue(key, std::move(value))) {
+  DCHECK(value);
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)))) {
     committed_ = false;
     NotifyPrefValueChanged(key);
   }
@@ -63,9 +64,9 @@
 void TestingPrefStore::SetValueSilently(const std::string& key,
                                         std::unique_ptr<base::Value> value,
                                         uint32_t flags) {
-  if (value)
-    CheckPrefIsSerializable(key, *value);
-  if (prefs_.SetValue(key, std::move(value)))
+  DCHECK(value);
+  CheckPrefIsSerializable(key, *value);
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value))))
     committed_ = false;
 }
 
diff --git a/components/prefs/value_map_pref_store.cc b/components/prefs/value_map_pref_store.cc
index c31d7c7..79c2017 100644
--- a/components/prefs/value_map_pref_store.cc
+++ b/components/prefs/value_map_pref_store.cc
@@ -36,7 +36,8 @@
 void ValueMapPrefStore::SetValue(const std::string& key,
                                  std::unique_ptr<base::Value> value,
                                  uint32_t flags) {
-  if (prefs_.SetValue(key, std::move(value))) {
+  DCHECK(value);
+  if (prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)))) {
     for (Observer& observer : observers_)
       observer.OnPrefValueChanged(key);
   }
@@ -63,7 +64,8 @@
 void ValueMapPrefStore::SetValueSilently(const std::string& key,
                                          std::unique_ptr<base::Value> value,
                                          uint32_t flags) {
-  prefs_.SetValue(key, std::move(value));
+  DCHECK(value);
+  prefs_.SetValue(key, base::Value::FromUniquePtrValue(std::move(value)));
 }
 
 ValueMapPrefStore::~ValueMapPrefStore() {}
diff --git a/components/previews/core/previews_features.cc b/components/previews/core/previews_features.cc
index b4d0dc7..6c87ad1 100644
--- a/components/previews/core/previews_features.cc
+++ b/components/previews/core/previews_features.cc
@@ -99,5 +99,9 @@
 const base::Feature kHTTPSServerPreviewsUsingURLLoader{
     "HTTPSServerPreviewsUsingURLLoader", base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Uses 'Lite Mode' strings instead of 'Data Saver'.
+const base::Feature kDataSaverLiteModeRebranding{
+    "DataSaverLiteModeRebranding", base::FEATURE_DISABLED_BY_DEFAULT};
+
 }  // namespace features
 }  // namespace previews
diff --git a/components/previews/core/previews_features.h b/components/previews/core/previews_features.h
index 2ca4dd987..9266dde 100644
--- a/components/previews/core/previews_features.h
+++ b/components/previews/core/previews_features.h
@@ -24,6 +24,7 @@
 extern const base::Feature kSlowPageTriggering;
 extern const base::Feature kPreviewsDisallowedOnReloads;
 extern const base::Feature kHTTPSServerPreviewsUsingURLLoader;
+extern const base::Feature kDataSaverLiteModeRebranding;
 
 }  // namespace features
 }  // namespace previews
diff --git a/components/resources/security_interstitials_resources.grdp b/components/resources/security_interstitials_resources.grdp
index f6327679..d290349 100644
--- a/components/resources/security_interstitials_resources.grdp
+++ b/components/resources/security_interstitials_resources.grdp
@@ -8,5 +8,4 @@
   <include name="IDR_SECURITY_INTERSTITIAL_CONNECTION_HELP_HTML" file="../security_interstitials/content/resources/connection_help.html" compress="gzip" type="BINDATA" />
   <include name="IDR_SECURITY_INTERSTITIAL_CONNECTION_HELP_CSS" file="../security_interstitials/content/resources/connection_help.css" compress="gzip" type="BINDATA" />
   <include name="IDR_SECURITY_INTERSTITIAL_CONNECTION_HELP_JS" file="../security_interstitials/content/resources/connection_help.js" compress="gzip" type="BINDATA" />
-  <include name="IDR_SECURITY_INTERSTITIAL_ORIGIN_POLICY_HTML" compress="gzip" file="../security_interstitials/core/browser/resources/interstitial_origin_policy.html" type="BINDATA" />
 </grit-part>
diff --git a/components/safe_browsing/password_protection/password_protection_request.cc b/components/safe_browsing/password_protection/password_protection_request.cc
index 1259f57..b5688c4 100644
--- a/components/safe_browsing/password_protection/password_protection_request.cc
+++ b/components/safe_browsing/password_protection/password_protection_request.cc
@@ -13,11 +13,8 @@
 #include "components/password_manager/core/browser/password_reuse_detector.h"
 #include "components/safe_browsing/db/whitelist_checker_client.h"
 #include "components/safe_browsing/password_protection/password_protection_navigation_throttle.h"
-#include "components/safe_browsing/password_protection/visual_utils.h"
 #include "components/safe_browsing/web_ui/safe_browsing_ui.h"
-#include "components/zoom/zoom_controller.h"
 #include "content/public/browser/browser_task_traits.h"
-#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "net/base/escape.h"
 #include "net/base/load_flags.h"
@@ -38,20 +35,6 @@
 // the size of the report. UMA suggests 99.9% will have < 200 domains.
 const int kMaxReusedDomains = 200;
 
-// Parameters chosen to ensure privacy is preserved by visual features.
-const int kMinWidthForVisualFeatures = 576;
-const int kMinHeightForVisualFeatures = 576;
-const float kMaxZoomForVisualFeatures = 2.0;
-
-std::unique_ptr<VisualFeatures> ExtractVisualFeatures(
-    const SkBitmap& screenshot) {
-  auto features = std::make_unique<VisualFeatures>();
-  visual_utils::GetHistogramForImage(screenshot,
-                                     features->mutable_color_histogram());
-  visual_utils::GetBlurredImage(screenshot, features->mutable_image());
-  return features;
-}
-
 }  // namespace
 
 PasswordProtectionRequest::PasswordProtectionRequest(
@@ -156,7 +139,7 @@
   if (verdict != LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED)
     Finish(RequestOutcome::RESPONSE_ALREADY_CACHED, std::move(cached_response));
   else
-    FillRequestProto();
+    SendRequest();
 }
 
 void PasswordProtectionRequest::FillRequestProto() {
@@ -229,53 +212,11 @@
     default:
       NOTREACHED();
   }
-
-  if (trigger_type_ == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE &&
-      password_protection_service_->IsExtendedReporting() &&
-      zoom::ZoomController::GetZoomLevelForWebContents(web_contents_) <=
-          kMaxZoomForVisualFeatures &&
-      request_proto_->content_area_width() >= kMinWidthForVisualFeatures &&
-      request_proto_->content_area_height() >= kMinHeightForVisualFeatures) {
-    CollectVisualFeatures();
-  } else {
-    SendRequest();
-  }
-}
-
-void PasswordProtectionRequest::CollectVisualFeatures() {
-  content::RenderWidgetHostView* view =
-      web_contents_ ? web_contents_->GetRenderWidgetHostView() : nullptr;
-
-  if (!view)
-    SendRequest();
-
-  view->CopyFromSurface(
-      gfx::Rect(), gfx::Size(),
-      base::BindOnce(&PasswordProtectionRequest::OnScreenshotTaken,
-                     GetWeakPtr()));
-}
-
-void PasswordProtectionRequest::OnScreenshotTaken(const SkBitmap& screenshot) {
-  // Do the feature extraction on a worker thread, to avoid blocking the UI.
-  base::PostTaskWithTraitsAndReplyWithResult(
-      FROM_HERE,
-      {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
-       base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
-      base::BindOnce(&ExtractVisualFeatures, screenshot),
-      base::BindOnce(&PasswordProtectionRequest::OnVisualFeatureCollectionDone,
-                     GetWeakPtr()));
-}
-
-void PasswordProtectionRequest::OnVisualFeatureCollectionDone(
-    std::unique_ptr<VisualFeatures> visual_features) {
-  DCHECK_CURRENTLY_ON(BrowserThread::UI);
-
-  request_proto_->mutable_visual_features()->Swap(visual_features.get());
-  SendRequest();
 }
 
 void PasswordProtectionRequest::SendRequest() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  FillRequestProto();
 
   web_ui_token_ =
       WebUIInfoSingleton::GetInstance()->AddToPGPings(*request_proto_);
diff --git a/components/safe_browsing/password_protection/password_protection_request.h b/components/safe_browsing/password_protection/password_protection_request.h
index 8c086f4..db3faa8 100644
--- a/components/safe_browsing/password_protection/password_protection_request.h
+++ b/components/safe_browsing/password_protection/password_protection_request.h
@@ -10,9 +10,7 @@
 #include "base/task/cancelable_task_tracker.h"
 #include "components/safe_browsing/password_protection/metrics_util.h"
 #include "components/safe_browsing/password_protection/password_protection_service.h"
-#include "components/safe_browsing/proto/csd.pb.h"
 #include "content/public/browser/browser_thread.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 
 #include <vector>
 
@@ -136,16 +134,6 @@
   // Fill |request_proto_| with appropriate values.
   void FillRequestProto();
 
-  // Collects visual features from the current login page.
-  void CollectVisualFeatures();
-
-  // Processes the screenshot of the login page into visual features.
-  void OnScreenshotTaken(const SkBitmap& bitmap);
-
-  // Called when the visual feature extraction is complete.
-  void OnVisualFeatureCollectionDone(
-      std::unique_ptr<VisualFeatures> visual_features);
-
   // Initiates network request to Safe Browsing backend.
   void SendRequest();
 
diff --git a/components/security_interstitials/content/BUILD.gn b/components/security_interstitials/content/BUILD.gn
index 7d1a57e..e26ccdd 100644
--- a/components/security_interstitials/content/BUILD.gn
+++ b/components/security_interstitials/content/BUILD.gn
@@ -6,6 +6,8 @@
   sources = [
     "connection_help_ui.cc",
     "connection_help_ui.h",
+    "origin_policy_interstitial_page.cc",
+    "origin_policy_interstitial_page.h",
     "origin_policy_ui.cc",
     "origin_policy_ui.h",
     "security_interstitial_controller_client.cc",
diff --git a/components/security_interstitials/content/origin_policy_interstitial_page.cc b/components/security_interstitials/content/origin_policy_interstitial_page.cc
new file mode 100644
index 0000000..3697645
--- /dev/null
+++ b/components/security_interstitials/content/origin_policy_interstitial_page.cc
@@ -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.
+
+#include "components/security_interstitials/content/origin_policy_interstitial_page.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/strings/utf_string_conversions.h"
+#include "base/values.h"
+#include "components/strings/grit/components_strings.h"
+#include "content/public/browser/navigation_controller.h"
+#include "content/public/browser/origin_policy_commands.h"
+#include "content/public/browser/web_contents.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace security_interstitials {
+
+OriginPolicyInterstitialPage::OriginPolicyInterstitialPage(
+    content::WebContents* web_contents,
+    const GURL& request_url,
+    std::unique_ptr<SecurityInterstitialControllerClient> controller,
+    content::OriginPolicyErrorReason error_reason)
+    : SecurityInterstitialPage(web_contents,
+                               request_url,
+                               std::move(controller)),
+      error_reason_(error_reason) {}
+
+OriginPolicyInterstitialPage::~OriginPolicyInterstitialPage() {}
+
+void OriginPolicyInterstitialPage::OnInterstitialClosing() {}
+
+bool OriginPolicyInterstitialPage::ShouldCreateNewNavigation() const {
+  return false;
+}
+
+void OriginPolicyInterstitialPage::PopulateInterstitialStrings(
+    base::DictionaryValue* load_time_data) {
+  load_time_data->SetString("type", "ORIGIN_POLICY");
+
+  // User may choose to ignore the warning & proceed to the site.
+  load_time_data->SetBoolean("overridable", true);
+
+  // Custom messages depending on the OriginPolicyErrorReason:
+  int explanation_paragraph_id = IDS_ORIGIN_POLICY_EXPLANATION_OTHER;
+  switch (error_reason_) {
+    case content::OriginPolicyErrorReason::kCannotLoadPolicy:
+      explanation_paragraph_id = IDS_ORIGIN_POLICY_EXPLANATION_CANNOT_LOAD;
+      break;
+    case content::OriginPolicyErrorReason::kPolicyShouldNotRedirect:
+      explanation_paragraph_id =
+          IDS_ORIGIN_POLICY_EXPLANATION_SHOULD_NOT_REDIRECT;
+      break;
+    case content::OriginPolicyErrorReason::kOther:
+      explanation_paragraph_id = IDS_ORIGIN_POLICY_EXPLANATION_OTHER;
+      break;
+  }
+
+  // Variables in IDR_SECURITY_INTERSTITIAL_HTML / interstitial_large.html,
+  // resources defined in security_interstitials_strings.grdp.
+  const struct {
+    const char* name;
+    int id;
+  } messages[] = {
+      {"closeDetails", IDS_ORIGIN_POLICY_CLOSE},
+      {"explanationParagraph", explanation_paragraph_id},
+      {"finalParagraph", IDS_ORIGIN_POLICY_FINAL_PARAGRAPH},
+      {"heading", IDS_ORIGIN_POLICY_HEADING},
+      {"openDetails", IDS_ORIGIN_POLICY_DETAILS},
+      {"primaryButtonText", IDS_ORIGIN_POLICY_BUTTON},
+      {"primaryParagraph", IDS_ORIGIN_POLICY_INFO},
+      {"recurrentErrorParagraph", IDS_ORIGIN_POLICY_INFO2},
+      {"tabTitle", IDS_ORIGIN_POLICY_TITLE},
+  };
+  // We interpolate _all_ strings with URL ($1) and origin ($2).
+  const std::vector<base::string16> message_params = {
+      base::ASCIIToUTF16(request_url().spec()),
+      base::ASCIIToUTF16(url::Origin::Create(request_url()).Serialize()),
+  };
+  for (const auto& message : messages) {
+    load_time_data->SetString(
+        message.name,
+        base::ReplaceStringPlaceholders(l10n_util::GetStringUTF16(message.id),
+                                        message_params, nullptr));
+  };
+
+  // Selectively enable certain UI elements.
+  load_time_data->SetBoolean(
+      "hide_primary_button",
+      !web_contents()->GetController().CanGoBack() ||
+          load_time_data->FindStringKey("primaryButtonText")->empty());
+  load_time_data->SetBoolean(
+      "show_recurrent_error_paragraph",
+      !load_time_data->FindStringKey("recurrentErrorParagraph")->empty());
+}
+
+void OriginPolicyInterstitialPage::CommandReceived(const std::string& command) {
+  // The command string we get passed in is interstitial_commands.mojom turned
+  // into a number turned into a string.
+  //
+  // TODO(carlosil): After non-committed insterstitials have been removed this
+  //                 should be cleaned up to use enum values (or somesuch).
+  if (command == "0") {
+    OnDontProceed();
+  } else if (command == "1") {
+    OnProceed();
+  } else if (command == "2") {
+    // "Advanced" button, which shows extra text. This is handled within
+    // the page.
+  } else {
+    NOTREACHED();
+  }
+}
+
+void OriginPolicyInterstitialPage::OnProceed() {
+  content::OriginPolicyAddExceptionFor(request_url());
+  web_contents()->GetController().Reload(content::ReloadType::NORMAL, true);
+}
+
+void OriginPolicyInterstitialPage::OnDontProceed() {
+  // "Go Back" / "Don't Proceed" button should be disabled if we can't go back.
+  DCHECK(web_contents()->GetController().CanGoBack());
+  web_contents()->GetController().GoBack();
+}
+
+}  // namespace security_interstitials
diff --git a/components/security_interstitials/content/origin_policy_interstitial_page.h b/components/security_interstitials/content/origin_policy_interstitial_page.h
new file mode 100644
index 0000000..87d43c3
--- /dev/null
+++ b/components/security_interstitials/content/origin_policy_interstitial_page.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 COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_ORIGIN_POLICY_INTERSTITIAL_PAGE_H_
+#define COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_ORIGIN_POLICY_INTERSTITIAL_PAGE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/security_interstitials/content/security_interstitial_controller_client.h"
+#include "components/security_interstitials/content/security_interstitial_page.h"
+#include "content/public/browser/interstitial_page_delegate.h"
+#include "content/public/browser/origin_policy_error_reason.h"
+
+#include "url/gurl.h"
+
+namespace content {
+class WebContents;
+}  // namespace content
+
+namespace security_interstitials {
+class SecurityInterstitialControllerClient;
+
+class OriginPolicyInterstitialPage : public SecurityInterstitialPage {
+ public:
+  OriginPolicyInterstitialPage(
+      content::WebContents* web_contents,
+      const GURL& request_url,
+      std::unique_ptr<SecurityInterstitialControllerClient> controller,
+      content::OriginPolicyErrorReason error_reason);
+
+  ~OriginPolicyInterstitialPage() override;
+
+  void OnInterstitialClosing() override;
+
+  void CommandReceived(const std::string& command) override;
+  void OnProceed() override;
+  void OnDontProceed() override;
+
+ protected:
+  bool ShouldCreateNewNavigation() const override;
+  void PopulateInterstitialStrings(base::DictionaryValue*) override;
+
+ private:
+  content::OriginPolicyErrorReason error_reason_;
+
+  DISALLOW_COPY_AND_ASSIGN(OriginPolicyInterstitialPage);
+};
+
+}  // namespace security_interstitials
+
+#endif  // COMPONENTS_SECURITY_INTERSTITIALS_CONTENT_ORIGIN_POLICY_INTERSTITIAL_PAGE_H_
diff --git a/components/security_interstitials/content/origin_policy_ui.cc b/components/security_interstitials/content/origin_policy_ui.cc
index 9f83d41..5ed3836f 100644
--- a/components/security_interstitials/content/origin_policy_ui.cc
+++ b/components/security_interstitials/content/origin_policy_ui.cc
@@ -4,36 +4,60 @@
 
 #include "components/security_interstitials/content/origin_policy_ui.h"
 
-#include "components/grit/components_resources.h"
-#include "third_party/zlib/google/compression_utils.h"
-#include "ui/base/resource/resource_bundle.h"
-#include "ui/base/template_expressions.h"
+#include <string>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/optional.h"
+#include "components/security_interstitials/content/origin_policy_interstitial_page.h"
+#include "components/security_interstitials/content/security_interstitial_tab_helper.h"
+#include "components/security_interstitials/core/metrics_helper.h"
+#include "content/public/browser/navigation_handle.h"
 #include "url/gurl.h"
-#include "url/origin.h"
 
 namespace security_interstitials {
 
-base::Optional<std::string> OriginPolicyUI::GetErrorPage(
+namespace {
+
+std::unique_ptr<SecurityInterstitialPage> GetErrorPageImpl(
     content::OriginPolicyErrorReason error_reason,
-    const url::Origin& origin,
+    content::WebContents* web_contents,
     const GURL& url) {
-  const base::StringPiece raw_interstitial_page_resource =
-      ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
-          IDR_SECURITY_INTERSTITIAL_ORIGIN_POLICY_HTML);
+  MetricsHelper::ReportDetails report_details;
+  report_details.metric_prefix = "origin_policy";
+  std::unique_ptr<SecurityInterstitialControllerClient> controller =
+      std::make_unique<SecurityInterstitialControllerClient>(
+          web_contents,
+          std::make_unique<MetricsHelper>(url, report_details, nullptr),
+          nullptr, /* pref service: can be null */
+          "", GURL());
+  return std::make_unique<security_interstitials::OriginPolicyInterstitialPage>(
+      web_contents, url, std::move(controller), error_reason);
+}
 
-  // The resource is gzip compressed.
-  std::string interstitial_page_resource;
-  interstitial_page_resource.resize(
-      compression::GetUncompressedSize(raw_interstitial_page_resource));
-  base::StringPiece buffer(interstitial_page_resource.c_str(),
-                           interstitial_page_resource.size());
-  CHECK(compression::GzipUncompress(raw_interstitial_page_resource, buffer));
+}  // namespace
 
-  ui::TemplateReplacements params;
-  params["url"] = url.spec();
-  params["origin"] = origin.Serialize();
+base::Optional<std::string> OriginPolicyUI::GetErrorPageAsHTML(
+    content::OriginPolicyErrorReason error_reason,
+    content::NavigationHandle* handle) {
+  DCHECK(handle);
+  std::unique_ptr<SecurityInterstitialPage> page(GetErrorPageImpl(
+      error_reason, handle->GetWebContents(), handle->GetURL()));
+  std::string html = page->GetHTMLContents();
 
-  return ui::ReplaceTemplateExpressions(interstitial_page_resource, params);
+  // The page object is "associated" with the web contents, and this is how
+  // the interstitial infrastructure will find this instance again.
+  security_interstitials::SecurityInterstitialTabHelper::AssociateBlockingPage(
+      handle->GetWebContents(), handle->GetNavigationId(), std::move(page));
+
+  return html;
+}
+
+SecurityInterstitialPage* OriginPolicyUI::GetBlockingPage(
+    content::OriginPolicyErrorReason error_reason,
+    content::WebContents* web_contents,
+    const GURL& url) {
+  return GetErrorPageImpl(error_reason, web_contents, url).release();
 }
 
 }  // namespace security_interstitials
diff --git a/components/security_interstitials/content/origin_policy_ui.h b/components/security_interstitials/content/origin_policy_ui.h
index 6f8fbd4..06a27c5 100644
--- a/components/security_interstitials/content/origin_policy_ui.h
+++ b/components/security_interstitials/content/origin_policy_ui.h
@@ -7,26 +7,36 @@
 
 #include <memory>
 
+#include <string>
 #include "base/macros.h"
 #include "base/optional.h"
 #include "base/strings/string16.h"
 
 class GURL;
-namespace url {
-class Origin;
-}  // namespace url
+
 namespace content {
+class NavigationHandle;
+class WebContents;
 enum class OriginPolicyErrorReason;
 }  // namespace content
 
 namespace security_interstitials {
+class SecurityInterstitialPage;
 
 // A helper class to build the error page for Origin Policy errors.
 class OriginPolicyUI {
  public:
-  static base::Optional<std::string> GetErrorPage(
+  // Create the error page for the given NavigationHandle.
+  // This is intended to implement the ContentBrowserClient interface.
+  static base::Optional<std::string> GetErrorPageAsHTML(
       content::OriginPolicyErrorReason error_reason,
-      const url::Origin& origin,
+      content::NavigationHandle* handle);
+
+  // Create the error page instance for the given WebContents + URL.
+  // This is intended for use by debug functions (like chrome:://interstitials).
+  static SecurityInterstitialPage* GetBlockingPage(
+      content::OriginPolicyErrorReason error_reason,
+      content::WebContents* web_contents,
       const GURL& url);
 };
 
diff --git a/components/security_interstitials/core/browser/resources/interstitial_large.js b/components/security_interstitials/core/browser/resources/interstitial_large.js
index 8756cab..2674b999 100644
--- a/components/security_interstitials/core/browser/resources/interstitial_large.js
+++ b/components/security_interstitials/core/browser/resources/interstitial_large.js
@@ -110,6 +110,7 @@
           break;
 
         case 'SAFEBROWSING':
+        case 'ORIGIN_POLICY':
           sendCommand(SecurityInterstitialCommandId.CMD_DONT_PROCEED);
           break;
 
diff --git a/components/security_interstitials/core/browser/resources/interstitial_origin_policy.html b/components/security_interstitials/core/browser/resources/interstitial_origin_policy.html
deleted file mode 100644
index d47e5568..0000000
--- a/components/security_interstitials/core/browser/resources/interstitial_origin_policy.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!doctype html>
-<html>
-<head>
-  <title>Origin Policy Error</title>
-</head>
-<body>
-  <p>The server has requested that an
-    <a href="https://github.com/WICG/origin-policy">Origin Policy</a>
-    is applied to this origin, but it is not currently serving a suitable
-    policy.</p>
-  <p><ul>
-    <li>You're trying to go to: $i18n{url}</li>
-    <li>The policy applies to: $i18n{origin}</li>
-  </ul></p>
-</body>
-</html>
diff --git a/components/security_interstitials/core/browser/resources/list_of_interstitials.html b/components/security_interstitials/core/browser/resources/list_of_interstitials.html
index 5bdf2bc..79fbe93d 100644
--- a/components/security_interstitials/core/browser/resources/list_of_interstitials.html
+++ b/components/security_interstitials/core/browser/resources/list_of_interstitials.html
@@ -117,5 +117,13 @@
       </a>
     </li>
   </ul>
+  <h3>Origin Policy</h3>
+  <ul>
+    <li>
+      <a href="origin_policy">
+        Origin Policy Error
+      </a>
+    </li>
+  </ul>
 </body>
 </html>
diff --git a/components/security_interstitials_strings.grdp b/components/security_interstitials_strings.grdp
index d76cefd3..041a2d4 100644
--- a/components/security_interstitials_strings.grdp
+++ b/components/security_interstitials_strings.grdp
@@ -380,4 +380,54 @@
   <message name="IDS_BILLING_PROCEED_BUTTON" desc="The text for the billing interstitial proceed button.">
     Proceed
   </message>
+
+  <!-- Origin Policy Error Interstial -->
+  <message name="IDS_ORIGIN_POLICY_TITLE"
+    desc="Title of the Origin Policy Error interstitial.">
+    Origin Policy Error
+  </message>
+  <message name="IDS_ORIGIN_POLICY_HEADING"
+    desc="The large heading at the top of the Origin Policy Error Interstitial">
+    Blocked according to <ph name="ORIGIN">$2</ph>'s security policy.
+  </message>
+  <message name="IDS_ORIGIN_POLICY_INFO"
+    desc="The primary explanatory paragraph for the Origin Policy Error interstitial.">
+    The site <ph name="ORIGIN">$2</ph> has requested that a security policy
+    will apply to all its request, and this policy presently deems the site
+    unsafe.
+  </message>
+  <message name="IDS_ORIGIN_POLICY_INFO2" desc="The second paragrpah of the text of the Origin Policy Error Interstitial. (Presently intentionally left blank.)" />
+  <message name="IDS_ORIGIN_POLICY_BUTTON"
+    desc="The text for the Origin Policy Error Interstitial button that takes the user back to the previous page">
+    Back to safety.
+  </message>
+  <message name="IDS_ORIGIN_POLICY_DETAILS"
+    desc="The text for the Origin Policy Error Interstitial button that contains additional information.">
+    Advanced
+  </message>
+  <message name="IDS_ORIGIN_POLICY_EXPLANATION_CANNOT_LOAD" desc="The text of the Origin Policy Error Interstitial that will be displayed when the user presses the 'Advanced' button for additional information, for the case where the policy could not be loaded.">
+    The server you are going to, <ph name="ORIGIN">$2</ph>, has requested that
+    a security policy will be applied to all requests to it. But it has now
+    failed to deliver a policy, which prevents the browser from fulfilling
+    your request for <ph name="SITE">$1</ph>.
+  </message>
+  <message name="IDS_ORIGIN_POLICY_EXPLANATION_SHOULD_NOT_REDIRECT" desc="The text of the Origin Policy Error Interstitial that will be displayed when the user presses the 'Advanced' button for additional information, for the case where the policy request was met with a 'redirect' response.">
+    The server you are going to, <ph name="ORIGIN">$2</ph>, has requested that
+    a security policy will be applied to all requests to it. But instead of
+    delivering a policy it has redirected the browser elsewhere, which prevents
+    the browser from fulfilling your request for <ph name="SITE">$1</ph>.
+  </message>
+  <message name="IDS_ORIGIN_POLICY_EXPLANATION_OTHER" desc="The text of the Origin Policy Error Interstitial that will be displayed when the user presses the 'Advanced' button for additional information, for cases other than redirect or load error.">
+    The server you are going to, <ph name="ORIGIN">$2</ph>, has requested that
+    a security policy will be applied to all requests to it. But it has now
+    delivered an invalid policy, which prevents the browser from
+    fulfilling your request for <ph name="SITE">$1</ph>.
+  </message>
+  <message name="IDS_ORIGIN_POLICY_FINAL_PARAGRAPH"
+    desc="The text of the Origin Policy Error Interstitial that a user can click to proceed to the site, despite the error.">
+    <ph name="BEGIN_LINK">&lt;a href="#" id="proceed-link"&gt;</ph>Proceed to <ph name="SITE">$1<ex>example.com</ex></ph> (unsafe)<ph name="END_LINK">&lt;/a&gt;</ph>
+  </message>
+  <message name="IDS_ORIGIN_POLICY_CLOSE" desc="The text of the Origin Policy Error Interstitial that will hide the 'advanced' section again.">
+    Hide advanced
+  </message>
 </grit-part>
diff --git a/components/services/heap_profiling/connection_manager.cc b/components/services/heap_profiling/connection_manager.cc
index 9fee87a2..d3965bfe 100644
--- a/components/services/heap_profiling/connection_manager.cc
+++ b/components/services/heap_profiling/connection_manager.cc
@@ -151,15 +151,14 @@
   // when the user is attempting to manually start profiling for processes, so
   // we ignore this edge case.
 
-  scoped_refptr<ReceiverPipe> new_pipe = new ReceiverPipe(
+  scoped_refptr<ReceiverPipe> new_pipe = base::MakeRefCounted<ReceiverPipe>(
       mojo::UnwrapPlatformHandle(std::move(receiver_pipe_end)));
 
   // The allocation tracker will call this on a background thread, so thunk
   // back to the current thread with weak pointers.
-  AllocationTracker::CompleteCallback complete_cb =
-      base::BindOnce(&ConnectionManager::OnConnectionCompleteThunk,
-                     base::MessageLoopCurrent::Get()->task_runner(),
-                     weak_factory_.GetWeakPtr(), pid);
+  AllocationTracker::CompleteCallback complete_cb = base::BindOnce(
+      &ConnectionManager::OnConnectionCompleteThunk,
+      base::ThreadTaskRunnerHandle::Get(), weak_factory_.GetWeakPtr(), pid);
 
   auto connection = std::make_unique<Connection>(
       std::move(complete_cb), &backtrace_storage_, pid, std::move(client),
@@ -170,7 +169,7 @@
   options.message_loop_type = base::MessageLoop::TYPE_IO;
   connection->thread.StartWithOptions(options);
 
-  connection->parser = new StreamParser(&connection->tracker);
+  connection->parser = base::MakeRefCounted<StreamParser>(&connection->tracker);
   new_pipe->SetReceiver(connection->thread.task_runner(), connection->parser);
 
   connection->thread.task_runner()->PostTask(
@@ -187,9 +186,8 @@
   base::AutoLock lock(connections_lock_);
   std::vector<base::ProcessId> results;
   results.reserve(connections_.size());
-  for (const auto& pair : connections_) {
+  for (const auto& pair : connections_)
     results.push_back(pair.first);
-  }
   return results;
 }
 
@@ -253,9 +251,6 @@
   tracking->vm_regions = std::move(vm_regions);
   tracking->results.reserve(connections_.size());
 
-  scoped_refptr<base::SingleThreadTaskRunner> task_runner =
-      base::MessageLoopCurrent::Get()->task_runner();
-
   for (auto& it : connections_) {
     base::ProcessId pid = it.first;
     Connection* connection = it.second.get();
@@ -272,7 +267,7 @@
     // signal. The callback will be issued on the allocation tracker thread so
     // need to thunk back to the I/O thread.
     connection->tracker.SnapshotOnBarrier(
-        barrier_id, task_runner,
+        barrier_id, base::ThreadTaskRunnerHandle::Get(),
         base::BindOnce(&ConnectionManager::DoDumpOneProcessForTracing,
                        weak_factory_.GetWeakPtr(), tracking, pid,
                        connection->process_type, keep_small_allocations,
@@ -432,9 +427,8 @@
                        task_runner->PostTask(FROM_HERE,
                                              base::BindOnce(std::move(callback),
                                                             std::move(buffer)));
-
                      },
-                     reply_size, base::MessageLoopCurrent::Get()->task_runner(),
+                     reply_size, base::ThreadTaskRunnerHandle::Get(),
                      std::move(finished_callback)));
 }
 
diff --git a/components/services/heap_profiling/heap_profiling_service.cc b/components/services/heap_profiling/heap_profiling_service.cc
index eb6a37a..3d43108 100644
--- a/components/services/heap_profiling/heap_profiling_service.cc
+++ b/components/services/heap_profiling/heap_profiling_service.cc
@@ -4,8 +4,11 @@
 
 #include "components/services/heap_profiling/heap_profiling_service.h"
 
+#include <memory>
+
 #include "base/bind.h"
 #include "base/logging.h"
+#include "base/task/post_task.h"
 #include "components/services/heap_profiling/public/mojom/heap_profiling_client.mojom.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "services/resource_coordinator/public/cpp/resource_coordinator_features.h"
@@ -18,7 +21,32 @@
     service_manager::mojom::ServiceRequest request)
     : service_binding_(this, std::move(request)) {}
 
-HeapProfilingService::~HeapProfilingService() {}
+HeapProfilingService::~HeapProfilingService() = default;
+
+// static
+base::RepeatingCallback<void(service_manager::mojom::ServiceRequest)>
+HeapProfilingService::GetServiceFactory() {
+  return base::BindRepeating(
+      [](service_manager::mojom::ServiceRequest request) {
+        // base::WithBaseSyncPrimitives() and thus DEDICATED are needed
+        // because the thread owned by ConnectionManager::Connection is doing
+        // blocking Join during dectruction.
+        scoped_refptr<base::SingleThreadTaskRunner> task_runner =
+            base::CreateSingleThreadTaskRunnerWithTraits(
+                {base::TaskPriority::BEST_EFFORT,
+                 base::WithBaseSyncPrimitives()},
+                base::SingleThreadTaskRunnerThreadMode::DEDICATED);
+        task_runner->PostTask(
+            FROM_HERE,
+            base::BindOnce(
+                [](service_manager::mojom::ServiceRequest request) {
+                  service_manager::Service::RunAsyncUntilTermination(
+                      std::make_unique<heap_profiling::HeapProfilingService>(
+                          std::move(request)));
+                },
+                std::move(request)));
+      });
+}
 
 void HeapProfilingService::OnStart() {
   registry_.AddInterface(
diff --git a/components/services/heap_profiling/heap_profiling_service.h b/components/services/heap_profiling/heap_profiling_service.h
index 661c5573..ecdefe2 100644
--- a/components/services/heap_profiling/heap_profiling_service.h
+++ b/components/services/heap_profiling/heap_profiling_service.h
@@ -34,6 +34,9 @@
   explicit HeapProfilingService(service_manager::mojom::ServiceRequest request);
   ~HeapProfilingService() override;
 
+  static base::RepeatingCallback<void(service_manager::mojom::ServiceRequest)>
+  GetServiceFactory();
+
   // Lifescycle events that occur after the service has started to spinup.
   void OnStart() override;
   void OnBindInterface(const service_manager::BindSourceInfo& source_info,
diff --git a/components/services/heap_profiling/public/cpp/settings.cc b/components/services/heap_profiling/public/cpp/settings.cc
index 07d6d138..6a9d01f 100644
--- a/components/services/heap_profiling/public/cpp/settings.cc
+++ b/components/services/heap_profiling/public/cpp/settings.cc
@@ -23,9 +23,11 @@
 const char kOOPHeapProfilingFeatureStackMode[] = "stack-mode";
 const char kOOPHeapProfilingFeatureSampling[] = "sampling";
 const char kOOPHeapProfilingFeatureSamplingRate[] = "sampling-rate";
+const char kOOPHeapProfilingFeatureInProcess[] = "in-process";
 
 const uint32_t kDefaultSamplingRate = 100000;
 const bool kDefaultShouldSample = true;
+const bool kDefaultInProcessMode = false;
 
 bool RecordAllAllocationsForStartup() {
   const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
@@ -34,7 +36,7 @@
 
   return !base::GetFieldTrialParamByFeatureAsBool(
       kOOPHeapProfilingFeature, kOOPHeapProfilingFeatureSampling,
-      /*default_value=*/kDefaultShouldSample);
+      kDefaultShouldSample);
 }
 
 }  // namespace
@@ -146,7 +148,14 @@
 
   return base::GetFieldTrialParamByFeatureAsInt(
       kOOPHeapProfilingFeature, kOOPHeapProfilingFeatureSamplingRate,
-      /*default_value=*/kDefaultSamplingRate);
+      kDefaultSamplingRate);
+}
+
+bool IsInProcessModeEnabled() {
+  return base::CommandLine::ForCurrentProcess()->HasSwitch(kMemlogInProcess) ||
+         base::GetFieldTrialParamByFeatureAsBool(
+             kOOPHeapProfilingFeature, kOOPHeapProfilingFeatureInProcess,
+             kDefaultInProcessMode);
 }
 
 bool IsBackgroundHeapProfilingEnabled() {
diff --git a/components/services/heap_profiling/public/cpp/settings.h b/components/services/heap_profiling/public/cpp/settings.h
index 10ed1d0..1629cd3b 100644
--- a/components/services/heap_profiling/public/cpp/settings.h
+++ b/components/services/heap_profiling/public/cpp/settings.h
@@ -59,6 +59,7 @@
 // recorded every N bytes of allocated objects.
 uint32_t GetSamplingRateForStartup();
 
+bool IsInProcessModeEnabled();
 bool IsBackgroundHeapProfilingEnabled();
 bool ShouldKeepSmallAllocations();
 
diff --git a/components/services/heap_profiling/public/cpp/switches.cc b/components/services/heap_profiling/public/cpp/switches.cc
index 4f17ee31..1e9a82c7 100644
--- a/components/services/heap_profiling/public/cpp/switches.cc
+++ b/components/services/heap_profiling/public/cpp/switches.cc
@@ -7,6 +7,7 @@
 namespace heap_profiling {
 
 const char kMemlog[] = "memlog";
+const char kMemlogInProcess[] = "memlog-in-process";
 const char kMemlogKeepSmallAllocations[] = "memlog-keep-small-allocations";
 const char kMemlogModeAll[] = "all";
 const char kMemlogModeAllRenderers[] = "all-renderers";
diff --git a/components/services/heap_profiling/public/cpp/switches.h b/components/services/heap_profiling/public/cpp/switches.h
index 582ea2a..e304ffb9 100644
--- a/components/services/heap_profiling/public/cpp/switches.h
+++ b/components/services/heap_profiling/public/cpp/switches.h
@@ -8,6 +8,7 @@
 namespace heap_profiling {
 
 extern const char kMemlog[];
+extern const char kMemlogInProcess[];
 extern const char kMemlogKeepSmallAllocations[];
 extern const char kMemlogModeAll[];
 extern const char kMemlogModeAllRenderers[];
diff --git a/components/services/heap_profiling/receiver_pipe_win.cc b/components/services/heap_profiling/receiver_pipe_win.cc
index cba6a69..d2945bdd 100644
--- a/components/services/heap_profiling/receiver_pipe_win.cc
+++ b/components/services/heap_profiling/receiver_pipe_win.cc
@@ -20,13 +20,13 @@
     : ReceiverPipeBase(std::move(handle)),
       read_buffer_(new char[SenderPipe::kPipeSize]) {
   ZeroOverlapped();
-  base::MessageLoopCurrentForIO::Get()->RegisterIOHandler(
-      handle_.GetHandle().Get(), this);
 }
 
-ReceiverPipe::~ReceiverPipe() {}
+ReceiverPipe::~ReceiverPipe() = default;
 
 void ReceiverPipe::StartReadingOnIOThread() {
+  base::MessageLoopCurrentForIO::Get()->RegisterIOHandler(
+      handle_.GetHandle().Get(), this);
   ReadUntilBlocking();
 }
 
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index 548666a..bdf504e 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -225,8 +225,6 @@
   sources = [
     "fake_account_fetcher_service.cc",
     "fake_account_fetcher_service.h",
-    "fake_gaia_cookie_manager_service.cc",
-    "fake_gaia_cookie_manager_service.h",
     "fake_profile_oauth2_token_service.cc",
     "fake_profile_oauth2_token_service.h",
     "fake_signin_manager.cc",
diff --git a/components/signin/core/browser/account_investigator_unittest.cc b/components/signin/core/browser/account_investigator_unittest.cc
index d8eaca6d9..a08ba06 100644
--- a/components/signin/core/browser/account_investigator_unittest.cc
+++ b/components/signin/core/browser/account_investigator_unittest.cc
@@ -19,6 +19,7 @@
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "google_apis/gaia/google_service_auth_error.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 using base::HistogramTester;
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc
index 7ffec70..2b85df62b 100644
--- a/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -26,9 +26,9 @@
 #include "components/signin/core/browser/account_reconcilor.h"
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/fake_account_fetcher_service.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/signin/core/browser/fake_signin_manager.h"
+#include "components/signin/core/browser/list_accounts_test_utils.h"
 #include "components/signin/core/browser/mirror_account_reconcilor_delegate.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_buildflags.h"
@@ -237,7 +237,7 @@
 };
 
 // Converts CookieParams to ListedAccounts.
-gaia::ListedAccount ListedAccounfFromCookieParams(
+gaia::ListedAccount ListedAccountFromCookieParams(
     const signin::CookieParams& params,
     const std::string& account_id) {
   gaia::ListedAccount listed_account;
@@ -1982,7 +1982,7 @@
 
   // Add extra cookie change notification. Reconcilor should ignore it.
   gaia::ListedAccount listed_account =
-      ListedAccounfFromCookieParams(cookie_params, account_id);
+      ListedAccountFromCookieParams(cookie_params, account_id);
   reconcilor->OnGaiaAccountsInCookieUpdated(
       {listed_account}, {}, GoogleServiceAuthError::AuthErrorNone());
 
diff --git a/components/signin/core/browser/fake_gaia_cookie_manager_service.cc b/components/signin/core/browser/fake_gaia_cookie_manager_service.cc
deleted file mode 100644
index a568d86..0000000
--- a/components/signin/core/browser/fake_gaia_cookie_manager_service.cc
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
-#include "base/bind.h"
-#include "base/strings/string_util.h"
-#include "base/strings/stringprintf.h"
-#include "components/signin/core/browser/list_accounts_test_utils.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "google_apis/gaia/gaia_constants.h"
-#include "google_apis/gaia/gaia_urls.h"
-#include "net/url_request/test_url_fetcher_factory.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "services/network/test/test_url_loader_factory.h"
-
-namespace {
-// Factory method to return a SharedURLLoaderFactory of our choosing.
-scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory(
-    scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory) {
-  return shared_url_loader_factory;
-}
-}  // namespace
-
-FakeGaiaCookieManagerService::FakeGaiaCookieManagerService(
-    OAuth2TokenService* token_service,
-    SigninClient* client)
-    : GaiaCookieManagerService(
-          token_service,
-          client,
-          base::BindRepeating(&SigninClient::GetURLLoaderFactory,
-                              base::Unretained(client))) {}
-
-FakeGaiaCookieManagerService::FakeGaiaCookieManagerService(
-    OAuth2TokenService* token_service,
-    SigninClient* client,
-    network::TestURLLoaderFactory* test_url_loader_factory)
-    : GaiaCookieManagerService(
-          token_service,
-          client,
-          base::BindRepeating(&GetSharedURLLoaderFactory,
-                              test_url_loader_factory->GetSafeWeakWrapper())),
-      test_url_loader_factory_(test_url_loader_factory) {}
-
-FakeGaiaCookieManagerService::~FakeGaiaCookieManagerService() {}
-
-void FakeGaiaCookieManagerService::SetListAccountsResponseHttpNotFound() {
-  signin::SetListAccountsResponseHttpNotFound(test_url_loader_factory_);
-}
-
-void FakeGaiaCookieManagerService::SetListAccountsResponseWebLoginRequired() {
-  signin::SetListAccountsResponseWebLoginRequired(test_url_loader_factory_);
-}
-
-void FakeGaiaCookieManagerService::SetListAccountsResponseWithParams(
-    const std::vector<signin::CookieParams>& params) {
-  signin::SetListAccountsResponseWithParams(params, test_url_loader_factory_);
-}
-
-void FakeGaiaCookieManagerService::SetListAccountsResponseNoAccounts() {
-  signin::SetListAccountsResponseNoAccounts(test_url_loader_factory_);
-}
-
-void FakeGaiaCookieManagerService::SetListAccountsResponseOneAccount(
-    const std::string& email,
-    const std::string& gaia_id) {
-  signin::SetListAccountsResponseOneAccount(email, gaia_id,
-                                            test_url_loader_factory_);
-}
-
-void FakeGaiaCookieManagerService::SetListAccountsResponseOneAccountWithParams(
-    const signin::CookieParams& params) {
-  signin::SetListAccountsResponseOneAccountWithParams(params,
-                                                      test_url_loader_factory_);
-}
-
-void FakeGaiaCookieManagerService::SetListAccountsResponseTwoAccounts(
-    const std::string& email1,
-    const std::string& gaia_id1,
-    const std::string& email2,
-    const std::string& gaia_id2) {
-  signin::SetListAccountsResponseTwoAccounts(email1, gaia_id1, email2, gaia_id2,
-                                             test_url_loader_factory_);
-}
diff --git a/components/signin/core/browser/fake_gaia_cookie_manager_service.h b/components/signin/core/browser/fake_gaia_cookie_manager_service.h
deleted file mode 100644
index 874987e..0000000
--- a/components/signin/core/browser/fake_gaia_cookie_manager_service.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-#ifndef COMPONENTS_SIGNIN_CORE_BROWSER_FAKE_GAIA_COOKIE_MANAGER_SERVICE_H_
-#define COMPONENTS_SIGNIN_CORE_BROWSER_FAKE_GAIA_COOKIE_MANAGER_SERVICE_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "base/memory/scoped_refptr.h"
-#include "components/signin/core/browser/gaia_cookie_manager_service.h"
-#include "components/signin/core/browser/list_accounts_test_utils.h"
-#include "services/network/test/test_url_loader_factory.h"
-
-class FakeGaiaCookieManagerService : public GaiaCookieManagerService {
- public:
-  // Convenience constructor overload which uses the SharedURLLoaderFactory from
-  // SigninClient.
-  FakeGaiaCookieManagerService(OAuth2TokenService* token_service,
-                               SigninClient* client);
-
-  // Constructor overload for tests that want to use a TestURLLoaderFactory for
-  // cookie related requests.
-  FakeGaiaCookieManagerService(
-      OAuth2TokenService* token_service,
-      SigninClient* client,
-      network::TestURLLoaderFactory* test_url_loader_factory);
-
-  ~FakeGaiaCookieManagerService() override;
-
-  void SetListAccountsResponseHttpNotFound();
-  void SetListAccountsResponseWebLoginRequired();
-  void SetListAccountsResponseWithParams(
-      const std::vector<signin::CookieParams>& params);
-
-  // Helper methods, equivalent to calling SetListAccountsResponseWithParams().
-  void SetListAccountsResponseNoAccounts();
-  void SetListAccountsResponseOneAccount(const std::string& email,
-                                         const std::string& gaia_id);
-  void SetListAccountsResponseOneAccountWithParams(
-      const signin::CookieParams& params);
-  void SetListAccountsResponseTwoAccounts(const std::string& email1,
-                                          const std::string& gaia_id1,
-                                          const std::string& email2,
-                                          const std::string& gaia_id2);
-
- private:
-  // Provides a fake response for calls to /ListAccounts.
-  // Owned by the client if passed in via the constructor that takes in this
-  // pointer; null otherwise.
-  network::TestURLLoaderFactory* test_url_loader_factory_ = nullptr;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeGaiaCookieManagerService);
-};
-
-#endif  // COMPONENTS_SIGNIN_CORE_BROWSER_FAKE_GAIA_COOKIE_MANAGER_SERVICE_H_
diff --git a/components/signin/core/browser/signin_manager_unittest.cc b/components/signin/core/browser/signin_manager_unittest.cc
index ef48ab2..8d5711d 100644
--- a/components/signin/core/browser/signin_manager_unittest.cc
+++ b/components/signin/core/browser/signin_manager_unittest.cc
@@ -22,8 +22,8 @@
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/device_id_helper.h"
 #include "components/signin/core/browser/fake_account_fetcher_service.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "components/signin/core/browser/signin_pref_names.h"
 #include "components/signin/core/browser/test_signin_client.h"
diff --git a/components/signin/ios/browser/account_consistency_service_unittest.mm b/components/signin/ios/browser/account_consistency_service_unittest.mm
index 39dbb9c3..89f7fcf 100644
--- a/components/signin/ios/browser/account_consistency_service_unittest.mm
+++ b/components/signin/ios/browser/account_consistency_service_unittest.mm
@@ -14,9 +14,8 @@
 #include "components/signin/core/browser/account_reconcilor.h"
 #include "components/signin/core/browser/account_reconcilor_delegate.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/fake_account_fetcher_service.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
+#include "components/signin/core/browser/fake_account_fetcher_service.h"
 #include "components/signin/core/browser/fake_signin_manager.h"
 #include "components/signin/core/browser/gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/signin_pref_names.h"
diff --git a/components/suggestions/suggestions_service_impl.cc b/components/suggestions/suggestions_service_impl.cc
index 3b86fce..0814503 100644
--- a/components/suggestions/suggestions_service_impl.cc
+++ b/components/suggestions/suggestions_service_impl.cc
@@ -445,8 +445,8 @@
   // Add Chrome experiment state to the request headers.
   // TODO: We should call AppendVariationHeaders with explicit
   // variations::SignedIn::kNo If the access_token is empty
-  variations::AppendVariationHeadersUnknownSignedIn(
-      url, variations::InIncognito::kNo, &resource_request->headers);
+  variations::AppendVariationsHeaderUnknownSignedIn(
+      url, variations::InIncognito::kNo, resource_request.get());
   if (!access_token.empty()) {
     resource_request->headers.SetHeader(
         "Authorization", base::StrCat({"Bearer ", access_token}));
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn
index 1d1fd31..a84bb0cf 100644
--- a/components/sync/BUILD.gn
+++ b/components/sync/BUILD.gn
@@ -170,6 +170,8 @@
     "driver/sync_token_status.h",
     "driver/sync_type_preference_provider.h",
     "driver/sync_user_settings.h",
+    "driver/sync_user_settings_impl.cc",
+    "driver/sync_user_settings_impl.h",
     "driver/sync_util.cc",
     "driver/sync_util.h",
     "driver/syncable_service_based_model_type_controller.cc",
@@ -813,6 +815,8 @@
     "driver/sync_api_component_factory_mock.h",
     "driver/sync_client_mock.cc",
     "driver/sync_client_mock.h",
+    "driver/sync_user_settings_mock.cc",
+    "driver/sync_user_settings_mock.h",
     "driver/test_sync_service.cc",
     "driver/test_sync_service.h",
     "driver/test_sync_user_settings.cc",
diff --git a/components/sync/base/model_type.h b/components/sync/base/model_type.h
index ba4472b..1b03052 100644
--- a/components/sync/base/model_type.h
+++ b/components/sync/base/model_type.h
@@ -228,7 +228,8 @@
 
 // User types, which are not user-controlled.
 constexpr ModelTypeSet AlwaysPreferredUserTypes() {
-  return ModelTypeSet(DEVICE_INFO, USER_CONSENTS);
+  return ModelTypeSet(DEVICE_INFO, USER_CONSENTS, SUPERVISED_USER_SETTINGS,
+                      SUPERVISED_USER_WHITELISTS);
 }
 
 // These are the user-selectable data types.
@@ -248,7 +249,8 @@
 // This is the subset of UserTypes() that have priority over other types.  These
 // types are synced before other user types and are never encrypted.
 constexpr ModelTypeSet PriorityUserTypes() {
-  return ModelTypeSet(DEVICE_INFO, PRIORITY_PREFERENCES);
+  return ModelTypeSet(DEVICE_INFO, PRIORITY_PREFERENCES,
+                      SUPERVISED_USER_SETTINGS, SUPERVISED_USER_WHITELISTS);
 }
 
 // Proxy types are placeholder types for handling implicitly enabling real
@@ -280,20 +282,6 @@
   return ControlTypes().Has(model_type);
 }
 
-// Core types are those data types used by sync's core functionality (i.e. not
-// user data types). These types are always enabled, and include ControlTypes().
-//
-// The set of all core types.
-constexpr ModelTypeSet CoreTypes() {
-  return ModelTypeSet(NIGORI, EXPERIMENTS, SUPERVISED_USER_SETTINGS,
-                      SYNCED_NOTIFICATIONS, SYNCED_NOTIFICATION_APP_INFO,
-                      SUPERVISED_USER_WHITELISTS);
-}
-// Those core types that have high priority (includes ControlTypes()).
-constexpr ModelTypeSet PriorityCoreTypes() {
-  return ModelTypeSet(NIGORI, EXPERIMENTS, SUPERVISED_USER_SETTINGS);
-}
-
 // Types that may commit data, but should never be included in a GetUpdates.
 constexpr ModelTypeSet CommitOnlyTypes() {
   return ModelTypeSet(USER_EVENTS, USER_CONSENTS);
diff --git a/components/sync/driver/data_type_manager_impl.cc b/components/sync/driver/data_type_manager_impl.cc
index 54aee02..ea180493 100644
--- a/components/sync/driver/data_type_manager_impl.cc
+++ b/components/sync/driver/data_type_manager_impl.cc
@@ -81,7 +81,7 @@
   if (context.reason == CONFIGURE_REASON_CATCH_UP)
     catch_up_in_progress_ = true;
 
-  desired_types.PutAll(CoreTypes());
+  desired_types.PutAll(ControlTypes());
 
   ModelTypeSet allowed_types = ControlTypes();
   // Add types with controllers.
@@ -343,7 +343,7 @@
 
 ModelTypeSet DataTypeManagerImpl::GetPriorityTypes() const {
   ModelTypeSet high_priority_types;
-  high_priority_types.PutAll(PriorityCoreTypes());
+  high_priority_types.PutAll(ControlTypes());
   high_priority_types.PutAll(PriorityUserTypes());
   return high_priority_types;
 }
diff --git a/components/sync/driver/glue/sync_backend_host_core.cc b/components/sync/driver/glue/sync_backend_host_core.cc
index 671423b8..ee27878 100644
--- a/components/sync/driver/glue/sync_backend_host_core.cc
+++ b/components/sync/driver/glue/sync_backend_host_core.cc
@@ -68,59 +68,6 @@
   }
 }
 
-// Relevant for UMA, do not change.
-enum class StringConsistency {
-  kBothEqual = 0,
-  kOnlyLhsEmpty = 1,
-  kOnlyRhsEmpty = 2,
-  kBothNonEmptyAndDifferent = 3,
-  kMaxValue = kBothNonEmptyAndDifferent
-};
-
-StringConsistency CompareStringsForConsistency(const std::string& lhs,
-                                               const std::string& rhs) {
-  if (lhs == rhs) {
-    return StringConsistency::kBothEqual;
-  }
-  if (lhs.empty()) {
-    return StringConsistency::kOnlyLhsEmpty;
-  }
-  if (rhs.empty()) {
-    return StringConsistency::kOnlyRhsEmpty;
-  }
-  return StringConsistency::kBothNonEmptyAndDifferent;
-}
-
-constexpr int GetStringConsistencyUmaBucket(
-    StringConsistency cache_guid_consistency,
-    StringConsistency birthday_consistency) {
-  return static_cast<int>(cache_guid_consistency) *
-             (static_cast<int>(StringConsistency::kMaxValue) + 1) +
-         static_cast<int>(birthday_consistency);
-}
-
-// Logs information to UMA to understand whether prefs are populated with
-// information identical to the Directory's value, for the fields that are
-// stored in both. We mostly care about cache GUID and store birthday.
-void RecordConsistencyBetweenDirectoryAndPrefs(
-    const syncable::Directory* directory,
-    const SyncEngine::InitParams& params) {
-  DCHECK(directory);
-
-  const StringConsistency cache_guid_consistency =
-      CompareStringsForConsistency(params.cache_guid, directory->cache_guid());
-  const StringConsistency birthday_consistency = CompareStringsForConsistency(
-      params.birthday, directory->store_birthday());
-
-  UMA_HISTOGRAM_ENUMERATION(
-      "Sync.DirectoryVsPrefsConsistency",
-      GetStringConsistencyUmaBucket(cache_guid_consistency,
-                                    birthday_consistency),
-      GetStringConsistencyUmaBucket(StringConsistency::kMaxValue,
-                                    StringConsistency::kMaxValue) +
-          1);
-}
-
 }  // namespace
 
 SyncBackendHostCore::SyncBackendHostCore(
@@ -404,15 +351,12 @@
   args.saved_nigori_state = std::move(params.saved_nigori_state);
   args.short_poll_interval = params.short_poll_interval;
   args.long_poll_interval = params.long_poll_interval;
+  args.cache_guid = params.cache_guid;
+  args.birthday = params.birthday;
+  args.bag_of_chips = params.bag_of_chips;
   sync_manager_->Init(&args);
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       this, "SyncDirectory", base::ThreadTaskRunnerHandle::Get());
-
-  const UserShare* user_share = sync_manager_->GetUserShare();
-  if (user_share) {  // Null in some tests.
-    RecordConsistencyBetweenDirectoryAndPrefs(user_share->directory.get(),
-                                              params);
-  }
 }
 
 void SyncBackendHostCore::DoUpdateCredentials(
diff --git a/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc b/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc
index f2c169c2f..abb668b 100644
--- a/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc
+++ b/components/sync/driver/sync_session_durations_metrics_recorder_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/timer/timer.h"
 #include "components/sync/driver/test_sync_service.h"
 #include "services/identity/public/cpp/identity_test_environment.h"
+#include "services/network/test/test_url_loader_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace syncer {
diff --git a/components/sync/driver/sync_user_settings_impl.cc b/components/sync/driver/sync_user_settings_impl.cc
new file mode 100644
index 0000000..a202962
--- /dev/null
+++ b/components/sync/driver/sync_user_settings_impl.cc
@@ -0,0 +1,161 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync/driver/sync_user_settings_impl.h"
+
+#include "base/metrics/histogram_macros.h"
+#include "components/sync/base/sync_prefs.h"
+#include "components/sync/driver/sync_service_crypto.h"
+
+namespace syncer {
+
+SyncUserSettingsImpl::SyncUserSettingsImpl(
+    SyncServiceCrypto* crypto,
+    SyncPrefs* prefs,
+    ModelTypeSet registered_types,
+    const base::RepeatingCallback<void(bool)>& sync_allowed_by_platform_changed)
+    : crypto_(crypto),
+      prefs_(prefs),
+      registered_types_(registered_types),
+      sync_allowed_by_platform_changed_cb_(sync_allowed_by_platform_changed) {
+  DCHECK(crypto_);
+  DCHECK(prefs_);
+}
+
+SyncUserSettingsImpl::~SyncUserSettingsImpl() = default;
+
+bool SyncUserSettingsImpl::IsSyncRequested() const {
+  return prefs_->IsSyncRequested();
+}
+
+void SyncUserSettingsImpl::SetSyncRequested(bool requested) {
+  prefs_->SetSyncRequested(requested);
+}
+
+bool SyncUserSettingsImpl::IsSyncAllowedByPlatform() const {
+  return sync_allowed_by_platform_;
+}
+
+void SyncUserSettingsImpl::SetSyncAllowedByPlatform(bool allowed) {
+  if (sync_allowed_by_platform_ == allowed) {
+    return;
+  }
+
+  sync_allowed_by_platform_ = allowed;
+
+  sync_allowed_by_platform_changed_cb_.Run(sync_allowed_by_platform_);
+}
+
+bool SyncUserSettingsImpl::IsFirstSetupComplete() const {
+  return prefs_->IsFirstSetupComplete();
+}
+
+void SyncUserSettingsImpl::SetFirstSetupComplete() {
+  prefs_->SetFirstSetupComplete();
+}
+
+bool SyncUserSettingsImpl::IsSyncEverythingEnabled() const {
+  return prefs_->HasKeepEverythingSynced();
+}
+
+ModelTypeSet SyncUserSettingsImpl::GetChosenDataTypes() const {
+  ModelTypeSet types = GetPreferredDataTypes();
+  types.RetainAll(UserSelectableTypes());
+  return types;
+}
+
+void SyncUserSettingsImpl::SetChosenDataTypes(bool sync_everything,
+                                              ModelTypeSet types) {
+  DCHECK(UserSelectableTypes().HasAll(types));
+
+  prefs_->SetDataTypesConfiguration(sync_everything, registered_types_, types);
+}
+
+bool SyncUserSettingsImpl::IsEncryptEverythingAllowed() const {
+  return crypto_->IsEncryptEverythingAllowed();
+}
+
+void SyncUserSettingsImpl::SetEncryptEverythingAllowed(bool allowed) {
+  crypto_->SetEncryptEverythingAllowed(allowed);
+}
+
+bool SyncUserSettingsImpl::IsEncryptEverythingEnabled() const {
+  return crypto_->IsEncryptEverythingEnabled();
+}
+
+void SyncUserSettingsImpl::EnableEncryptEverything() {
+  crypto_->EnableEncryptEverything();
+}
+
+bool SyncUserSettingsImpl::IsPassphraseRequired() const {
+  return crypto_->passphrase_required_reason() !=
+         REASON_PASSPHRASE_NOT_REQUIRED;
+}
+
+bool SyncUserSettingsImpl::IsPassphraseRequiredForDecryption() const {
+  // If there is an encrypted datatype enabled and we don't have the proper
+  // passphrase, we must prompt the user for a passphrase. The only way for the
+  // user to avoid entering their passphrase is to disable the encrypted types.
+  return IsEncryptedDatatypeEnabled() && IsPassphraseRequired();
+}
+
+bool SyncUserSettingsImpl::IsUsingSecondaryPassphrase() const {
+  return crypto_->IsUsingSecondaryPassphrase();
+}
+
+base::Time SyncUserSettingsImpl::GetExplicitPassphraseTime() const {
+  return crypto_->GetExplicitPassphraseTime();
+}
+
+PassphraseType SyncUserSettingsImpl::GetPassphraseType() const {
+  return crypto_->GetPassphraseType();
+}
+
+void SyncUserSettingsImpl::SetEncryptionPassphrase(
+    const std::string& passphrase) {
+  crypto_->SetEncryptionPassphrase(passphrase);
+}
+
+bool SyncUserSettingsImpl::SetDecryptionPassphrase(
+    const std::string& passphrase) {
+  DCHECK(IsPassphraseRequired())
+      << "SetDecryptionPassphrase must not be called when "
+         "IsPassphraseRequired() is false.";
+
+  DVLOG(1) << "Setting passphrase for decryption.";
+
+  bool result = crypto_->SetDecryptionPassphrase(passphrase);
+  UMA_HISTOGRAM_BOOLEAN("Sync.PassphraseDecryptionSucceeded", result);
+  return result;
+}
+
+ModelTypeSet SyncUserSettingsImpl::GetPreferredDataTypes() const {
+  ModelTypeSet types =
+      Union(prefs_->GetPreferredDataTypes(registered_types_), ControlTypes());
+  if (prefs_->IsLocalSyncEnabled()) {
+    types.Remove(APP_LIST);
+    types.Remove(USER_CONSENTS);
+    types.Remove(USER_EVENTS);
+  }
+  return types;
+}
+
+ModelTypeSet SyncUserSettingsImpl::GetEncryptedDataTypes() const {
+  return crypto_->GetEncryptedDataTypes();
+}
+
+bool SyncUserSettingsImpl::IsEncryptedDatatypeEnabled() const {
+  if (IsEncryptionPending())
+    return true;
+  const ModelTypeSet preferred_types = GetPreferredDataTypes();
+  const ModelTypeSet encrypted_types = GetEncryptedDataTypes();
+  DCHECK(encrypted_types.Has(PASSWORDS));
+  return !Intersection(preferred_types, encrypted_types).Empty();
+}
+
+bool SyncUserSettingsImpl::IsEncryptionPending() const {
+  return crypto_->encryption_pending();
+}
+
+}  // namespace syncer
diff --git a/components/sync/driver/sync_user_settings_impl.h b/components/sync/driver/sync_user_settings_impl.h
new file mode 100644
index 0000000..a962ade0
--- /dev/null
+++ b/components/sync/driver/sync_user_settings_impl.h
@@ -0,0 +1,73 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_DRIVER_SYNC_USER_SETTINGS_IMPL_H_
+#define COMPONENTS_SYNC_DRIVER_SYNC_USER_SETTINGS_IMPL_H_
+
+#include <string>
+
+#include "base/callback.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_user_settings.h"
+
+namespace syncer {
+
+class SyncPrefs;
+class SyncServiceCrypto;
+
+class SyncUserSettingsImpl : public SyncUserSettings {
+ public:
+  // Both |crypto| and |prefs| must not be null, and must outlive this object.
+  SyncUserSettingsImpl(SyncServiceCrypto* crypto,
+                       SyncPrefs* prefs,
+                       ModelTypeSet registered_types,
+                       const base::RepeatingCallback<void(bool)>&
+                           sync_allowed_by_platform_changed);
+  ~SyncUserSettingsImpl() override;
+
+  bool IsSyncRequested() const override;
+  void SetSyncRequested(bool requested) override;
+
+  bool IsSyncAllowedByPlatform() const override;
+  void SetSyncAllowedByPlatform(bool allowed) override;
+
+  bool IsFirstSetupComplete() const override;
+  void SetFirstSetupComplete() override;
+
+  bool IsSyncEverythingEnabled() const override;
+  ModelTypeSet GetChosenDataTypes() const override;
+  void SetChosenDataTypes(bool sync_everything, ModelTypeSet types) override;
+
+  bool IsEncryptEverythingAllowed() const override;
+  void SetEncryptEverythingAllowed(bool allowed) override;
+  bool IsEncryptEverythingEnabled() const override;
+  void EnableEncryptEverything() override;
+
+  ModelTypeSet GetEncryptedDataTypes() const override;
+  bool IsPassphraseRequired() const override;
+  bool IsPassphraseRequiredForDecryption() const override;
+  bool IsUsingSecondaryPassphrase() const override;
+  base::Time GetExplicitPassphraseTime() const override;
+  PassphraseType GetPassphraseType() const override;
+
+  void SetEncryptionPassphrase(const std::string& passphrase) override;
+  bool SetDecryptionPassphrase(const std::string& passphrase) override;
+
+  ModelTypeSet GetPreferredDataTypes() const;
+  bool IsEncryptedDatatypeEnabled() const;
+  bool IsEncryptionPending() const;
+
+ private:
+  SyncServiceCrypto* const crypto_;
+  SyncPrefs* const prefs_;
+  const ModelTypeSet registered_types_;
+  base::RepeatingCallback<void(bool)> sync_allowed_by_platform_changed_cb_;
+
+  // Whether sync is currently allowed on this platform.
+  bool sync_allowed_by_platform_ = true;
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_DRIVER_SYNC_USER_SETTINGS_IMPL_H_
diff --git a/components/sync/driver/sync_user_settings_mock.cc b/components/sync/driver/sync_user_settings_mock.cc
new file mode 100644
index 0000000..6a8720a0
--- /dev/null
+++ b/components/sync/driver/sync_user_settings_mock.cc
@@ -0,0 +1,13 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/sync/driver/sync_user_settings_mock.h"
+
+namespace syncer {
+
+SyncUserSettingsMock::SyncUserSettingsMock() = default;
+
+SyncUserSettingsMock::~SyncUserSettingsMock() = default;
+
+}  // namespace syncer
diff --git a/components/sync/driver/sync_user_settings_mock.h b/components/sync/driver/sync_user_settings_mock.h
new file mode 100644
index 0000000..117cbe3
--- /dev/null
+++ b/components/sync/driver/sync_user_settings_mock.h
@@ -0,0 +1,51 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_SYNC_DRIVER_SYNC_USER_SETTINGS_MOCK_H_
+#define COMPONENTS_SYNC_DRIVER_SYNC_USER_SETTINGS_MOCK_H_
+
+#include <string>
+
+#include "components/sync/driver/sync_user_settings.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace syncer {
+
+class SyncUserSettingsMock : public SyncUserSettings {
+ public:
+  SyncUserSettingsMock();
+  ~SyncUserSettingsMock() override;
+
+  MOCK_CONST_METHOD0(IsSyncRequested, bool());
+  MOCK_METHOD1(SetSyncRequested, void(bool));
+
+  MOCK_CONST_METHOD0(IsSyncAllowedByPlatform, bool());
+  MOCK_METHOD1(SetSyncAllowedByPlatform, void(bool));
+
+  MOCK_CONST_METHOD0(IsFirstSetupComplete, bool());
+  MOCK_METHOD0(SetFirstSetupComplete, void());
+
+  MOCK_CONST_METHOD0(IsSyncEverythingEnabled, bool());
+  MOCK_CONST_METHOD0(GetChosenDataTypes, syncer::ModelTypeSet());
+  MOCK_METHOD2(SetChosenDataTypes, void(bool, syncer::ModelTypeSet));
+
+  MOCK_CONST_METHOD0(IsEncryptEverythingAllowed, bool());
+  MOCK_METHOD1(SetEncryptEverythingAllowed, void(bool));
+  MOCK_CONST_METHOD0(IsEncryptEverythingEnabled, bool());
+  MOCK_METHOD0(EnableEncryptEverything, void());
+
+  MOCK_CONST_METHOD0(GetEncryptedDataTypes, syncer::ModelTypeSet());
+  MOCK_CONST_METHOD0(IsPassphraseRequired, bool());
+  MOCK_CONST_METHOD0(IsPassphraseRequiredForDecryption, bool());
+  MOCK_CONST_METHOD0(IsUsingSecondaryPassphrase, bool());
+  MOCK_CONST_METHOD0(GetExplicitPassphraseTime, base::Time());
+  MOCK_CONST_METHOD0(GetPassphraseType, syncer::PassphraseType());
+
+  MOCK_METHOD1(SetEncryptionPassphrase, void(const std::string&));
+  MOCK_METHOD1(SetDecryptionPassphrase, bool(const std::string&));
+};
+
+}  // namespace syncer
+
+#endif  // COMPONENTS_SYNC_DRIVER_SYNC_USER_SETTINGS_MOCK_H_
diff --git a/components/sync/engine/sync_engine.h b/components/sync/engine/sync_engine.h
index df8ed03f..db0c9d8 100644
--- a/components/sync/engine/sync_engine.h
+++ b/components/sync/engine/sync_engine.h
@@ -81,6 +81,7 @@
 
     // Non-authoritative values from prefs, to be compared with the Directory's
     // counterparts.
+    // TODO(crbug.com/923285): Consider making these the authoritative data.
     std::string cache_guid;
     std::string birthday;
     std::string bag_of_chips;
diff --git a/components/sync/engine/sync_manager.h b/components/sync/engine/sync_manager.h
index 514b05be..03387a5 100644
--- a/components/sync/engine/sync_manager.h
+++ b/components/sync/engine/sync_manager.h
@@ -250,6 +250,13 @@
     // Define the polling intervals. Must not be zero.
     base::TimeDelta short_poll_interval;
     base::TimeDelta long_poll_interval;
+
+    // Non-authoritative values from prefs, to be compared with the Directory's
+    // counterparts.
+    // TODO(crbug.com/923285): Consider making these the authoritative data.
+    std::string cache_guid;
+    std::string birthday;
+    std::string bag_of_chips;
   };
 
   // The state of sync the feature. If the user turned on sync explicitly, it
diff --git a/components/sync/engine_impl/sync_manager_impl.cc b/components/sync/engine_impl/sync_manager_impl.cc
index a35bf89..7bc09e21 100644
--- a/components/sync/engine_impl/sync_manager_impl.cc
+++ b/components/sync/engine_impl/sync_manager_impl.cc
@@ -71,6 +71,73 @@
   return sync_pb::SyncEnums::UNKNOWN_ORIGIN;
 }
 
+// Relevant for UMA, do not change.
+enum class StringConsistency {
+  kBothEqual = 0,
+  kOnlyLhsEmpty = 1,
+  kOnlyRhsEmpty = 2,
+  kBothNonEmptyAndDifferent = 3,
+  kMaxValue = kBothNonEmptyAndDifferent
+};
+
+StringConsistency CompareStringsForConsistency(const std::string& lhs,
+                                               const std::string& rhs) {
+  if (lhs == rhs) {
+    return StringConsistency::kBothEqual;
+  }
+  if (lhs.empty()) {
+    return StringConsistency::kOnlyLhsEmpty;
+  }
+  if (rhs.empty()) {
+    return StringConsistency::kOnlyRhsEmpty;
+  }
+  return StringConsistency::kBothNonEmptyAndDifferent;
+}
+
+constexpr int GetStringConsistencyUmaBucket(
+    StringConsistency cache_guid_consistency,
+    StringConsistency birthday_consistency) {
+  return static_cast<int>(cache_guid_consistency) *
+             (static_cast<int>(StringConsistency::kMaxValue) + 1) +
+         static_cast<int>(birthday_consistency);
+}
+
+// Logs information to UMA to understand whether prefs are populated with
+// information identical to the Directory's value, for the fields that are
+// stored in both. We mostly care about cache GUID and store birthday.
+void RecordConsistencyBetweenDirectoryAndPrefs(
+    syncable::DirOpenResult open_result,
+    const syncable::Directory* directory,
+    const SyncManager::InitArgs* args) {
+  DCHECK(directory);
+
+  std::string directory_cache_guid;
+  std::string directory_birthday;
+
+  // We mimic the directory being empty if it was just opened (OPENED_NEW),
+  // because that means a random cache GUID was just generated and it's not
+  // possible to match empty prefs.
+  DCHECK(open_result == syncable::OPENED_EXISTING ||
+         open_result == syncable::OPENED_NEW);
+  if (open_result == syncable::OPENED_EXISTING) {
+    directory_cache_guid = directory->cache_guid();
+    directory_birthday = directory->store_birthday();
+  }
+
+  const StringConsistency cache_guid_consistency =
+      CompareStringsForConsistency(args->cache_guid, directory_cache_guid);
+  const StringConsistency birthday_consistency =
+      CompareStringsForConsistency(args->birthday, directory_birthday);
+
+  UMA_HISTOGRAM_ENUMERATION(
+      "Sync.DirectoryVsPrefsConsistency",
+      GetStringConsistencyUmaBucket(cache_guid_consistency,
+                                    birthday_consistency),
+      GetStringConsistencyUmaBucket(StringConsistency::kMaxValue,
+                                    StringConsistency::kMaxValue) +
+          1);
+}
+
 }  // namespace
 
 SyncManagerImpl::SyncManagerImpl(
@@ -249,7 +316,7 @@
 
   DVLOG(1) << "Username: " << args->credentials.email;
   DVLOG(1) << "AccountId: " << args->credentials.account_id;
-  if (!OpenDirectory(args->credentials.account_id)) {
+  if (!OpenDirectory(args)) {
     NotifyInitializationFailure();
     LOG(ERROR) << "Sync manager initialization failed!";
     return;
@@ -408,21 +475,26 @@
   return connection_manager_->HasInvalidAuthToken();
 }
 
-bool SyncManagerImpl::OpenDirectory(const std::string& username) {
+bool SyncManagerImpl::OpenDirectory(const InitArgs* args) {
   DCHECK(!initialized_) << "Should only happen once";
 
+  const std::string& account_id = args->credentials.account_id;
+
   // Set before Open().
   change_observer_ = MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr());
   WeakHandle<syncable::TransactionObserver> transaction_observer(
       MakeWeakHandle(js_mutation_event_observer_.AsWeakPtr()));
 
   syncable::DirOpenResult open_result = syncable::NOT_INITIALIZED;
-  open_result = directory()->Open(username, this, transaction_observer);
-  if (open_result != syncable::OPENED) {
-    LOG(ERROR) << "Could not open share for:" << username;
+  open_result = directory()->Open(account_id, this, transaction_observer);
+  if (open_result != syncable::OPENED_NEW &&
+      open_result != syncable::OPENED_EXISTING) {
+    LOG(ERROR) << "Could not open share for:" << account_id;
     return false;
   }
 
+  RecordConsistencyBetweenDirectoryAndPrefs(open_result, directory(), args);
+
   // Unapplied datatypes (those that do not have initial sync ended set) get
   // re-downloaded during any configuration. But, it's possible for a datatype
   // to have a progress marker but not have initial sync ended yet, making
diff --git a/components/sync/engine_impl/sync_manager_impl.h b/components/sync/engine_impl/sync_manager_impl.h
index 4a6f7f4..5492da7 100644
--- a/components/sync/engine_impl/sync_manager_impl.h
+++ b/components/sync/engine_impl/sync_manager_impl.h
@@ -219,8 +219,8 @@
   bool VisiblePropertiesDiffer(const syncable::EntryKernelMutation& mutation,
                                Cryptographer* cryptographer) const;
 
-  // Open the directory named with |username|.
-  bool OpenDirectory(const std::string& username);
+  // Opens the directory.
+  bool OpenDirectory(const InitArgs* args);
 
   void RequestNudgeForDataTypes(const base::Location& nudge_location,
                                 ModelTypeSet type);
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor.cc b/components/sync/model_impl/client_tag_based_model_type_processor.cc
index b6bc204..f66a2f0 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor.cc
@@ -969,16 +969,17 @@
 
     return false;
   } else if (!HasClearAllDirective(model_type_state) &&
-             !bridge_->SupportsIncrementalUpdates()) {
-    // We receive empty updates (without clear all directive) from the server to
-    // indicate nothing changed. We can just ignore these updates for bridges
-    // that don't support incremental updates.
-    if (!updates.empty()) {
-      ReportError(ModelError(FROM_HERE,
-                             "Received an update without version watermark for "
-                             "bridge that does not support incremental "
-                             "updates"));
-    }
+             !bridge_->SupportsIncrementalUpdates() && !updates.empty()) {
+    // We receive an update without clear all directive from the server to
+    // indicate no data has changed. This contradicts with the list of updates
+    // being non-empty, the bridge cannot handle it and we need to fail here.
+    // (If the last condition does not hold true and the list of updates is
+    // empty, we still need to pass the empty update to the bridge because the
+    // progress marker might have changed.)
+    ReportError(ModelError(FROM_HERE,
+                           "Received a non-empty update without version "
+                           "watermark for bridge that does not support "
+                           "incremental updates"));
     return false;
   }
   return true;
diff --git a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
index ae7939b..5b90834 100644
--- a/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
+++ b/components/sync/model_impl/client_tag_based_model_type_processor_unittest.cc
@@ -222,7 +222,7 @@
   }
 
   void ModelReadyToSync() {
-    type_processor()->ModelReadyToSync(db().CreateMetadataBatch());
+    type_processor()->ModelReadyToSync(db()->CreateMetadataBatch());
   }
 
   void OnCommitDataLoaded() { bridge()->OnCommitDataLoaded(); }
@@ -331,7 +331,7 @@
 
   TestModelTypeSyncBridge* bridge() const { return bridge_.get(); }
 
-  const FakeModelTypeSyncBridge::Store& db() const { return bridge()->db(); }
+  FakeModelTypeSyncBridge::Store* db() const { return bridge()->mutable_db(); }
 
   MockModelTypeWorker* worker() const { return worker_; }
 
@@ -344,8 +344,10 @@
   void CheckPostConditions() { EXPECT_FALSE(expect_error_); }
 
   void OnReadyToConnect(std::unique_ptr<DataTypeActivationResponse> context) {
-    std::unique_ptr<MockModelTypeWorker> worker(
-        new MockModelTypeWorker(context->model_type_state, type_processor()));
+    model_type_state_ = context->model_type_state;
+    std::unique_ptr<MockModelTypeWorker> worker =
+        std::make_unique<MockModelTypeWorker>(model_type_state_,
+                                              type_processor());
     // Keep an unsafe pointer to the commit queue the processor will use.
     worker_ = worker.get();
     // The context contains a proxy to the processor, but this call is
@@ -359,8 +361,11 @@
     expect_error_ = false;
   }
 
+  sync_pb::ModelTypeState model_type_state() { return model_type_state_; }
+
  private:
   std::unique_ptr<TestModelTypeSyncBridge> bridge_;
+  sync_pb::ModelTypeState model_type_state_;
 
   // This sets SequencedTaskRunnerHandle on the current thread, which the type
   // processor will pick up as the sync task runner.
@@ -384,7 +389,7 @@
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
        ShouldExposePreviouslyTrackedAccountId) {
-  std::unique_ptr<MetadataBatch> metadata_batch = db().CreateMetadataBatch();
+  std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
   sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
   model_type_state.set_initial_sync_done(true);
   model_type_state.set_authenticated_account_id("PersistedAccountId");
@@ -408,8 +413,8 @@
   bridge()->WriteItem(kKey1, kValue1);
 
   // Has data, but no metadata, entity in the processor, or commit request.
-  EXPECT_EQ(1U, db().data_count());
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(1U, db()->data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 
@@ -420,11 +425,11 @@
 
   // Now have data and metadata for both items, as well as a commit request for
   // the local item.
-  EXPECT_EQ(2U, db().data_count());
-  EXPECT_EQ(2U, db().metadata_count());
+  EXPECT_EQ(2U, db()->data_count());
+  EXPECT_EQ(2U, db()->metadata_count());
   EXPECT_EQ(2U, ProcessorEntityCount());
-  EXPECT_EQ(1, db().GetMetadata(kKey1).sequence_number());
-  EXPECT_EQ(0, db().GetMetadata(kKey2).sequence_number());
+  EXPECT_EQ(1, db()->GetMetadata(kKey1).sequence_number());
+  EXPECT_EQ(0, db()->GetMetadata(kKey2).sequence_number());
   worker()->VerifyPendingCommits({{kHash1}});
 }
 
@@ -440,8 +445,8 @@
   EXPECT_EQ(1, bridge()->merge_call_count());
 
   // Should still have no data, metadata, or commit requests.
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 }
@@ -467,16 +472,16 @@
 
   // Write an item before sync connects.
   bridge()->WriteItem(kKey1, kValue1);
-  EXPECT_EQ(1U, db().data_count());
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
 
   // Check that data coming from sync is treated as a normal GetUpdates.
   OnSyncStarting();
   worker()->UpdateFromServer(kHash2, GenerateSpecifics(kKey2, kValue2));
   EXPECT_EQ(0, bridge()->merge_call_count());
   EXPECT_EQ(1, bridge()->apply_call_count());
-  EXPECT_EQ(2U, db().data_count());
-  EXPECT_EQ(2U, db().metadata_count());
+  EXPECT_EQ(2U, db()->data_count());
+  EXPECT_EQ(2U, db()->metadata_count());
 }
 
 // Test that an error during the merge is propagated to the error handler.
@@ -721,8 +726,8 @@
   EXPECT_EQ(kKey1, tag1_data.specifics.preference().name());
   EXPECT_EQ(kValue1, tag1_data.specifics.preference().value());
 
-  EXPECT_EQ(1U, db().metadata_count());
-  const EntityMetadata metadata = db().GetMetadata(kKey1);
+  EXPECT_EQ(1U, db()->metadata_count());
+  const EntityMetadata metadata = db()->GetMetadata(kKey1);
   EXPECT_TRUE(metadata.has_client_tag_hash());
   EXPECT_FALSE(metadata.has_server_id());
   EXPECT_FALSE(metadata.is_deleted());
@@ -735,8 +740,8 @@
 
   worker()->AckOnePendingCommit();
   EXPECT_FALSE(type_processor()->IsEntityUnsynced(kKey1));
-  EXPECT_EQ(1U, db().metadata_count());
-  const EntityMetadata acked_metadata = db().GetMetadata(kKey1);
+  EXPECT_EQ(1U, db()->metadata_count());
+  const EntityMetadata acked_metadata = db()->GetMetadata(kKey1);
   EXPECT_TRUE(acked_metadata.has_server_id());
   EXPECT_EQ(1, acked_metadata.sequence_number());
   EXPECT_EQ(1, acked_metadata.acked_sequence_number());
@@ -775,10 +780,10 @@
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
   ASSERT_FALSE(worker()->HasPendingCommitForHash(kHash3));
   ASSERT_TRUE(worker()->HasPendingCommitForHash(kHash1));
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->metadata_count());
   const EntityData& out_entity1 =
       worker()->GetLatestPendingCommitForHash(kHash1).entity.value();
-  const EntityMetadata metadata_v1 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
 
   EXPECT_EQ(kId1, out_entity1.id);
   EXPECT_NE(kHash3, out_entity1.client_tag_hash);
@@ -800,10 +805,10 @@
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
   ASSERT_FALSE(worker()->HasPendingCommitForHash(kHash3));
   ASSERT_TRUE(worker()->HasPendingCommitForHash(kHash1));
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->metadata_count());
   const EntityData& out_entity2 =
       worker()->GetLatestPendingCommitForHash(kHash1).entity.value();
-  const EntityMetadata metadata_v2 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
 
   EXPECT_EQ(kValue2, out_entity2.specifics.preference().value());
   // Should still see old cid1 value, override is not respected on update.
@@ -821,13 +826,13 @@
   InitializeToReadyState();
 
   bridge()->WriteItem(kKey1, kValue1);
-  ASSERT_EQ(1U, db().metadata_count());
+  ASSERT_EQ(1U, db()->metadata_count());
   worker()->VerifyPendingCommits({{kHash1}});
 
   const CommitRequestData& request_data_v1 =
       worker()->GetLatestPendingCommitForHash(kHash1);
   const EntityData& data_v1 = request_data_v1.entity.value();
-  const EntityMetadata metadata_v1 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
 
   worker()->AckOnePendingCommit();
   ASSERT_FALSE(type_processor()->IsEntityUnsynced(kKey1));
@@ -840,7 +845,7 @@
   ASSERT_NE(ctime, base::Time::Now());
 
   bridge()->WriteItem(kKey1, kValue2);
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->metadata_count());
   worker()->VerifyPendingCommits({{kHash1}});
 
   EXPECT_TRUE(type_processor()->IsEntityUnsynced(kKey1));
@@ -851,7 +856,7 @@
   const CommitRequestData& request_data_v2 =
       worker()->GetLatestPendingCommitForHash(kHash1);
   const EntityData& data_v2 = request_data_v2.entity.value();
-  const EntityMetadata metadata_v2 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
 
   // Test some of the relations between old and new commit requests.
   EXPECT_GT(request_data_v2.sequence_number, request_data_v1.sequence_number);
@@ -891,13 +896,13 @@
   InitializeToReadyState();
 
   bridge()->WriteItem(kKey1, kValue1);
-  ASSERT_EQ(1U, db().metadata_count());
+  ASSERT_EQ(1U, db()->metadata_count());
   worker()->VerifyPendingCommits({{kHash1}});
 
   const CommitRequestData& request_data_v1 =
       worker()->GetLatestPendingCommitForHash(kHash1);
   const EntityData& data_v1 = request_data_v1.entity.value();
-  const EntityMetadata metadata_v1 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
 
   ASSERT_TRUE(type_processor()->IsEntityUnsynced(kKey1));
   const base::Time ctime = type_processor()->GetEntityCreationTime(kKey1);
@@ -909,7 +914,7 @@
   ASSERT_NE(ctime, base::Time::Now());
 
   bridge()->WriteItem(kKey1, kValue2);
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->metadata_count());
   worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
 
   EXPECT_TRUE(type_processor()->IsEntityUnsynced(kKey1));
@@ -920,7 +925,7 @@
   const CommitRequestData& request_data_v2 =
       worker()->GetLatestPendingCommitForHash(kHash1);
   const EntityData& data_v2 = request_data_v2.entity.value();
-  const EntityMetadata metadata_v2 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
 
   // Test some of the relations between old and new commit requests.
   EXPECT_GT(request_data_v2.sequence_number, request_data_v1.sequence_number);
@@ -958,7 +963,7 @@
 TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldIgnoreRedundantLocalUpdate) {
   InitializeToReadyState();
   bridge()->WriteItem(kKey1, kValue1);
-  ASSERT_EQ(1U, db().metadata_count());
+  ASSERT_EQ(1U, db()->metadata_count());
   worker()->VerifyPendingCommits({{kHash1}});
 
   const base::Time ctime = type_processor()->GetEntityCreationTime(kKey1);
@@ -977,12 +982,12 @@
 TEST_F(ClientTagBasedModelTypeProcessorTest, ShouldProcessRemoteCreation) {
   InitializeToReadyState();
   worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
-  EXPECT_EQ(1U, db().data_count());
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
   EXPECT_EQ(1U, ProcessorEntityCount());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 
-  const EntityData& data = db().GetData(kKey1);
+  const EntityData& data = db()->GetData(kKey1);
   EXPECT_FALSE(data.id.empty());
   EXPECT_EQ(kKey1, data.specifics.preference().name());
   EXPECT_EQ(kValue1, data.specifics.preference().value());
@@ -991,7 +996,7 @@
   EXPECT_EQ(kKey1, data.non_unique_name);
   EXPECT_FALSE(data.is_deleted());
 
-  const EntityMetadata metadata = db().GetMetadata(kKey1);
+  const EntityMetadata metadata = db()->GetMetadata(kKey1);
   EXPECT_TRUE(metadata.has_client_tag_hash());
   EXPECT_TRUE(metadata.has_server_id());
   EXPECT_FALSE(metadata.is_deleted());
@@ -1018,8 +1023,8 @@
        ShouldIgnoreRemoteUpdatesWithUnexpectedClientTagHash) {
   InitializeToReadyState();
   worker()->UpdateFromServer(kHash2, GenerateSpecifics(kKey1, kValue1));
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
 }
 
@@ -1038,19 +1043,19 @@
 
   // Local add writes data and metadata; ack writes metadata again.
   WriteItemAndAck(kKey1, kValue1);
-  EXPECT_EQ(1U, db().data_change_count());
-  EXPECT_EQ(2U, db().metadata_change_count());
+  EXPECT_EQ(1U, db()->data_change_count());
+  EXPECT_EQ(2U, db()->metadata_change_count());
 
   // Redundant update from server doesn't write data but updates metadata.
   worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
-  EXPECT_EQ(1U, db().data_change_count());
-  EXPECT_EQ(3U, db().metadata_change_count());
+  EXPECT_EQ(1U, db()->data_change_count());
+  EXPECT_EQ(3U, db()->metadata_change_count());
 
   // A reflection (update already received) is ignored completely.
   worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1),
                              0 /* version_offset */);
-  EXPECT_EQ(1U, db().data_change_count());
-  EXPECT_EQ(3U, db().metadata_change_count());
+  EXPECT_EQ(1U, db()->data_change_count());
+  EXPECT_EQ(3U, db()->metadata_change_count());
 }
 
 // Tests locally deleting an acknowledged item.
@@ -1059,20 +1064,20 @@
   WriteItemAndAck(kKey1, kValue1);
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 
-  const EntityMetadata metadata_v1 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
   EXPECT_FALSE(metadata_v1.is_deleted());
   EXPECT_EQ(1, metadata_v1.sequence_number());
   EXPECT_EQ(1, metadata_v1.acked_sequence_number());
   EXPECT_EQ(1, metadata_v1.server_version());
 
   bridge()->DeleteItem(kKey1);
-  EXPECT_EQ(0U, db().data_count());
+  EXPECT_EQ(0U, db()->data_count());
   // Metadata is not removed until the commit response comes back.
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->metadata_count());
   EXPECT_EQ(1U, ProcessorEntityCount());
   worker()->VerifyPendingCommits({{kHash1}});
 
-  const EntityMetadata metadata_v2 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
   EXPECT_TRUE(metadata_v2.is_deleted());
   EXPECT_EQ(2, metadata_v2.sequence_number());
   EXPECT_EQ(1, metadata_v2.acked_sequence_number());
@@ -1080,12 +1085,12 @@
 
   // Ack the delete and check that the metadata is cleared.
   worker()->AckOnePendingCommit();
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
 
   // Create item again.
   WriteItemAndAck(kKey1, kValue1);
-  const EntityMetadata metadata_v3 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v3 = db()->GetMetadata(kKey1);
   EXPECT_FALSE(metadata_v3.is_deleted());
   EXPECT_EQ(1, metadata_v3.sequence_number());
   EXPECT_EQ(1, metadata_v3.acked_sequence_number());
@@ -1113,15 +1118,15 @@
   const CommitRequestData& data_v1 =
       worker()->GetLatestPendingCommitForHash(kHash1);
 
-  const EntityMetadata metadata_v1 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v1 = db()->GetMetadata(kKey1);
   EXPECT_FALSE(metadata_v1.is_deleted());
   EXPECT_EQ(1, metadata_v1.sequence_number());
   EXPECT_EQ(0, metadata_v1.acked_sequence_number());
   EXPECT_EQ(kUncommittedVersion, metadata_v1.server_version());
 
   bridge()->DeleteItem(kKey1);
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
   EXPECT_EQ(1U, ProcessorEntityCount());
   worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
 
@@ -1132,7 +1137,7 @@
   EXPECT_EQ(kUncommittedVersion, data_v2.base_version);
   EXPECT_TRUE(data_v2.entity->is_deleted());
 
-  const EntityMetadata metadata_v2 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v2 = db()->GetMetadata(kKey1);
   EXPECT_TRUE(metadata_v2.is_deleted());
   EXPECT_EQ(2, metadata_v2.sequence_number());
   EXPECT_EQ(0, metadata_v2.acked_sequence_number());
@@ -1140,11 +1145,11 @@
 
   // A response for the first commit doesn't change much.
   worker()->AckOnePendingCommit();
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
   EXPECT_EQ(1U, ProcessorEntityCount());
 
-  const EntityMetadata metadata_v3 = db().GetMetadata(kKey1);
+  const EntityMetadata metadata_v3 = db()->GetMetadata(kKey1);
   EXPECT_TRUE(metadata_v3.is_deleted());
   EXPECT_EQ(2, metadata_v3.sequence_number());
   EXPECT_EQ(1, metadata_v3.acked_sequence_number());
@@ -1152,7 +1157,7 @@
 
   worker()->AckOnePendingCommit();
   // The delete was acked so the metadata should now be cleared.
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
 }
 
@@ -1160,20 +1165,20 @@
   InitializeToReadyState();
   WriteItemAndAck(kKey1, kValue1);
   EXPECT_EQ(1U, ProcessorEntityCount());
-  EXPECT_EQ(1U, db().metadata_count());
-  EXPECT_EQ(1U, db().data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
+  EXPECT_EQ(1U, db()->data_count());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 
   worker()->TombstoneFromServer(kHash1);
   // Delete from server should clear the data and all the metadata.
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 
   // Create item again.
   WriteItemAndAck(kKey1, kValue1);
-  const EntityMetadata metadata = db().GetMetadata(kKey1);
+  const EntityMetadata metadata = db()->GetMetadata(kKey1);
   EXPECT_FALSE(metadata.is_deleted());
   EXPECT_EQ(1, metadata.sequence_number());
   EXPECT_EQ(1, metadata.acked_sequence_number());
@@ -1186,8 +1191,8 @@
        ShouldIgnoreLocalDeletionOfUnknownEntity) {
   InitializeToReadyState();
   bridge()->DeleteItem(kKey1);
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 }
@@ -1198,8 +1203,8 @@
        ShouldIgnoreRemoteDeletionOfUnknownEntity) {
   InitializeToReadyState();
   worker()->TombstoneFromServer(kHash1);
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, ProcessorEntityCount());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 }
@@ -1248,17 +1253,17 @@
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 
   bridge()->WriteItem(kKey1, kValue1);
-  EXPECT_EQ(1U, db().data_count());
-  EXPECT_EQ(1U, db().metadata_count());
-  const EntityMetadata metadata1 = db().GetMetadata(kKey1);
+  EXPECT_EQ(1U, db()->data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
+  const EntityMetadata metadata1 = db()->GetMetadata(kKey1);
 
   // There should be one commit request for this item only.
   worker()->VerifyPendingCommits({{kHash1}});
 
   bridge()->WriteItem(kKey2, kValue2);
-  EXPECT_EQ(2U, db().data_count());
-  EXPECT_EQ(2U, db().metadata_count());
-  const EntityMetadata metadata2 = db().GetMetadata(kKey2);
+  EXPECT_EQ(2U, db()->data_count());
+  EXPECT_EQ(2U, db()->metadata_count());
+  const EntityMetadata metadata2 = db()->GetMetadata(kKey2);
 
   // The second write should trigger another single-item commit request.
   worker()->VerifyPendingCommits({{kHash1}, {kHash2}});
@@ -1278,10 +1283,10 @@
        ShouldNotTreatMatchingChangesAsConflict) {
   InitializeToReadyState();
   EntitySpecifics specifics = bridge()->WriteItem(kKey1, kValue1);
-  EXPECT_EQ(1U, db().data_change_count());
-  EXPECT_EQ(kValue1, db().GetValue(kKey1));
-  EXPECT_EQ(1U, db().metadata_change_count());
-  EXPECT_EQ(kUncommittedVersion, db().GetMetadata(kKey1).server_version());
+  EXPECT_EQ(1U, db()->data_change_count());
+  EXPECT_EQ(kValue1, db()->GetValue(kKey1));
+  EXPECT_EQ(1U, db()->metadata_change_count());
+  EXPECT_EQ(kUncommittedVersion, db()->GetMetadata(kKey1).server_version());
   worker()->VerifyPendingCommits({{kHash1}});
   worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics});
 
@@ -1289,8 +1294,8 @@
   worker()->UpdateFromServer(kHash1, specifics);
 
   // Updated metadata but not data; no new commit request.
-  EXPECT_EQ(1U, db().data_change_count());
-  EXPECT_EQ(1, db().GetMetadata(kKey1).server_version());
+  EXPECT_EQ(1U, db()->data_change_count());
+  EXPECT_EQ(1, db()->GetMetadata(kKey1).server_version());
   worker()->VerifyPendingCommits({{kHash1}});
 }
 
@@ -1307,9 +1312,9 @@
   worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue3));
 
   // Updated metadata but not data; new commit request.
-  EXPECT_EQ(2U, db().data_change_count());
-  EXPECT_EQ(4U, db().metadata_change_count());
-  EXPECT_EQ(2, db().GetMetadata(kKey1).server_version());
+  EXPECT_EQ(2U, db()->data_change_count());
+  EXPECT_EQ(4U, db()->metadata_change_count());
+  EXPECT_EQ(2, db()->GetMetadata(kKey1).server_version());
   worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
   worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics2});
 }
@@ -1347,8 +1352,8 @@
   EXPECT_EQ(kKey1, tag1_data.specifics.preference().name());
   EXPECT_EQ(kValue1, tag1_data.specifics.preference().value());
 
-  EXPECT_EQ(1U, db().metadata_count());
-  const EntityMetadata metadata = db().GetMetadata(kKey1);
+  EXPECT_EQ(1U, db()->metadata_count());
+  const EntityMetadata metadata = db()->GetMetadata(kKey1);
   EXPECT_TRUE(metadata.has_client_tag_hash());
   EXPECT_TRUE(metadata.has_server_id());
   EXPECT_FALSE(metadata.is_deleted());
@@ -1368,10 +1373,10 @@
   worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue2));
 
   // Updated client data and metadata; no new commit request.
-  EXPECT_EQ(2U, db().data_change_count());
-  EXPECT_EQ(kValue2, db().GetValue(kKey1));
-  EXPECT_EQ(2U, db().metadata_change_count());
-  EXPECT_EQ(1, db().GetMetadata(kKey1).server_version());
+  EXPECT_EQ(2U, db()->data_change_count());
+  EXPECT_EQ(kValue2, db()->GetValue(kKey1));
+  EXPECT_EQ(2U, db()->metadata_change_count());
+  EXPECT_EQ(1, db()->GetMetadata(kKey1).server_version());
   worker()->VerifyPendingCommits({{kHash1}});
 }
 
@@ -1383,10 +1388,10 @@
   worker()->TombstoneFromServer(kHash1);
 
   // Updated client data and metadata; no new commit request.
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(0U, db().metadata_count());
-  EXPECT_EQ(2U, db().data_change_count());
-  EXPECT_EQ(2U, db().metadata_change_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
+  EXPECT_EQ(2U, db()->data_change_count());
+  EXPECT_EQ(2U, db()->metadata_change_count());
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -1397,10 +1402,10 @@
       ConflictResolution::UseNew(GenerateEntityData(kKey1, kValue3)));
 
   worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue2));
-  EXPECT_EQ(2U, db().data_change_count());
-  EXPECT_EQ(kValue3, db().GetValue(kKey1));
-  EXPECT_EQ(2U, db().metadata_change_count());
-  EXPECT_EQ(1, db().GetMetadata(kKey1).server_version());
+  EXPECT_EQ(2U, db()->data_change_count());
+  EXPECT_EQ(kValue3, db()->GetValue(kKey1));
+  EXPECT_EQ(2U, db()->metadata_change_count());
+  EXPECT_EQ(1, db()->GetMetadata(kKey1).server_version());
   worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
   worker()->VerifyNthPendingCommit(1, {kHash1},
                                    {GenerateSpecifics(kKey1, kValue3)});
@@ -1505,7 +1510,7 @@
   // Populate the bridge's metadata with some non-empty values for us to later
   // check that it hasn't been cleared.
   const std::string kTestEncryptionKeyName = "TestEncryptionKey";
-  ModelTypeState model_type_state(db().model_type_state());
+  ModelTypeState model_type_state(db()->model_type_state());
   model_type_state.set_encryption_key_name(kTestEncryptionKeyName);
   bridge()->mutable_db()->set_model_type_state(model_type_state);
 
@@ -1516,7 +1521,7 @@
   type_processor()->OnSyncStopping(CLEAR_METADATA);
   EXPECT_FALSE(type_processor()->IsTrackingMetadata());
   EXPECT_EQ(kTestEncryptionKeyName,
-            db().model_type_state().encryption_key_name());
+            db()->model_type_state().encryption_key_name());
 }
 
 // Test re-encrypt everything when desired encryption key changes.
@@ -1528,8 +1533,8 @@
   // Create another item and don't wait for its commit response.
   EntitySpecifics specifics2 = bridge()->WriteItem(kKey2, kValue2);
   worker()->VerifyPendingCommits({{kHash2}});
-  EXPECT_EQ(1U, db().GetMetadata(kKey1).sequence_number());
-  EXPECT_EQ(1U, db().GetMetadata(kKey2).sequence_number());
+  EXPECT_EQ(1U, db()->GetMetadata(kKey1).sequence_number());
+  EXPECT_EQ(1U, db()->GetMetadata(kKey2).sequence_number());
 
   // Receive notice that the account's desired encryption key has changed.
   worker()->UpdateWithEncryptionKey("k1");
@@ -1542,8 +1547,8 @@
   worker()->VerifyNthPendingCommit(1, {kHash1, kHash2},
                                    {specifics1, specifics2});
   // Sequence numbers in the store are updated.
-  EXPECT_EQ(2U, db().GetMetadata(kKey1).sequence_number());
-  EXPECT_EQ(2U, db().GetMetadata(kKey2).sequence_number());
+  EXPECT_EQ(2U, db()->GetMetadata(kKey1).sequence_number());
+  EXPECT_EQ(2U, db()->GetMetadata(kKey2).sequence_number());
 }
 
 // Test that an error loading pending commit data for re-encryption is
@@ -1610,7 +1615,7 @@
   // Ensure the re-commit has the correct value.
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
   worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics});
-  EXPECT_EQ(kValue2, db().GetValue(kKey1));
+  EXPECT_EQ(kValue2, db()->GetValue(kKey1));
 
   // GetData was launched as a result of GetLocalChanges call(). Since the
   // conflict resolution encrypted all entities, no data is required.
@@ -1635,7 +1640,7 @@
   // Ensure the re-commit has the correct value.
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
   worker()->VerifyNthPendingCommit(1, {kHash1}, {specifics});
-  EXPECT_EQ(kValue2, db().GetValue(kKey1));
+  EXPECT_EQ(kValue2, db()->GetValue(kKey1));
 }
 
 // Test that re-encrypting enqueues the right data for USE_NEW conflicts.
@@ -1654,7 +1659,7 @@
   EXPECT_EQ(2U, worker()->GetNumPendingCommits());
   worker()->VerifyNthPendingCommit(1, {kHash1},
                                    {GenerateSpecifics(kKey1, kValue3)});
-  EXPECT_EQ(kValue3, db().GetValue(kKey1));
+  EXPECT_EQ(kValue3, db()->GetValue(kKey1));
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -1673,7 +1678,7 @@
   // Ensure the re-commit has the correct value.
   EXPECT_EQ(1U, worker()->GetNumPendingCommits());
   worker()->VerifyNthPendingCommit(0, {kHash1}, {specifics});
-  EXPECT_EQ(kValue2, db().GetValue(kKey1));
+  EXPECT_EQ(kValue2, db()->GetValue(kKey1));
 
   // Data load completing should add no commit requests.
   OnCommitDataLoaded();
@@ -1706,8 +1711,8 @@
   worker()->UpdateFromServer(updates);
 
   // Verify that the data wasn't actually stored.
-  EXPECT_EQ(0U, db().metadata_count());
-  EXPECT_EQ(0U, db().data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
 }
 
 // Tests that initial updates for ephemeral storage result in reporting setup
@@ -1778,8 +1783,8 @@
 
   // Verify entries are created correctly.
   ASSERT_EQ(2U, ProcessorEntityCount());
-  ASSERT_EQ(2U, db().metadata_count());
-  ASSERT_EQ(2U, db().data_count());
+  ASSERT_EQ(2U, db()->metadata_count());
+  ASSERT_EQ(2U, db()->data_count());
   ASSERT_EQ(0U, worker()->GetNumPendingCommits());
   ASSERT_EQ(1, bridge()->merge_call_count());
 
@@ -1791,7 +1796,7 @@
   // Verify that merge is called on the bridge to replace the current sync data.
   EXPECT_EQ(2, bridge()->merge_call_count());
   // Verify that the processor cleared all metadata.
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 }
 
@@ -1844,17 +1849,29 @@
   worker()->UpdateFromServer(kHash1, GenerateSpecifics(kKey1, kValue1));
 }
 
-// Tests that empty updates without a version GC are ignored for types that
-// don't support incremental updates.
+// Tests that empty updates without a version GC are processed for types that
+// don't support incremental updates. The only outcome if these updates should
+// be storing an updated progress marker.
 TEST_F(FullUpdateClientTagBasedModelTypeProcessorTest,
-       ShouldIgnoreEmptyUpdate) {
+       ShouldProcessEmptyUpdate) {
+  // Override the initial progress marker token so that we can check it gets
+  // changed.
+  sync_pb::ModelTypeState initial_model_type_state(db()->model_type_state());
+  initial_model_type_state.mutable_progress_marker()->set_token("OLD");
+  db()->set_model_type_state(initial_model_type_state);
+
   InitializeToReadyState();
 
+  sync_pb::ModelTypeState new_model_type_state(db()->model_type_state());
+  new_model_type_state.mutable_progress_marker()->set_token("NEW");
+  worker()->UpdateModelTypeState(new_model_type_state);
   worker()->UpdateFromServer(UpdateResponseDataList());
 
-  // Verify that the empty update was ignored in the processor.
+  // Verify that the empty update was correctly passed into the bridge and that
+  // it stored the updated progress marker.
   EXPECT_EQ(0, bridge()->merge_call_count());
-  EXPECT_EQ(0, bridge()->apply_call_count());
+  EXPECT_EQ(1, bridge()->apply_call_count());
+  EXPECT_EQ("NEW", db()->model_type_state().progress_marker().token());
 }
 
 // Tests that the processor correctly handles an initial (non-empty) update
@@ -1892,9 +1909,9 @@
   worker()->UpdateWithGarbageCollection(updates, garbage_collection_directive);
 
   // Verify that the data was stored.
-  EXPECT_EQ(1U, db().metadata_count());
-  EXPECT_EQ(1U, db().data_count());
-  EXPECT_FALSE(db().GetMetadata(kKey1).client_tag_hash().empty());
+  EXPECT_EQ(1U, db()->metadata_count());
+  EXPECT_EQ(1U, db()->data_count());
+  EXPECT_FALSE(db()->GetMetadata(kKey1).client_tag_hash().empty());
 }
 
 // Tests that a real local change wins over a remote encryption-only change.
@@ -1957,22 +1974,22 @@
   EXPECT_EQ(1U, ProcessorEntityCount());
   // Metadata should be written under kKey1. This means that UpdateStorageKey
   // was called and value of storage key got propagated to MetadataChangeList.
-  EXPECT_TRUE(db().HasMetadata(kKey1));
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_TRUE(db()->HasMetadata(kKey1));
+  EXPECT_EQ(1U, db()->metadata_count());
   EXPECT_EQ(0, bridge()->get_storage_key_call_count());
 
   // Local update of kKey1 should affect the same entity. This ensures that
   // storage key to client tag hash mapping was updated on the previous step.
   bridge()->WriteItem(kKey1, kValue2);
   EXPECT_EQ(1U, ProcessorEntityCount());
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->metadata_count());
 
   // Second update from server should be handled by ApplySyncChanges. Similarly
   // It should call UpdateStorageKey, not GetStorageKey.
   worker()->UpdateFromServer(kHash2, GenerateSpecifics(kKey2, kValue2));
   EXPECT_EQ(1, bridge()->apply_call_count());
-  EXPECT_TRUE(db().HasMetadata(kKey2));
-  EXPECT_EQ(2U, db().metadata_count());
+  EXPECT_TRUE(db()->HasMetadata(kKey2));
+  EXPECT_EQ(2U, db()->metadata_count());
   EXPECT_EQ(0, bridge()->get_storage_key_call_count());
 }
 
@@ -2011,8 +2028,8 @@
   // Metadata should not be written under kUntrackKey1. This means that
   // UntrackEntity was called and corresponding ProcessorEntityTracker is
   // removed and no storage key got propagated to MetadataChangeList.
-  EXPECT_FALSE(db().HasMetadata(kHash1));
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_FALSE(db()->HasMetadata(kHash1));
+  EXPECT_EQ(0U, db()->metadata_count());
   EXPECT_EQ(0, bridge()->get_storage_key_call_count());
 }
 
@@ -2097,8 +2114,8 @@
 
   // Verify entries are created correctly.
   EXPECT_EQ(2U, ProcessorEntityCount());
-  EXPECT_EQ(2U, db().metadata_count());
-  EXPECT_EQ(2U, db().data_count());
+  EXPECT_EQ(2U, db()->metadata_count());
+  EXPECT_EQ(2U, db()->data_count());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 
   // Expired the entries which are older than 10 days.
@@ -2107,8 +2124,8 @@
   worker()->UpdateWithGarbageCollection(garbage_collection_directive);
 
   EXPECT_EQ(1U, ProcessorEntityCount());
-  EXPECT_EQ(1U, db().metadata_count());
-  EXPECT_EQ(2U, db().data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
+  EXPECT_EQ(2U, db()->data_count());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 }
 
@@ -2138,8 +2155,8 @@
 
   // Verify entries are created correctly.
   EXPECT_EQ(3U, ProcessorEntityCount());
-  EXPECT_EQ(3U, db().metadata_count());
-  EXPECT_EQ(3U, db().data_count());
+  EXPECT_EQ(3U, db()->metadata_count());
+  EXPECT_EQ(3U, db()->data_count());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 
   // Expired the entries which are older than 10 days.
@@ -2148,8 +2165,8 @@
   worker()->UpdateWithGarbageCollection(garbage_collection_directive);
 
   EXPECT_EQ(2U, ProcessorEntityCount());
-  EXPECT_EQ(2U, db().metadata_count());
-  EXPECT_EQ(3U, db().data_count());
+  EXPECT_EQ(2U, db()->metadata_count());
+  EXPECT_EQ(3U, db()->data_count());
   EXPECT_EQ(0U, worker()->GetNumPendingCommits());
 }
 
@@ -2164,7 +2181,7 @@
   // A new processor loads the metadata after changing the cache GUID.
   bridge()->SetInitialSyncDone(true);
 
-  std::unique_ptr<MetadataBatch> metadata_batch = db().CreateMetadataBatch();
+  std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
   sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
   model_type_state.set_cache_guid("WRONG_CACHE_GUID");
   metadata_batch->SetModelTypeState(model_type_state);
@@ -2179,7 +2196,7 @@
   // OnSyncStarting() should have completed.
   EXPECT_NE(nullptr, worker());
   // Upon a mismatch, metadata should have been cleared.
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->metadata_count());
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -2197,7 +2214,7 @@
   // A new processor loads the metadata after changing the cache GUID.
   bridge()->SetInitialSyncDone(true);
 
-  std::unique_ptr<MetadataBatch> metadata_batch = db().CreateMetadataBatch();
+  std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
   sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
   model_type_state.set_cache_guid("WRONG_CACHE_GUID");
   metadata_batch->SetModelTypeState(model_type_state);
@@ -2212,7 +2229,7 @@
   // OnSyncStarting() should NOT have completed.
   EXPECT_EQ(nullptr, worker());
   // Upon a mismatch, metadata should have been cleared.
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->metadata_count());
 
   // Calling ModelReadyToSync() should complete OnSyncStarting().
   type_processor()->ModelReadyToSync(std::make_unique<MetadataBatch>());
@@ -2227,8 +2244,8 @@
   // Loose the entity in the bridge (keeping the metadata around as an orphan).
   bridge()->MimicBugToLooseItemWithoutNotifyingProcessor(kKey1);
 
-  ASSERT_FALSE(db().HasData(kKey1));
-  ASSERT_TRUE(db().HasMetadata(kKey1));
+  ASSERT_FALSE(db()->HasData(kKey1));
+  ASSERT_TRUE(db()->HasMetadata(kKey1));
   ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
 
   // Reset "the browser" so that the processor looses the copy of the data.
@@ -2248,7 +2265,7 @@
 
   // Orphan metadata should have been deleted.
   EXPECT_EQ(1, bridge()->apply_call_count());
-  EXPECT_FALSE(db().HasMetadata(kKey1));
+  EXPECT_FALSE(db()->HasMetadata(kKey1));
   EXPECT_EQ(nullptr, GetEntityForStorageKey(kKey1));
 
   base::HistogramTester histogram_tester;
@@ -2276,8 +2293,8 @@
   // Loose the entity in the bridge (keeping the metadata around as an orphan).
   bridge()->MimicBugToLooseItemWithoutNotifyingProcessor(kKey1);
 
-  ASSERT_FALSE(db().HasData(kKey1));
-  ASSERT_TRUE(db().HasMetadata(kKey1));
+  ASSERT_FALSE(db()->HasData(kKey1));
+  ASSERT_TRUE(db()->HasMetadata(kKey1));
   ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
 
   // Reset "the browser" so that the processor looses the copy of the data.
@@ -2303,7 +2320,7 @@
 
   // The expectation below documents the fact that bridges are responsible for
   // clearing the untracked metadata from their databases.
-  EXPECT_TRUE(db().HasMetadata(kKey1));
+  EXPECT_TRUE(db()->HasMetadata(kKey1));
 }
 
 TEST_F(ClientTagBasedModelTypeProcessorTest,
@@ -2314,8 +2331,8 @@
   // Loose the entity in the bridge (keeping the metadata around as an orphan).
   bridge()->MimicBugToLooseItemWithoutNotifyingProcessor(kKey1);
 
-  ASSERT_FALSE(db().HasData(kKey1));
-  ASSERT_TRUE(db().HasMetadata(kKey1));
+  ASSERT_FALSE(db()->HasData(kKey1));
+  ASSERT_TRUE(db()->HasMetadata(kKey1));
   ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
 
   // Reset "the browser" so that the processor looses the copy of the data.
@@ -2345,8 +2362,8 @@
   InitializeToReadyState();
   bridge()->WriteItem(kKey1, kValue1);
 
-  ASSERT_TRUE(db().HasData(kKey1));
-  ASSERT_TRUE(db().HasMetadata(kKey1));
+  ASSERT_TRUE(db()->HasData(kKey1));
+  ASSERT_TRUE(db()->HasMetadata(kKey1));
   ASSERT_NE(nullptr, GetEntityForStorageKey(kKey1));
 
   // Reset "the browser" so that the processor looses the copy of the data.
@@ -2368,8 +2385,8 @@
   histogram_tester.ExpectTotalCount("Sync.ModelTypeOrphanMetadata",
                                     /*count=*/0);
 
-  EXPECT_TRUE(db().HasData(kKey1));
-  EXPECT_TRUE(db().HasMetadata(kKey1));
+  EXPECT_TRUE(db()->HasData(kKey1));
+  EXPECT_TRUE(db()->HasMetadata(kKey1));
   EXPECT_NE(nullptr, GetEntityForStorageKey(kKey1));
 }
 
@@ -2389,7 +2406,7 @@
 
 TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
        ShouldExposePreviouslyTrackedAccountId) {
-  std::unique_ptr<MetadataBatch> metadata_batch = db().CreateMetadataBatch();
+  std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
   sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
   model_type_state.set_initial_sync_done(true);
   model_type_state.set_authenticated_account_id("PersistedAccountId");
@@ -2415,7 +2432,7 @@
 
 TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
        ShouldNotCallMergeAfterRestart) {
-  std::unique_ptr<MetadataBatch> metadata_batch = db().CreateMetadataBatch();
+  std::unique_ptr<MetadataBatch> metadata_batch = db()->CreateMetadataBatch();
   sync_pb::ModelTypeState model_type_state(metadata_batch->GetModelTypeState());
   model_type_state.set_initial_sync_done(true);
   model_type_state.set_authenticated_account_id("PersistedAccountId");
@@ -2434,16 +2451,16 @@
 TEST_F(CommitOnlyClientTagBasedModelTypeProcessorTest,
        ShouldCommitAndDeleteWhenAcked) {
   InitializeToReadyState();
-  EXPECT_TRUE(db().model_type_state().initial_sync_done());
+  EXPECT_TRUE(db()->model_type_state().initial_sync_done());
 
   bridge()->WriteItem(kKey1, kValue1);
   worker()->VerifyPendingCommits({{kHash1}});
-  EXPECT_EQ(1U, db().data_count());
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
 
   worker()->AckOnePendingCommit();
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
 }
 
 // Test that commit only types maintain tracking of entities while unsynced
@@ -2454,25 +2471,25 @@
 
   bridge()->WriteItem(kKey1, kValue1);
   worker()->VerifyPendingCommits({{kHash1}});
-  EXPECT_EQ(1U, db().data_count());
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
 
   bridge()->WriteItem(kKey1, kValue2);
   worker()->VerifyPendingCommits({{kHash1}, {kHash1}});
-  EXPECT_EQ(1U, db().data_count());
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
 
   worker()->AckOnePendingCommit();
   worker()->VerifyPendingCommits({{kHash1}});
-  EXPECT_EQ(1U, db().data_count());
-  EXPECT_EQ(1U, db().metadata_count());
+  EXPECT_EQ(1U, db()->data_count());
+  EXPECT_EQ(1U, db()->metadata_count());
 
   // The version field isn't meaningful on commit only types, so force a value
   // that isn't incremented to verify everything still works.
   worker()->AckOnePendingCommit(0 /* version_offset */);
   worker()->VerifyPendingCommits({});
-  EXPECT_EQ(0U, db().data_count());
-  EXPECT_EQ(0U, db().metadata_count());
+  EXPECT_EQ(0U, db()->data_count());
+  EXPECT_EQ(0U, db()->metadata_count());
 }
 
 }  // namespace syncer
diff --git a/components/sync/syncable/dir_open_result.h b/components/sync/syncable/dir_open_result.h
index e408889..09adf18 100644
--- a/components/sync/syncable/dir_open_result.h
+++ b/components/sync/syncable/dir_open_result.h
@@ -10,7 +10,8 @@
 
 enum DirOpenResult {
   NOT_INITIALIZED,
-  OPENED,                     // success.
+  OPENED_NEW,                 // success (created from scratch).
+  OPENED_EXISTING,            // success (previously existing).
   FAILED_NEWER_VERSION,       // DB version is too new.
   FAILED_MAKE_REPOSITORY,     // Couldn't create subdir.
   FAILED_OPEN_DATABASE,       // sqlite_open() failed.
diff --git a/components/sync/syncable/directory.cc b/components/sync/syncable/directory.cc
index d477c023..1c477bc 100644
--- a/components/sync/syncable/directory.cc
+++ b/components/sync/syncable/directory.cc
@@ -136,7 +136,7 @@
 
   const DirOpenResult result = OpenImpl(name, delegate, transaction_observer);
 
-  if (OPENED != result)
+  if (OPENED_NEW != result && OPENED_EXISTING != result)
     Close();
   return result;
 }
@@ -191,7 +191,7 @@
 
   DirOpenResult result = store_->Load(&tmp_handles_map, delete_journals.get(),
                                       &metahandles_to_purge, &info);
-  if (OPENED != result)
+  if (OPENED_NEW != result && OPENED_EXISTING != result)
     return result;
 
   DCHECK(!kernel_);
@@ -211,7 +211,7 @@
   store_->SetCatastrophicErrorHandler(base::Bind(
       &Directory::OnCatastrophicError, weak_ptr_factory_.GetWeakPtr()));
 
-  return OPENED;
+  return result;
 }
 
 DeleteJournal* Directory::delete_journal() {
diff --git a/components/sync/syncable/directory_backing_store.cc b/components/sync/syncable/directory_backing_store.cc
index d8f9ba6..d44d1d55 100644
--- a/components/sync/syncable/directory_backing_store.cc
+++ b/components/sync/syncable/directory_backing_store.cc
@@ -429,7 +429,9 @@
   return db_->OpenInMemory();
 }
 
-bool DirectoryBackingStore::InitializeTables() {
+bool DirectoryBackingStore::InitializeTables(bool* did_start_new) {
+  *did_start_new = false;
+
   if (!UpdatePageSizeIfNecessary())
     return false;
 
@@ -442,6 +444,8 @@
     DropAllTables();
     if (!CreateTables())
       return false;
+
+    *did_start_new = true;
   }
 
   int version_on_disk = GetVersion();
diff --git a/components/sync/syncable/directory_backing_store.h b/components/sync/syncable/directory_backing_store.h
index bb9b107..47b4546 100644
--- a/components/sync/syncable/directory_backing_store.h
+++ b/components/sync/syncable/directory_backing_store.h
@@ -127,7 +127,10 @@
   bool OpenInMemory();
 
   // Initialize database tables. Return true on success, false on error.
-  bool InitializeTables();
+  // |did_start_new| must not be null and allows callers to know whether a
+  // previously existing directory was opened or a new empty one had to be
+  // initialized.
+  bool InitializeTables(bool* did_start_new);
 
   // Load helpers for entries and attributes. Return true on success, false on
   // error.
diff --git a/components/sync/syncable/directory_backing_store_unittest.cc b/components/sync/syncable/directory_backing_store_unittest.cc
index 3a353399..9daf959 100644
--- a/components/sync/syncable/directory_backing_store_unittest.cc
+++ b/components/sync/syncable/directory_backing_store_unittest.cc
@@ -66,8 +66,9 @@
     JournalIndex delete_journals;
     MetahandleSet metahandles_to_purge;
     Directory::KernelLoadInfo kernel_load_info;
-    return dbs->Load(&tmp_handles_map, &delete_journals, &metahandles_to_purge,
-                     &kernel_load_info) == OPENED;
+    DirOpenResult result = dbs->Load(&tmp_handles_map, &delete_journals,
+                                     &metahandles_to_purge, &kernel_load_info);
+    return result == OPENED_NEW || result == OPENED_EXISTING;
   }
 
   void SetUpCorruptedRootDatabase(sql::Database* connection);
@@ -3959,8 +3960,8 @@
 
   {
     OnDiskDirectoryBackingStore dbs(GetUsername(), GetDatabasePath());
-    ASSERT_EQ(OPENED, dbs.Load(&handles_map, &delete_journals,
-                               &metahandles_to_purge, &dir_info));
+    ASSERT_EQ(OPENED_EXISTING, dbs.Load(&handles_map, &delete_journals,
+                                        &metahandles_to_purge, &dir_info));
     if (!metahandles_to_purge.empty())
       dbs.DeleteEntries(DirectoryBackingStore::METAS_TABLE,
                         metahandles_to_purge);
@@ -4399,7 +4400,7 @@
 
   DirOpenResult open_result = dbs.Load(
       &handles_map, &delete_journals, &metahandles_to_purge, &kernel_load_info);
-  EXPECT_EQ(open_result, OPENED);
+  EXPECT_EQ(open_result, OPENED_EXISTING);
 
   // Set up database's page size to 4096
   EXPECT_TRUE(dbs.db_->Execute("PRAGMA page_size=4096;"));
diff --git a/components/sync/syncable/directory_unittest.cc b/components/sync/syncable/directory_unittest.cc
index de27121..d2e7395 100644
--- a/components/sync/syncable/directory_unittest.cc
+++ b/components/sync/syncable/directory_unittest.cc
@@ -63,7 +63,7 @@
 
 void SyncableDirectoryTest::SetUp() {
   ASSERT_TRUE(connection_.OpenInMemory());
-  ASSERT_EQ(OPENED, ReopenDirectory());
+  ASSERT_EQ(OPENED_NEW, ReopenDirectory());
 }
 
 void SyncableDirectoryTest::TearDown() {
@@ -83,7 +83,7 @@
   DirOpenResult open_result =
       dir_->Open(kDirectoryName, &delegate_, NullTransactionObserver());
 
-  if (open_result != OPENED) {
+  if (open_result != OPENED_NEW && open_result != OPENED_EXISTING) {
     dir_.reset();
   }
 
@@ -453,7 +453,7 @@
       item3.PutIsUnsynced(true);
       handle3 = item3.GetMetahandle();
     }
-    ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
+    ASSERT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
   }
 
   {  // Test adding and saving delete journals.
@@ -492,7 +492,7 @@
       ReadTransaction trans(FROM_HERE, dir().get());
       EXPECT_EQ(0u, delete_journal->GetDeleteJournalSize(&trans));
     }
-    ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
+    ASSERT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
   }
 
   {
@@ -523,7 +523,7 @@
       EXPECT_EQ(1u, delete_journal->delete_journals_to_purge_.size());
       EXPECT_TRUE(delete_journal->delete_journals_to_purge_.count(handle2));
     }
-    ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
+    ASSERT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
   }
 
   {
@@ -548,7 +548,7 @@
       EXPECT_EQ(1u, delete_journal->delete_journals_to_purge_.size());
       EXPECT_TRUE(delete_journal->delete_journals_to_purge_.count(handle1));
     }
-    ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
+    ASSERT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
   }
 
   {
@@ -615,7 +615,7 @@
         item.PutIsUnappliedUpdate(true);
     }
   }
-  ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
+  ASSERT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
 
   // Expect items 0 and 5 to be purged according to
   // DirectoryBackingStore::SafeToPurgeOnLoading:
@@ -1127,7 +1127,7 @@
   }
 
   // Final check for validity.
-  EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
+  EXPECT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
 }
 
 // A test that roughly mimics the directory interaction that occurs when a
@@ -1169,7 +1169,7 @@
   }
 
   // Final check for validity.
-  EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
+  EXPECT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
 
   // Verify that child's PARENT_ID hasn't been updated.
   {
@@ -1227,7 +1227,7 @@
   }
 
   // Final check for validity.
-  EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
+  EXPECT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
 }
 
 // Ask the directory to generate a unique ID.  Close and re-open the database
@@ -1304,7 +1304,7 @@
     zombie.PutIsUnsynced(true);
   }
 
-  ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
+  ASSERT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
 
   {
     ReadTransaction trans(FROM_HERE, dir().get());
@@ -1353,7 +1353,7 @@
     null_child_id = child.GetId();
   }
 
-  EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
+  EXPECT_EQ(OPENED_EXISTING, SimulateSaveAndReloadDir());
 
   {
     ReadTransaction trans(FROM_HERE, dir().get());
@@ -1829,8 +1829,8 @@
       std::make_unique<InMemoryDirectoryBackingStore>("catastrophic_error"),
       MakeWeakHandle(unrecoverable_error_handler.GetWeakPtr()), base::Closure(),
       nullptr, nullptr);
-  ASSERT_EQ(OPENED, dir.Open(kDirectoryName, directory_change_delegate(),
-                             NullTransactionObserver()));
+  ASSERT_EQ(OPENED_NEW, dir.Open(kDirectoryName, directory_change_delegate(),
+                                 NullTransactionObserver()));
   ASSERT_EQ(0, unrecoverable_error_handler.invocation_count());
 
   // Fire off two catastrophic errors. Call it twice to ensure Directory is
diff --git a/components/sync/syncable/in_memory_directory_backing_store.cc b/components/sync/syncable/in_memory_directory_backing_store.cc
index 031e8ea..6c9148b 100644
--- a/components/sync/syncable/in_memory_directory_backing_store.cc
+++ b/components/sync/syncable/in_memory_directory_backing_store.cc
@@ -21,7 +21,8 @@
       return FAILED_OPEN_DATABASE;
   }
 
-  if (!InitializeTables())
+  bool did_start_new = false;
+  if (!InitializeTables(&did_start_new))
     return FAILED_OPEN_DATABASE;
 
   if (!LoadEntries(handles_map, metahandles_to_purge))
@@ -33,7 +34,7 @@
   if (!VerifyReferenceIntegrity(handles_map))
     return FAILED_DATABASE_CORRUPT;
 
-  return OPENED;
+  return did_start_new ? OPENED_NEW : OPENED_EXISTING;
 }
 
 }  // namespace syncable
diff --git a/components/sync/syncable/on_disk_directory_backing_store.cc b/components/sync/syncable/on_disk_directory_backing_store.cc
index 63575be..5074fec 100644
--- a/components/sync/syncable/on_disk_directory_backing_store.cc
+++ b/components/sync/syncable/on_disk_directory_backing_store.cc
@@ -42,7 +42,8 @@
       return FAILED_OPEN_DATABASE;
   }
 
-  if (!InitializeTables())
+  bool did_start_new = false;
+  if (!InitializeTables(&did_start_new))
     return FAILED_OPEN_DATABASE;
 
   if (!LoadEntries(handles_map, metahandles_to_purge))
@@ -54,7 +55,7 @@
   if (!VerifyReferenceIntegrity(handles_map))
     return FAILED_DATABASE_CORRUPT;
 
-  return OPENED;
+  return did_start_new ? OPENED_NEW : OPENED_EXISTING;
 }
 
 DirOpenResult OnDiskDirectoryBackingStore::Load(
@@ -65,10 +66,10 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DirOpenResult result = TryLoad(handles_map, delete_journals,
                                  metahandles_to_purge, kernel_load_info);
-  if (result == OPENED) {
+  if (result == OPENED_NEW || result == OPENED_EXISTING) {
     UMA_HISTOGRAM_ENUMERATION("Sync.DirectoryOpenResult", FIRST_TRY_SUCCESS,
                               RESULT_COUNT);
-    return OPENED;
+    return result;
   }
 
   ReportFirstTryOpenFailure();
@@ -84,7 +85,7 @@
 
   result = TryLoad(handles_map, delete_journals, metahandles_to_purge,
                    kernel_load_info);
-  if (result == OPENED) {
+  if (result == OPENED_NEW || result == OPENED_EXISTING) {
     UMA_HISTOGRAM_ENUMERATION("Sync.DirectoryOpenResult", SECOND_TRY_SUCCESS,
                               RESULT_COUNT);
   } else {
diff --git a/components/sync/syncable/syncable_unittest.cc b/components/sync/syncable/syncable_unittest.cc
index 93bbc84..742d94b 100644
--- a/components/sync/syncable/syncable_unittest.cc
+++ b/components/sync/syncable/syncable_unittest.cc
@@ -159,7 +159,8 @@
     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
     file_path_ = temp_dir_.GetPath().Append(FILE_PATH_LITERAL("Test.sqlite3"));
     base::DeleteFile(file_path_, false);
-    CreateDirectory();
+    ASSERT_EQ(OPENED_NEW, CreateDirectory());
+    ASSERT_TRUE(dir()->good());
   }
 
   void TearDown() override {
@@ -171,22 +172,22 @@
   }
 
   // Creates a new directory.  Deletes the old directory, if it exists.
-  void CreateDirectory() {
+  DirOpenResult CreateDirectory() {
     std::unique_ptr<TestDirectory> test_directory = TestDirectory::Create(
         encryptor(),
         MakeWeakHandle(unrecoverable_error_handler()->GetWeakPtr()),
         kDirectoryName, file_path_);
     test_directory_ = test_directory.get();
     dir() = std::move(test_directory);
-    ASSERT_TRUE(dir().get());
-    ASSERT_EQ(OPENED, dir()->Open(kDirectoryName, directory_change_delegate(),
-                                  NullTransactionObserver()));
-    ASSERT_TRUE(dir()->good());
+    DCHECK(dir());
+    return dir()->Open(kDirectoryName, directory_change_delegate(),
+                       NullTransactionObserver());
   }
 
   void SaveAndReloadDir() {
     dir()->SaveChanges();
-    CreateDirectory();
+    ASSERT_EQ(OPENED_EXISTING, CreateDirectory());
+    ASSERT_TRUE(dir()->good());
   }
 
   void StartFailingSaveChanges() { test_directory_->StartFailingSaveChanges(); }
@@ -354,8 +355,9 @@
       base::Closure(), nullptr, nullptr);
 
   ASSERT_TRUE(dir().get());
-  ASSERT_EQ(OPENED, dir()->Open(kDirectoryName, directory_change_delegate(),
-                                NullTransactionObserver()));
+  ASSERT_EQ(OPENED_EXISTING,
+            dir()->Open(kDirectoryName, directory_change_delegate(),
+                        NullTransactionObserver()));
   ASSERT_TRUE(dir()->good());
 
   {
@@ -566,7 +568,7 @@
         nullptr);
     DirOpenResult result =
         dir.Open("ScopeTest", &delegate_, NullTransactionObserver());
-    ASSERT_EQ(result, OPENED);
+    ASSERT_EQ(result, OPENED_NEW);
   }
 
   // Destroying the directory should have released the backing database file.
diff --git a/components/sync/test/engine/mock_model_type_worker.cc b/components/sync/test/engine/mock_model_type_worker.cc
index 692c015e..5e3e923 100644
--- a/components/sync/test/engine/mock_model_type_worker.cc
+++ b/components/sync/test/engine/mock_model_type_worker.cc
@@ -106,6 +106,11 @@
   }
 }
 
+void MockModelTypeWorker::UpdateModelTypeState(
+    const sync_pb::ModelTypeState& model_type_state) {
+  model_type_state_ = model_type_state;
+}
+
 void MockModelTypeWorker::UpdateFromServer() {
   processor_->OnUpdateReceived(model_type_state_, UpdateResponseDataList());
 }
diff --git a/components/sync/test/engine/mock_model_type_worker.h b/components/sync/test/engine/mock_model_type_worker.h
index 4487c90..5b1830a 100644
--- a/components/sync/test/engine/mock_model_type_worker.h
+++ b/components/sync/test/engine/mock_model_type_worker.h
@@ -60,6 +60,9 @@
   void VerifyPendingCommits(
       const std::vector<std::vector<std::string>>& tag_hashes);
 
+  // Updates the model type state to be used in all future updates from server.
+  void UpdateModelTypeState(const sync_pb::ModelTypeState& model_type_state);
+
   // Trigger an update from the server. See GenerateUpdateData for parameter
   // descriptions. |version_offset| defaults to 1 and |ekn| defaults to the
   // current encryption key name the worker has.
diff --git a/components/sync/test/engine/test_directory_setter_upper.cc b/components/sync/test/engine/test_directory_setter_upper.cc
index 91ca7943..600ca735 100644
--- a/components/sync/test/engine/test_directory_setter_upper.cc
+++ b/components/sync/test/engine/test_directory_setter_upper.cc
@@ -33,7 +33,7 @@
       std::make_unique<syncable::InMemoryDirectoryBackingStore>(name_),
       MakeWeakHandle(handler_.GetWeakPtr()), base::Closure(),
       &encryption_handler_, encryption_handler_.cryptographer());
-  ASSERT_EQ(syncable::OPENED,
+  ASSERT_EQ(syncable::OPENED_NEW,
             directory_->Open(name_, &delegate_, transaction_observer));
 }
 
@@ -49,7 +49,7 @@
       std::move(directory_store), MakeWeakHandle(handler_.GetWeakPtr()),
       base::Closure(), &encryption_handler_,
       encryption_handler_.cryptographer());
-  ASSERT_EQ(syncable::OPENED,
+  ASSERT_EQ(syncable::OPENED_EXISTING,
             directory_->Open(name_, &delegate_, transaction_observer));
 }
 
diff --git a/components/sync/test/test_directory_backing_store.cc b/components/sync/test/test_directory_backing_store.cc
index c38a09e..d075c7c 100644
--- a/components/sync/test/test_directory_backing_store.cc
+++ b/components/sync/test/test_directory_backing_store.cc
@@ -27,7 +27,8 @@
     Directory::KernelLoadInfo* kernel_load_info) {
   DCHECK(db_->is_open());
 
-  if (!InitializeTables())
+  bool did_start_new = false;
+  if (!InitializeTables(&did_start_new))
     return FAILED_OPEN_DATABASE;
 
   if (!LoadEntries(handles_map, metahandles_to_purge))
@@ -39,7 +40,7 @@
   if (!VerifyReferenceIntegrity(handles_map))
     return FAILED_DATABASE_CORRUPT;
 
-  return OPENED;
+  return did_start_new ? OPENED_NEW : OPENED_EXISTING;
 }
 
 bool TestDirectoryBackingStore::DeleteEntries(const MetahandleSet& handles) {
diff --git a/components/task_scheduler_util/variations_util.cc b/components/task_scheduler_util/variations_util.cc
index cff79ac..d7b3e7a 100644
--- a/components/task_scheduler_util/variations_util.cc
+++ b/components/task_scheduler_util/variations_util.cc
@@ -98,23 +98,14 @@
 
   const auto background_worker_pool_params =
       GetWorkerPoolParams("Background", variation_params);
-  const auto background_blocking_worker_pool_params =
-      GetWorkerPoolParams("BackgroundBlocking", variation_params);
   const auto foreground_worker_pool_params =
       GetWorkerPoolParams("Foreground", variation_params);
-  const auto foreground_blocking_worker_pool_params =
-      GetWorkerPoolParams("ForegroundBlocking", variation_params);
 
-  if (!background_worker_pool_params ||
-      !background_blocking_worker_pool_params ||
-      !foreground_worker_pool_params ||
-      !foreground_blocking_worker_pool_params) {
+  if (!background_worker_pool_params || !foreground_worker_pool_params)
     return nullptr;
-  }
 
   return std::make_unique<base::TaskScheduler::InitParams>(
-      *background_worker_pool_params, *background_blocking_worker_pool_params,
-      *foreground_worker_pool_params, *foreground_blocking_worker_pool_params);
+      *background_worker_pool_params, *foreground_worker_pool_params);
 }
 
 std::unique_ptr<base::TaskScheduler::InitParams>
diff --git a/components/task_scheduler_util/variations_util_unittest.cc b/components/task_scheduler_util/variations_util_unittest.cc
index 7193ca8..d5aaffb 100644
--- a/components/task_scheduler_util/variations_util_unittest.cc
+++ b/components/task_scheduler_util/variations_util_unittest.cc
@@ -41,9 +41,7 @@
 TEST_F(TaskSchedulerUtilVariationsUtilTest, OrderingParams5) {
   std::map<std::string, std::string> variation_params;
   variation_params["Background"] = "1;1;1;0;42";
-  variation_params["BackgroundBlocking"] = "2;2;1;0;52";
   variation_params["Foreground"] = "4;4;1;0;62";
-  variation_params["ForegroundBlocking"] = "8;8;1;0;72";
   SetVariationParams(variation_params);
 
   auto init_params = GetTaskSchedulerInitParams(kRendererSchedulerInitParams);
@@ -57,14 +55,6 @@
       base::SchedulerBackwardCompatibility::DISABLED,
       init_params->background_worker_pool_params.backward_compatibility());
 
-  EXPECT_EQ(2, init_params->background_blocking_worker_pool_params.max_tasks());
-  EXPECT_EQ(base::TimeDelta::FromMilliseconds(52),
-            init_params->background_blocking_worker_pool_params
-                .suggested_reclaim_time());
-  EXPECT_EQ(base::SchedulerBackwardCompatibility::DISABLED,
-            init_params->background_blocking_worker_pool_params
-                .backward_compatibility());
-
   EXPECT_EQ(4, init_params->foreground_worker_pool_params.max_tasks());
   EXPECT_EQ(
       base::TimeDelta::FromMilliseconds(62),
@@ -72,14 +62,6 @@
   EXPECT_EQ(
       base::SchedulerBackwardCompatibility::DISABLED,
       init_params->foreground_worker_pool_params.backward_compatibility());
-
-  EXPECT_EQ(8, init_params->foreground_blocking_worker_pool_params.max_tasks());
-  EXPECT_EQ(base::TimeDelta::FromMilliseconds(72),
-            init_params->foreground_blocking_worker_pool_params
-                .suggested_reclaim_time());
-  EXPECT_EQ(base::SchedulerBackwardCompatibility::DISABLED,
-            init_params->foreground_blocking_worker_pool_params
-                .backward_compatibility());
 }
 
 TEST_F(TaskSchedulerUtilVariationsUtilTest, NoData) {
@@ -89,9 +71,7 @@
 TEST_F(TaskSchedulerUtilVariationsUtilTest, IncompleteParameters) {
   std::map<std::string, std::string> variation_params;
   variation_params["Background"] = "1;1;1;0";
-  variation_params["BackgroundBlocking"] = "2;2;1;0";
   variation_params["Foreground"] = "4;4;1;0";
-  variation_params["ForegroundBlocking"] = "8;8;1;0";
   SetVariationParams(variation_params);
   EXPECT_FALSE(GetTaskSchedulerInitParams(kRendererSchedulerInitParams));
 }
@@ -99,9 +79,7 @@
 TEST_F(TaskSchedulerUtilVariationsUtilTest, InvalidParametersFormat) {
   std::map<std::string, std::string> variation_params;
   variation_params["Background"] = "a;b;c;d;e";
-  variation_params["BackgroundBlocking"] = "a;b;c;d;e";
   variation_params["Foreground"] = "a;b;c;d;e";
-  variation_params["ForegroundBlocking"] = "a;b;c;d;e";
   SetVariationParams(variation_params);
   EXPECT_FALSE(GetTaskSchedulerInitParams(kRendererSchedulerInitParams));
 }
@@ -111,9 +89,7 @@
   // invalid.
   std::map<std::string, std::string> variation_params;
   variation_params["Background"] = "0;0;0;0;0";
-  variation_params["BackgroundBlocking"] = "2;2;1;0;52";
   variation_params["Foreground"] = "4;4;1;0;62";
-  variation_params["ForegroundBlocking"] = "8;8;1;0;72";
   SetVariationParams(variation_params);
   EXPECT_FALSE(GetTaskSchedulerInitParams(kRendererSchedulerInitParams));
 }
@@ -123,9 +99,7 @@
   // invalid.
   std::map<std::string, std::string> variation_params;
   variation_params["Background"] = "-5;-5;0;0;0";
-  variation_params["BackgroundBlocking"] = "2;2;1;0;52";
   variation_params["Foreground"] = "4;4;1;0;62";
-  variation_params["ForegroundBlocking"] = "8;8;1;0;72";
   SetVariationParams(variation_params);
   EXPECT_FALSE(GetTaskSchedulerInitParams(kRendererSchedulerInitParams));
 }
@@ -135,9 +109,7 @@
   // invalid.
   std::map<std::string, std::string> variation_params;
   variation_params["Background"] = "1;1;1;0;-5";
-  variation_params["BackgroundBlocking"] = "2;2;1;0;52";
   variation_params["Foreground"] = "4;4;1;0;62";
-  variation_params["ForegroundBlocking"] = "8;8;1;0;72";
   SetVariationParams(variation_params);
   EXPECT_FALSE(GetTaskSchedulerInitParams(kRendererSchedulerInitParams));
 }
diff --git a/components/test/data/payments/can_make_payment_query.js b/components/test/data/payments/can_make_payment_query.js
index e955ce08..9f94ca11 100644
--- a/components/test/data/payments/can_make_payment_query.js
+++ b/components/test/data/payments/can_make_payment_query.js
@@ -7,28 +7,54 @@
 /* global PaymentRequest:false */
 /* global print:false */
 
+const bobPayMethod = {supportedMethods: 'https://bobpay.com'};
+
+const visaMethod = {
+  supportedMethods: 'basic-card',
+  data: {
+    supportedNetworks: ['visa'],
+  },
+};
+
+const defaultDetails = {
+  total: {
+    label: 'Total',
+    amount: {
+      currency: 'USD',
+      value: '5.00',
+    },
+  },
+};
+
+/**
+ * Runs |testFunction| and prints any result or error.
+ * @param {function} testFunction A function with no argument and returns a
+ * Promise.
+ */
+function run(testFunction) {
+  try {
+    testFunction().then(print).catch(print);
+  } catch (error) {
+    print(error.message);
+  }
+}
+
 /**
  * Checks for existence of Bob Pay or a complete credit card.
  */
 function buy() {  // eslint-disable-line no-unused-vars
-  try {
-    var request = new PaymentRequest(
-        [
-          {supportedMethods: 'https://bobpay.com'},
-          {
-            supportedMethods: 'basic-card',
-            data: {supportedNetworks: ['visa']},
-          },
-        ],
-        {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}});
-    request.canMakePayment()
-        .then(function(result) {
-          print(result);
-        })
-        .catch(function(error) {
-          print(error);
-        });
-  } catch (error) {
-    print(error.message);
-  }
+  var request = new PaymentRequest([bobPayMethod, visaMethod], defaultDetails);
+  run(() => {
+    return request.canMakePayment();
+  });
+}
+
+/**
+ * Checks for enrolled instrument of Bob Pay or a complete credit card.
+ */
+function hasEnrolledInstrument() {  // eslint-disable-line no-unused-vars
+  var request = new PaymentRequest([bobPayMethod, visaMethod], defaultDetails);
+  run(() => {
+    return request.hasEnrolledInstrument();
+  });
 }
diff --git a/components/test/data/payments/can_make_payment_query_bobpay.js b/components/test/data/payments/can_make_payment_query_bobpay.js
index 0e2d6ac..198d93280 100644
--- a/components/test/data/payments/can_make_payment_query_bobpay.js
+++ b/components/test/data/payments/can_make_payment_query_bobpay.js
@@ -7,6 +7,30 @@
 /* global PaymentRequest:false */
 /* global print:false */
 
+const defaultDetails = {
+  total: {
+    label: 'Total',
+    amount: {
+      currency: 'USD',
+      value: '5.00',
+    },
+  },
+};
+
+const bobPayMethod = {
+  supportedMethods: 'https://bobpay.com',
+  data: {
+    'bobPayParameter': '1',
+  },
+};
+
+const alicePayMethod = {
+  supportedMethods: 'https://alicepay.com',
+  data: {
+    'alicePayParameter': '2',
+  },
+};
+
 var first;
 var second;
 
@@ -35,6 +59,20 @@
 }
 
 /**
+ * Runs |testFunction| and logs any result or error using |logger|.
+ * @param {function} testFunction A function with no argument and returns a
+ * Promise.
+ * @param {function} logger A function that takes one argument.
+ */
+function run(testFunction, logger) {
+  try {
+    testFunction().then(logger).catch(logger);
+  } catch (error) {
+    logger(error);
+  }
+}
+
+/**
  * Checks for existence of Bob Pay twice, with the same payment method specific
  * parameters.
  */
@@ -42,41 +80,15 @@
   first = null;
   second = null;
 
-  try {
-    new PaymentRequest(
-        [{
-          supportedMethods: 'https://bobpay.com',
-          data: {'bobPayParameter': '1'},
-        }],
-        {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}})
-        .canMakePayment()
-        .then(function(result) {
-          printFirst(result);
-        })
-        .catch(function(error) {
-          printFirst(error);
-        });
-  } catch (error) {
-    printFirst(error);
-  }
+  const request1 = new PaymentRequest([bobPayMethod], defaultDetails);
+  run(() => {
+    return request1.canMakePayment();
+  }, printFirst);
 
-  try {
-    new PaymentRequest(
-        [{
-          supportedMethods: 'https://bobpay.com',
-          data: {'bobPayParameter': '1'},
-        }],
-        {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}})
-        .canMakePayment()
-        .then(function(result) {
-          printSecond(result);
-        })
-        .catch(function(error) {
-          printSecond(error);
-        });
-  } catch (error) {
-    printSecond(error);
-  }
+  const request2 = new PaymentRequest([bobPayMethod], defaultDetails);
+  run(() => {
+    return request2.canMakePayment();
+  }, printSecond);
 }
 
 /**
@@ -86,39 +98,31 @@
   first = null;
   second = null;
 
-  try {
-    new PaymentRequest(
-        [{
-          supportedMethods: 'https://bobpay.com',
-          data: {'bobPayParameter': '1'},
-        }],
-        {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}})
-        .canMakePayment()
-        .then(function(result) {
-          printFirst(result);
-        })
-        .catch(function(error) {
-          printFirst(error);
-        });
-  } catch (error) {
-    printFirst(error);
-  }
+  const request1 = new PaymentRequest([bobPayMethod], defaultDetails);
+  run(() => {
+    return request1.canMakePayment();
+  }, printFirst);
 
-  try {
-    new PaymentRequest(
-        [{
-          supportedMethods: 'https://alicepay.com',
-          data: {'alicePayParameter': '2'},
-        }],
-        {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}})
-        .canMakePayment()
-        .then(function(result) {
-          printSecond(result);
-        })
-        .catch(function(error) {
-          printSecond(error);
-        });
-  } catch (error) {
-    printSecond(error);
-  }
+  const request2 = new PaymentRequest([alicePayMethod], defaultDetails);
+  run(() => {
+    return request2.canMakePayment();
+  }, printSecond);
+}
+
+/**
+ * Checks for existence of an enrolled instrument for BobPay and AlicePay.
+ */
+function hasEnrolledInstrument() {  // eslint-disable-line no-unused-vars
+  first = null;
+  second = null;
+
+  const request1 = new PaymentRequest([bobPayMethod], defaultDetails);
+  run(() => {
+    return request1.hasEnrolledInstrument();
+  }, printFirst);
+
+  const request2 = new PaymentRequest([alicePayMethod], defaultDetails);
+  run(() => {
+    return request2.hasEnrolledInstrument();
+  }, printSecond);
 }
diff --git a/components/test/data/payments/can_make_payment_query_cc.js b/components/test/data/payments/can_make_payment_query_cc.js
index dcb081b..4dc07627 100644
--- a/components/test/data/payments/can_make_payment_query_cc.js
+++ b/components/test/data/payments/can_make_payment_query_cc.js
@@ -5,44 +5,72 @@
  */
 
 /**
- * Checks for existence of a complete VISA credit card.
+ * Creates a PaymentRequest for the specified card network.
+ *
+ * @param {string} network The supportedNetwork value to use.
+ * @return {PaymentRequest} The new PaymentRequest created.
  */
-function buy() {  // eslint-disable-line no-unused-vars
+function createPaymentRequest(network) {
+  return new PaymentRequest(
+      [{
+        supportedMethods: 'basic-card',
+        data: {
+          supportedNetworks: [network],
+        },
+      }],
+      {
+        total: {
+          label: 'Total',
+          amount: {
+            currency: 'USD',
+            value: '5.00',
+          },
+        },
+      });
+}
+
+/**
+ * Runs |testFunction| and prints any result or error.
+ *
+ * @param {function} testFunction A function with no argument and returns a
+ * Promise.
+ */
+function run(testFunction) {
   try {
-    var request = new PaymentRequest(
-        [{supportedMethods: 'basic-card', data: {supportedNetworks: ['visa']}}],
-        {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}});
-    request.canMakePayment()
-        .then(function(result) {
-          print(result);
-        })
-        .catch(function(error) {
-          print(error);
-        });
+    testFunction().then(print).catch(print);
   } catch (error) {
     print(error.message);
   }
 }
 
 /**
+ * Checks for existence of a complete VISA credit card.
+ */
+function buy() {  // eslint-disable-line no-unused-vars
+  const request = createPaymentRequest('visa');
+  run(() => {
+    return request.canMakePayment();
+  });
+}
+
+/**
  * Checks for existence of a complete MasterCard credit card.
  */
 function other_buy() {  // eslint-disable-line no-unused-vars, camelcase
-  try {
-    var request = new PaymentRequest(
-        [{
-          supportedMethods: 'basic-card',
-          data: {supportedNetworks: ['mastercard']},
-        }],
-        {total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}});
-    request.canMakePayment()
-        .then(function(result) {
-          print(result);
-        })
-        .catch(function(error) {
-          print(error);
-        });
-  } catch (error) {
-    print(error.message);
-  }
+  const request = createPaymentRequest('mastercard');
+  run(() => {
+    return request.canMakePayment();
+  });
+}
+
+/**
+ * Checks for existence of an enrolled instrument of the specified card network.
+ *
+ * @param {string} network The credit card network to check.
+ */
+function hasEnrolledInstrument(network) {  // eslint-disable-line no-unused-vars
+  const request = createPaymentRequest(network);
+  run(() => {
+    return request.hasEnrolledInstrument();
+  });
 }
diff --git a/components/test/data/payments/payment_method_identifier.js b/components/test/data/payments/payment_method_identifier.js
index 68bdc124..ae34d45 100644
--- a/components/test/data/payments/payment_method_identifier.js
+++ b/components/test/data/payments/payment_method_identifier.js
@@ -4,32 +4,79 @@
  * found in the LICENSE file.
  */
 
+const basicCardMethod = {supportedMethods: 'basic-card'};
+
+const basicVisaMethod = {
+  supportedMethods: 'basic-card',
+  data: {
+    supportedNetworks: ['visa'],
+  },
+};
+
+const basicMastercardMethod = {
+  supportedMethods: 'basic-card',
+  data: {
+    supportedNetworks: ['mastercard'],
+  },
+};
+
+const basicDebitMethod = {
+  supportedMethods: 'basic-card',
+  data: {
+    supportedTypes: ['debit'],
+  },
+};
+
+const visaMethod = {supportedMethods: 'visa'};
+const mastercardMethod = {supportedMethods: 'mastercard'};
+const alicePayMethod = {supportedMethods: 'https://alicepay.com/webpay'};
+const bobPayMethod = {supportedMethods: 'https://bobpay.com/webpay'};
+
+const defaultDetails = {
+  total: {
+    label: 'Total',
+    amount: {
+      currency: 'USD',
+      value: '5.00',
+    },
+  },
+};
+
+/**
+ * Runs |testFunction| and prints any result or error.
+ *
+ * @param {function} testFunction A function with no argument and returns a
+ * Promise.
+ */
+function run(testFunction) {
+  try {
+    testFunction().then(print).catch(print);
+  } catch (error) {
+    print(error);
+  }
+}
+
 /**
  * Calls PaymentRequest.canMakePayment() and prints out the result.
  * @param {sequence<PaymentMethodData>} methodData The supported methods.
  * @private
  */
-function canMakePaymentHelper(methodData) {
-  try {
-    new PaymentRequest(methodData, {
-      total: {
-        label: 'Total',
-        amount: {
-          currency: 'USD',
-          value: '5.00',
-        },
-      },
-    })
-        .canMakePayment()
-        .then(function(result) {
-          print(result);
-        })
-        .catch(function(error) {
-          print(error);
-        });
-  } catch (error) {
-    print(error);
-  }
+function checkCanMakePayment(methodData) {
+  run(() => {
+    const request = new PaymentRequest(methodData, defaultDetails);
+    return request.canMakePayment();
+  });
+}
+
+/**
+ * Calls PaymentRequest.hasEnrolledInstrument() and prints out the result.
+ * @param {sequence<PaymentMethodData>} methodData The supported methods.
+ */
+function checkHasEnrolledInstrument(methodData) {  // eslint-disable-line no-unused-vars, max-len
+  run(() => {
+    const request = new PaymentRequest(methodData, defaultDetails);
+    return request.hasEnrolledInstrument();
+  });
 }
 
 /**
@@ -37,21 +84,14 @@
  * network.
  */
 function checkBasicCard() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([{
-    supportedMethods: 'basic-card',
-  }]);
+  checkCanMakePayment([basicCardMethod]);
 }
 
 /**
  * Merchant checks for ability to pay using debit cards.
  */
 function checkBasicDebit() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([{
-    supportedMethods: 'basic-card',
-    data: {
-      supportedTypes: ['debit'],
-    },
-  }]);
+  checkCanMakePayment([basicDebitMethod]);
 }
 
 /**
@@ -59,12 +99,7 @@
  * the supported network.
  */
 function checkBasicMasterCard() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([{
-    supportedMethods: 'basic-card',
-    data: {
-      supportedNetworks: ['mastercard'],
-    },
-  }]);
+  checkCanMakePayment([basicMastercardMethod]);
 }
 
 /**
@@ -72,48 +107,35 @@
  * supported network.
  */
 function checkBasicVisa() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([{
-    supportedMethods: 'basic-card',
-    data: {
-      supportedNetworks: ['visa'],
-    },
-  }]);
+  checkCanMakePayment([basicVisaMethod]);
 }
 
 /**
  * Merchant checks for ability to pay using "mastercard".
  */
 function checkMasterCard() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([{
-    supportedMethods: 'mastercard',
-  }]);
+  checkCanMakePayment([mastercardMethod]);
 }
 
 /**
  * Merchant checks for ability to pay using "visa".
  */
 function checkVisa() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([{
-    supportedMethods: 'visa',
-  }]);
+  checkCanMakePayment([visaMethod]);
 }
 
 /**
  * Merchant checks for ability to pay using "https://alicepay.com/webpay".
  */
 function checkAlicePay() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([{
-    supportedMethods: 'https://alicepay.com/webpay',
-  }]);
+  checkCanMakePayment([alicePayMethod]);
 }
 
 /**
  * Merchant checks for ability to pay using "https://bobpay.com/webpay".
  */
 function checkBobPay() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([{
-    supportedMethods: 'https://bobpay.com/webpay',
-  }]);
+  checkCanMakePayment([bobPayMethod]);
 }
 
 /**
@@ -121,14 +143,7 @@
  * "basic-card".
  */
 function checkBobPayAndBasicCard() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([
-    {
-      supportedMethods: 'https://bobpay.com/webpay',
-    },
-    {
-      supportedMethods: 'basic-card',
-    },
-  ]);
+  checkCanMakePayment([bobPayMethod, basicCardMethod]);
 }
 
 /**
@@ -136,14 +151,7 @@
  * "visa".
  */
 function checkBobPayAndVisa() {  // eslint-disable-line no-unused-vars
-  canMakePaymentHelper([
-    {
-      supportedMethods: 'https://bobpay.com/webpay',
-    },
-    {
-      supportedMethods: 'visa',
-    },
-  ]);
+  checkCanMakePayment([bobPayMethod, visaMethod]);
 }
 
 /**
@@ -153,15 +161,7 @@
  */
 function buyHelper(methodData) {
   try {
-    new PaymentRequest(methodData, {
-      total: {
-        label: 'Total',
-        amount: {
-          currency: 'USD',
-          value: '5.00',
-        },
-      },
-    })
+    new PaymentRequest(methodData, defaultDetails)
         .show()
         .then(function(response) {
           response.complete('success')
@@ -185,26 +185,14 @@
  * as the supported network.
  */
 function buy() {  // eslint-disable-line no-unused-vars
-  buyHelper([
-    {
-      supportedMethods: 'mastercard',
-    },
-    {
-      supportedMethods: 'basic-card',
-      data: {
-        supportedNetworks: ['visa'],
-      },
-    },
-  ]);
+  buyHelper([mastercardMethod, basicVisaMethod]);
 }
 
 /**
  * Merchant requests payment via "basic-card" with any issuer network.
  */
 function buyBasicCard() {  // eslint-disable-line no-unused-vars
-  buyHelper([{
-    supportedMethods: 'basic-card',
-  }]);
+  buyHelper([basicCardMethod]);
 }
 
 /**
@@ -212,12 +200,7 @@
  * type.
  */
 function buyBasicDebit() {  // eslint-disable-line no-unused-vars
-  buyHelper([{
-    supportedMethods: 'basic-card',
-    data: {
-      supportedTypes: ['debit'],
-    },
-  }]);
+  buyHelper([basicDebitMethod]);
 }
 
 /**
@@ -225,10 +208,5 @@
  * as the only supported network.
  */
 function buyBasicMasterCard() {  // eslint-disable-line no-unused-vars
-  buyHelper([{
-    supportedMethods: 'basic-card',
-    data: {
-      supportedNetworks: ['mastercard'],
-    },
-  }]);
+  buyHelper([basicMastercardMethod]);
 }
diff --git a/components/test/data/payments/payment_request_can_make_payment_query_bobpay_test.html b/components/test/data/payments/payment_request_can_make_payment_query_bobpay_test.html
index 3c41a4ad..1b25517 100644
--- a/components/test/data/payments/payment_request_can_make_payment_query_bobpay_test.html
+++ b/components/test/data/payments/payment_request_can_make_payment_query_bobpay_test.html
@@ -13,6 +13,7 @@
 <body>
 <button onclick="buy()" id="buy">Check BobPay Twice</button>
 <button onclick="otherBuy()" id="otherBuy">Check BobPay and AlicePay</button>
+<button onclick="hasEnrolledInstrument()" id="hasEnrolledInstrument">Check hasEnrolledInstrument on BobPay and AlicePay</button>
 <pre id="result"></pre>
 <script src="util.js"></script>
 <script src="can_make_payment_query_bobpay.js"></script>
diff --git a/components/test/data/payments/payment_request_can_make_payment_query_cc_test.html b/components/test/data/payments/payment_request_can_make_payment_query_cc_test.html
index 40c02a86..fdaadd86d 100644
--- a/components/test/data/payments/payment_request_can_make_payment_query_cc_test.html
+++ b/components/test/data/payments/payment_request_can_make_payment_query_cc_test.html
@@ -13,6 +13,9 @@
 <body>
 <button onclick="buy()" id="buy">VISA Test</button>
 <button onclick="other_buy()" id="other-buy">MasterCard Test</button>
+<button onclick="hasEnrolledInstrument('visa')" id="has-enrolled-instrument-visa">
+  VISA hasEnrolledInstrument Test
+</button>
 <pre id="result"></pre>
 <script src="util.js"></script>
 <script src="can_make_payment_query_cc.js"></script>
diff --git a/components/test/data/payments/payment_request_update_with_test.html b/components/test/data/payments/payment_request_update_with_test.html
new file mode 100644
index 0000000..9dcc6c2
--- /dev/null
+++ b/components/test/data/payments/payment_request_update_with_test.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+<html>
+<head>
+<title>updateWith() Test</title>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
+<link rel="stylesheet" type="text/css" href="style.css">
+</head>
+<body>
+<button onclick="updateWithNoShippingOptions()" id="updateWithNoShippingOptions">updateWithNoShippingOptions</button>
+<button onclick="updateWithShippingOptions()" id="updateWithShippingOptions">updateWithShippingOptions</button>
+<pre id="result"></pre>
+<script src="util.js"></script>
+<script src="update_with.js"></script>
+</body>
+</html>
diff --git a/components/test/data/payments/update_with.js b/components/test/data/payments/update_with.js
new file mode 100644
index 0000000..87db7e5
--- /dev/null
+++ b/components/test/data/payments/update_with.js
@@ -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.
+ */
+
+/**
+ * Builds a PaymentRequest that requests a shipping address.
+ * @return {PaymentRequest} - A new PaymentRequest object.
+ */
+function buildPaymentRequest() {
+  try {
+    return new PaymentRequest(
+        [{supportedMethods: 'basic-card'}], {
+          total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}},
+          shippingOptions: [{
+            selected: true,
+            id: 'freeShipping',
+            label: 'Free shipping',
+            amount: {currency: 'USD', value: '0.00'},
+          }],
+        },
+        {requestShipping: true});
+  } catch (error) {
+    print(error.message);
+  }
+}
+
+/**
+ * Shows the PaymentRequest.
+ * @param {PaymentRequest} pr - The PaymentRequest object to show.
+ */
+function showPaymentRequest(pr) {
+  pr.show()
+      .then(function(resp) {
+        resp.complete('success')
+            .then(function() {
+              print(JSON.stringify(resp, undefined, 2));
+            })
+            .catch(function(error) {
+              print(error);
+            });
+      })
+      .catch(function(error) {
+        print(error);
+      });
+}
+
+/**
+ * Calls updateWith() with no shipping options
+ */
+function updateWithNoShippingOptions() {  // eslint-disable-line no-unused-vars
+  var pr = buildPaymentRequest();
+  var updatedDetails = {
+    total: {label: 'Updated total', amount: {currency: 'USD', value: '10.00'}},
+  };
+  pr.addEventListener('shippingaddresschange', function(e) {
+    e.updateWith(updatedDetails);
+  });
+  pr.addEventListener('shippingoptionchange', function(e) {
+    e.updateWith(updatedDetails);
+  });
+  showPaymentRequest(pr);
+}
+
+/**
+ * Calls updateWith() with shipping options
+ */
+function updateWithShippingOptions() {  // eslint-disable-line no-unused-vars
+  var pr = buildPaymentRequest();
+  var updatedDetails = {
+    total: {label: 'Updated total', amount: {currency: 'USD', value: '10.00'}},
+    shippingOptions: [{
+      selected: true,
+      id: 'updatedShipping',
+      label: 'Updated shipping',
+      amount: {currency: 'USD', value: '5.00'},
+    }],
+  };
+  pr.addEventListener('shippingaddresschange', function(e) {
+    e.updateWith(updatedDetails);
+  });
+  pr.addEventListener('shippingoptionchange', function(e) {
+    e.updateWith(updatedDetails);
+  });
+  showPaymentRequest(pr);
+}
diff --git a/components/translate/core/browser/translate_infobar_delegate.cc b/components/translate/core/browser/translate_infobar_delegate.cc
index 68d4a1a..3371696 100644
--- a/components/translate/core/browser/translate_infobar_delegate.cc
+++ b/components/translate/core/browser/translate_infobar_delegate.cc
@@ -24,6 +24,18 @@
 #include "ui/base/l10n/l10n_util.h"
 
 namespace translate {
+namespace {
+// The number of times user should consecutively translate for "Always
+// Translate" to automatically trigger.
+const int kAutoAlwaysThreshold = 5;
+// The number of times user should consecutively dismiss the translate infobar
+// for "Never Translate" to automatically trigger.
+const int kAutoNeverThreshold = 10;
+// The maximum number of times "Always Translate" is automatically triggered.
+const int kMaxNumberOfAutoAlways = 2;
+// The maximum number of times "Never Translate" is automatically triggered.
+const int kMaxNumberOfAutoNever = 2;
+}  // namespace
 
 const base::Feature kTranslateCompactUI{"TranslateCompactUI",
                                         base::FEATURE_ENABLED_BY_DEFAULT};
@@ -308,7 +320,52 @@
   prefs_->ResetTranslationDeniedCount(original_language_code());
 }
 
-#if defined(OS_ANDROID)
+bool TranslateInfoBarDelegate::ShouldAutoAlwaysTranslate() {
+  // Don't trigger if it's off the record or already set to always translate.
+  if (is_off_the_record() || ShouldAlwaysTranslate()) {
+    return false;
+  }
+
+  bool always_translate =
+      (GetTranslationAcceptedCount() >= kAutoAlwaysThreshold &&
+       GetTranslationAutoAlwaysCount() < kMaxNumberOfAutoAlways);
+
+  if (always_translate) {
+    // Auto-always will be triggered. Need to increment the auto-always counter.
+    IncrementTranslationAutoAlwaysCount();
+    // Reset translateAcceptedCount so that auto-always could be triggered
+    // again.
+    ResetTranslationAcceptedCount();
+  }
+  return always_translate;
+}
+
+bool TranslateInfoBarDelegate::ShouldAutoNeverTranslate() {
+  // Don't trigger if it's off the record or language already blocked.
+  if (is_off_the_record() || !IsTranslatableLanguageByPrefs()) {
+    return false;
+  }
+
+  int auto_never_count = GetTranslationAutoNeverCount();
+
+  // At the beginning (auto_never_count == 0), deniedCount starts at 0 and is
+  // off-by-one (because this checking is done before increment). However, after
+  // auto-never is triggered once (auto_never_count > 0), deniedCount starts at
+  // 1.  So there is no off-by-one by then.
+  int off_by_one = auto_never_count == 0 ? 1 : 0;
+
+  bool never_translate =
+      (GetTranslationDeniedCount() + off_by_one >= kAutoNeverThreshold &&
+       auto_never_count < kMaxNumberOfAutoNever);
+  if (never_translate) {
+    // Auto-never will be triggered. Need to increment the auto-never counter.
+    IncrementTranslationAutoNeverCount();
+    // Reset translateDeniedCount so that auto-never could be triggered again.
+    ResetTranslationDeniedCount();
+  }
+  return never_translate;
+}
+
 int TranslateInfoBarDelegate::GetTranslationAutoAlwaysCount() {
   return prefs_->GetTranslationAutoAlwaysCount(original_language_code());
 }
@@ -324,7 +381,6 @@
 void TranslateInfoBarDelegate::IncrementTranslationAutoNeverCount() {
   prefs_->IncrementTranslationAutoNeverCount(original_language_code());
 }
-#endif
 
 // static
 void TranslateInfoBarDelegate::GetAfterTranslateStrings(
diff --git a/components/translate/core/browser/translate_infobar_delegate.h b/components/translate/core/browser/translate_infobar_delegate.h
index 47c1c91..898c7c3 100644
--- a/components/translate/core/browser/translate_infobar_delegate.h
+++ b/components/translate/core/browser/translate_infobar_delegate.h
@@ -150,13 +150,18 @@
   void ResetTranslationAcceptedCount();
   void ResetTranslationDeniedCount();
 
-#if defined(OS_ANDROID)
+  // Returns whether "Always Translate Language" should automatically trigger.
+  // If true, this method has the side effect of mutating some prefs.
+  bool ShouldAutoAlwaysTranslate();
+  // Returns whether "Never Translate Language" should automatically trigger.
+  // If true, this method has the side effect of mutating some prefs.
+  bool ShouldAutoNeverTranslate();
+
   int GetTranslationAutoAlwaysCount();
   int GetTranslationAutoNeverCount();
 
   void IncrementTranslationAutoAlwaysCount();
   void IncrementTranslationAutoNeverCount();
-#endif
 
   // The following methods are called by the infobar that displays the status
   // while translating and also the one displaying the error message.
diff --git a/components/translate/core/browser/translate_prefs.cc b/components/translate/core/browser/translate_prefs.cc
index 141f5ca..06d45c66 100644
--- a/components/translate/core/browser/translate_prefs.cc
+++ b/components/translate/core/browser/translate_prefs.cc
@@ -56,11 +56,14 @@
 const char TranslatePrefs::kPrefTranslateRecentTarget[] =
     "translate_recent_target";
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
 const char TranslatePrefs::kPrefTranslateAutoAlwaysCount[] =
     "translate_auto_always_count";
 const char TranslatePrefs::kPrefTranslateAutoNeverCount[] =
     "translate_auto_never_count";
+#endif
+
+#if defined(OS_ANDROID)
 const char TranslatePrefs::kPrefExplicitLanguageAskShown[] =
     "translate_explicit_language_ask_shown";
 #endif
@@ -199,7 +202,7 @@
   prefs_->ClearPref(kPrefTranslateIgnoredCount);
   prefs_->ClearPref(kPrefTranslateAcceptedCount);
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
   prefs_->ClearPref(kPrefTranslateAutoAlwaysCount);
   prefs_->ClearPref(kPrefTranslateAutoNeverCount);
 #endif
@@ -644,7 +647,7 @@
   update.Get()->SetInteger(language, 0);
 }
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
 int TranslatePrefs::GetTranslationAutoAlwaysCount(
     const std::string& language) const {
   const base::DictionaryValue* dict =
@@ -690,7 +693,9 @@
   DictionaryPrefUpdate update(prefs_, kPrefTranslateAutoNeverCount);
   update.Get()->SetInteger(language, 0);
 }
+#endif  // defined(OS_ANDROID) || defined(OS_IOS)
 
+#if defined(OS_ANDROID)
 bool TranslatePrefs::GetExplicitLanguageAskPromptShown() const {
   return prefs_->GetBoolean(kPrefExplicitLanguageAskShown);
 }
@@ -843,13 +848,16 @@
       kForceTriggerTranslateCount, 0,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
   registry->RegisterDictionaryPref(
       kPrefTranslateAutoAlwaysCount,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
   registry->RegisterDictionaryPref(
       kPrefTranslateAutoNeverCount,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
+#endif
+
+#if defined(OS_ANDROID)
   registry->RegisterBooleanPref(
       kPrefExplicitLanguageAskShown, false,
       user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
diff --git a/components/translate/core/browser/translate_prefs.h b/components/translate/core/browser/translate_prefs.h
index 38d145ec..5d39f62 100644
--- a/components/translate/core/browser/translate_prefs.h
+++ b/components/translate/core/browser/translate_prefs.h
@@ -138,9 +138,11 @@
   static const char kPrefTranslateLastDeniedTimeForLanguage[];
   static const char kPrefTranslateTooOftenDeniedForLanguage[];
   static const char kPrefTranslateRecentTarget[];
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
   static const char kPrefTranslateAutoAlwaysCount[];
   static const char kPrefTranslateAutoNeverCount[];
+#endif
+#if defined(OS_ANDROID)
   static const char kPrefExplicitLanguageAskShown[];
 #endif
 
@@ -259,7 +261,7 @@
   void IncrementTranslationAcceptedCount(const std::string& language);
   void ResetTranslationAcceptedCount(const std::string& language);
 
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) || defined(OS_IOS)
   // These methods are used to track how many times the auto-always translation
   // has been triggered for a specific language.
   int GetTranslationAutoAlwaysCount(const std::string& language) const;
@@ -271,7 +273,9 @@
   int GetTranslationAutoNeverCount(const std::string& language) const;
   void IncrementTranslationAutoNeverCount(const std::string& language);
   void ResetTranslationAutoNeverCount(const std::string& language);
+#endif
 
+#if defined(OS_ANDROID)
   // These methods are used to determine whether the explicit language ask
   // prompt was displayed to the user already.
   bool GetExplicitLanguageAskPromptShown() const;
diff --git a/components/translate/core/browser/translate_url_fetcher.cc b/components/translate/core/browser/translate_url_fetcher.cc
index 3699852..3aeaa20 100644
--- a/components/translate/core/browser/translate_url_fetcher.cc
+++ b/components/translate/core/browser/translate_url_fetcher.cc
@@ -102,7 +102,7 @@
     resource_request->headers.AddHeaderFromString(extra_request_header_);
 
   simple_loader_ =
-      variations::CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn(
+      variations::CreateSimpleURLLoaderWithVariationsHeaderUnknownSignedIn(
           std::move(resource_request),
           is_incognito ? variations::InIncognito::kYes
                        : variations::InIncognito::kNo,
diff --git a/components/ukm/ukm_reporting_service.cc b/components/ukm/ukm_reporting_service.cc
index a74c6ac..e2fe21d 100644
--- a/components/ukm/ukm_reporting_service.cc
+++ b/components/ukm/ukm_reporting_service.cc
@@ -40,13 +40,13 @@
 // limit is exceeded.
 constexpr size_t kMaxLogRetransmitSize = 100 * 1024;
 
-std::string GetServerUrl() {
+GURL GetServerUrl() {
   constexpr char kDefaultServerUrl[] = "https://clients4.google.com/ukm";
   std::string server_url =
       base::GetFieldTrialParamValueByFeature(kUkmFeature, "ServerUrl");
   if (!server_url.empty())
-    return server_url;
-  return kDefaultServerUrl;
+    return GURL(server_url);
+  return GURL(kDefaultServerUrl);
 }
 
 }  // namespace
@@ -75,12 +75,12 @@
   return &persisted_logs_;
 }
 
-std::string UkmReportingService::GetUploadUrl() const {
+GURL UkmReportingService::GetUploadUrl() const {
   return GetServerUrl();
 }
 
-std::string UkmReportingService::GetInsecureUploadUrl() const {
-  return "";
+GURL UkmReportingService::GetInsecureUploadUrl() const {
+  return GURL();
 }
 
 base::StringPiece UkmReportingService::upload_mime_type() const {
diff --git a/components/ukm/ukm_reporting_service.h b/components/ukm/ukm_reporting_service.h
index a8053951..07ffc6a 100644
--- a/components/ukm/ukm_reporting_service.h
+++ b/components/ukm/ukm_reporting_service.h
@@ -47,9 +47,9 @@
  private:
   // metrics:ReportingService:
   metrics::LogStore* log_store() override;
-  std::string GetUploadUrl() const override;
+  GURL GetUploadUrl() const override;
   // Returns an empty string since retrying over HTTP is not enabled for UKM
-  std::string GetInsecureUploadUrl() const override;
+  GURL GetInsecureUploadUrl() const override;
   base::StringPiece upload_mime_type() const override;
   metrics::MetricsLogUploader::MetricServiceType service_type() const override;
   void LogCellularConstraint(bool upload_canceled) override;
diff --git a/components/unified_consent/unified_consent_metrics.cc b/components/unified_consent/unified_consent_metrics.cc
index 9a76647..7bc53b2 100644
--- a/components/unified_consent/unified_consent_metrics.cc
+++ b/components/unified_consent/unified_consent_metrics.cc
@@ -4,22 +4,67 @@
 
 #include "components/unified_consent/unified_consent_metrics.h"
 
+#include <utility>
+
 #include "base/metrics/histogram_macros.h"
+#include "components/autofill/core/common/autofill_prefs.h"
 #include "components/prefs/pref_service.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/driver/sync_user_settings.h"
 #include "components/unified_consent/pref_names.h"
 
-namespace {
-
-// Histogram recorded at startup to log which Google services are enabled.
-const char kSyncAndGoogleServicesSettingsHistogram[] =
-    "UnifiedConsent.SyncAndGoogleServicesSettings";
-
-}  // namespace
-
 namespace unified_consent {
 
 namespace metrics {
 
+namespace {
+
+typedef std::pair<SyncDataType, syncer::ModelType> DT;
+
+// Records a sample in the SyncAndGoogleServicesSettings histogram. Wrapped in a
+// function to avoid code size issues caused by histogram macros.
+void RecordSettingsHistogramSample(SettingsHistogramValue value) {
+  UMA_HISTOGRAM_ENUMERATION("UnifiedConsent.SyncAndGoogleServicesSettings",
+                            value);
+}
+
+// Checks if a pref is enabled and if so, records a sample in the
+// SyncAndGoogleServicesSettings histogram. Returns true if a sample was
+// recorded.
+bool RecordSettingsHistogramFromPref(const char* pref_name,
+                                     PrefService* pref_service,
+                                     SettingsHistogramValue value) {
+  if (!pref_service->GetBoolean(pref_name))
+    return false;
+  RecordSettingsHistogramSample(value);
+  return true;
+}
+
+// Checks if a service is enabled and if so, records a sample in the
+// SyncAndGoogleServicesSettings histogram. Returns true if a sample was
+// recorded.
+bool RecordSettingsHistogramFromService(
+    UnifiedConsentServiceClient* client,
+    UnifiedConsentServiceClient::Service service,
+    SettingsHistogramValue value) {
+  if (client->GetServiceState(service) !=
+      UnifiedConsentServiceClient::ServiceState::kEnabled) {
+    return false;
+  }
+
+  RecordSettingsHistogramSample(value);
+  return true;
+}
+
+void RecordSyncDataTypeSample(SyncDataType data_type) {
+  UMA_HISTOGRAM_ENUMERATION(
+      "UnifiedConsent.SyncAndGoogleServicesSettings.AfterAdvancedOptIn."
+      "SyncDataTypesOff",
+      data_type);
+}
+
+}  // namespace
+
 void RecordSettingsHistogram(UnifiedConsentServiceClient* service_client,
                              PrefService* pref_service) {
   bool metric_recorded = false;
@@ -42,30 +87,32 @@
     RecordSettingsHistogramSample(metrics::SettingsHistogramValue::kNone);
 }
 
-void RecordSettingsHistogramSample(SettingsHistogramValue value) {
-  UMA_HISTOGRAM_ENUMERATION(kSyncAndGoogleServicesSettingsHistogram, value);
-}
+void RecordSyncSetupDataTypesHistrogam(syncer::SyncUserSettings* sync_settings,
+                                       PrefService* pref_service) {
+  bool metric_recorded = false;
 
-bool RecordSettingsHistogramFromPref(const char* pref_name,
-                                     PrefService* pref_service,
-                                     SettingsHistogramValue value) {
-  if (!pref_service->GetBoolean(pref_name))
-    return false;
-  RecordSettingsHistogramSample(value);
-  return true;
-}
-
-bool RecordSettingsHistogramFromService(
-    UnifiedConsentServiceClient* client,
-    UnifiedConsentServiceClient::Service service,
-    SettingsHistogramValue value) {
-  if (client->GetServiceState(service) !=
-      UnifiedConsentServiceClient::ServiceState::kEnabled) {
-    return false;
+  for (DT data_type : {DT(SyncDataType::kApps, syncer::APPS),
+                       DT(SyncDataType::kBookmarks, syncer::BOOKMARKS),
+                       DT(SyncDataType::kExtensions, syncer::EXTENSIONS),
+                       DT(SyncDataType::kHistory, syncer::TYPED_URLS),
+                       DT(SyncDataType::kSettings, syncer::PREFERENCES),
+                       DT(SyncDataType::kThemes, syncer::THEMES),
+                       DT(SyncDataType::kTabs, syncer::PROXY_TABS),
+                       DT(SyncDataType::kPasswords, syncer::PASSWORDS),
+                       DT(SyncDataType::kAutofill, syncer::AUTOFILL)}) {
+    if (!sync_settings->GetChosenDataTypes().Has(data_type.second)) {
+      RecordSyncDataTypeSample(data_type.first);
+      metric_recorded = true;
+    }
   }
 
-  RecordSettingsHistogramSample(value);
-  return true;
+  if (!autofill::prefs::IsPaymentsIntegrationEnabled(pref_service)) {
+    RecordSyncDataTypeSample(SyncDataType::kPayments);
+    metric_recorded = true;
+  }
+
+  if (!metric_recorded)
+    RecordSyncDataTypeSample(SyncDataType::kNone);
 }
 
 }  // namespace metrics
diff --git a/components/unified_consent/unified_consent_metrics.h b/components/unified_consent/unified_consent_metrics.h
index aaf73a9..9b16c624 100644
--- a/components/unified_consent/unified_consent_metrics.h
+++ b/components/unified_consent/unified_consent_metrics.h
@@ -7,6 +7,10 @@
 
 #include "components/unified_consent/unified_consent_service_client.h"
 
+namespace syncer {
+class SyncUserSettings;
+}
+
 namespace unified_consent {
 
 namespace metrics {
@@ -26,29 +30,34 @@
   kMaxValue = kAllServicesWereEnabled
 };
 
-// Records settings entries in the kSyncAndGoogleServicesSettingsHistogram.
+// Sync data types that can be customized in settings.
+// Used in histograms. Do not change existing values, append new values at the
+// end.
+enum class SyncDataType {
+  kNone = 0,
+  kApps = 1,
+  kBookmarks = 2,
+  kExtensions = 3,
+  kHistory = 4,
+  kSettings = 5,
+  kThemes = 6,
+  kTabs = 7,
+  kPasswords = 8,
+  kAutofill = 9,
+  kPayments = 10,
+
+  kMaxValue = kPayments
+};
+
+// Records settings entries in the SyncAndGoogleServicesSettings.
 // kNone is recorded when none of the settings is enabled.
 void RecordSettingsHistogram(UnifiedConsentServiceClient* service_client,
                              PrefService* pref_service);
 
-// Records a sample in the kSyncAndGoogleServicesSettingsHistogram. Wrapped in a
-// function to avoid code size issues caused by histogram macros.
-void RecordSettingsHistogramSample(SettingsHistogramValue value);
-
-// Checks if a pref is enabled and if so, records a sample in the
-// kSyncAndGoogleServicesSettingsHistogram. Returns true if a sample was
-// recorded.
-bool RecordSettingsHistogramFromPref(const char* pref_name,
-                                     PrefService* pref_service,
-                                     SettingsHistogramValue value);
-
-// Checks if a service is enabled and if so, records a sample in the
-// kSyncAndGoogleServicesSettingsHistogram. Returns true if a sample was
-// recorded.
-bool RecordSettingsHistogramFromService(
-    UnifiedConsentServiceClient* client,
-    UnifiedConsentServiceClient::Service service,
-    SettingsHistogramValue value);
+// Records the sync data types that were turned off during the advanced sync
+// opt-in flow. When none of the data types were turned off, kNone is recorded.
+void RecordSyncSetupDataTypesHistrogam(syncer::SyncUserSettings* sync_settings,
+                                       PrefService* pref_service);
 
 }  // namespace metrics
 
diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc
index a0be5b7..a41f669 100644
--- a/components/user_manager/user_manager_base.cc
+++ b/components/user_manager/user_manager_base.cc
@@ -333,13 +333,18 @@
 
 void UserManagerBase::RemoveNonOwnerUserInternal(const AccountId& account_id,
                                                  RemoveUserDelegate* delegate) {
+  // If account_id points to AccountId in User object, it will become deleted
+  // after RemoveUserFromList(), which could lead to use-after-free in observer.
+  // TODO(https://crbug.com/928534): Update user removal flow to prevent this.
+  const AccountId account_id_copy(account_id);
+
   if (delegate)
     delegate->OnBeforeUserRemoved(account_id);
   AsyncRemoveCryptohome(account_id);
   RemoveUserFromList(account_id);
 
   if (delegate)
-    delegate->OnUserRemoved(account_id);
+    delegate->OnUserRemoved(account_id_copy);
 }
 
 void UserManagerBase::RemoveUserFromList(const AccountId& account_id) {
diff --git a/components/variations/net/variations_http_headers.cc b/components/variations/net/variations_http_headers.cc
index 072b67b..1ae29e4 100644
--- a/components/variations/net/variations_http_headers.cc
+++ b/components/variations/net/variations_http_headers.cc
@@ -6,6 +6,7 @@
 
 #include <stddef.h>
 
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -28,6 +29,10 @@
 
 namespace {
 
+// The name string for the header for variations information.
+// Note that prior to M33 this header was named X-Chrome-Variations.
+const char kClientDataHeader[] = "X-Client-Data";
+
 const char* kSuffixesToSetHeadersFor[] = {
     ".android.com",
     ".doubleclick.com",
@@ -94,89 +99,165 @@
                             URL_VALIDATION_RESULT_SIZE);
 }
 
-// Removes variations headers for requests when a redirect to a non-Google URL
-// occurs. This function is used as the callback parameter for
-// SimpleURLLoader::SetOnRedirectCallback() when
-// CreateSimpleURLLoaderWithVariationsHeaders() creates a SimpleURLLoader
-// object.
-void RemoveVariationsHeader(const net::RedirectInfo& redirect_info,
-                            const network::ResourceResponseHead& response_head,
-                            std::vector<std::string>* to_be_removed_headers) {
-  if (!ShouldAppendVariationHeaders(redirect_info.new_url))
-    to_be_removed_headers->push_back(kClientDataHeader);
-}
+constexpr network::ResourceRequest* null_resource_request = nullptr;
+constexpr net::URLRequest* null_url_request = nullptr;
+
+class VariationsHeaderHelper {
+ public:
+  // Note: It's OK to pass SignedIn::kNo if it's unknown, as it does not affect
+  // transmission of experiments coming from the variations server.
+  VariationsHeaderHelper(network::ResourceRequest* request,
+                         SignedIn signed_in = SignedIn::kNo)
+      : VariationsHeaderHelper(request,
+                               null_url_request,
+                               CreateVariationsHeader(signed_in)) {}
+  VariationsHeaderHelper(net::URLRequest* request,
+                         SignedIn signed_in = SignedIn::kNo)
+      : VariationsHeaderHelper(null_resource_request,
+                               request,
+                               CreateVariationsHeader(signed_in)) {}
+  VariationsHeaderHelper(network::ResourceRequest* request,
+                         const std::string& variations_header)
+      : VariationsHeaderHelper(request, null_url_request, variations_header) {}
+
+  bool AppendHeaderIfNeeded(const GURL& url, InIncognito incognito) {
+    // Note the criteria for attaching client experiment headers:
+    // 1. We only transmit to Google owned domains which can evaluate
+    // experiments.
+    //    1a. These include hosts which have a standard postfix such as:
+    //         *.doubleclick.net or *.googlesyndication.com or
+    //         exactly www.googleadservices.com or
+    //         international TLD domains *.google.<TLD> or *.youtube.<TLD>.
+    // 2. Only transmit for non-Incognito profiles.
+    // 3. For the X-Client-Data header, only include non-empty variation IDs.
+    if ((incognito == InIncognito::kYes) || !ShouldAppendVariationsHeader(url))
+      return false;
+
+    if (variations_header_.empty())
+      return false;
+
+    if (resource_request_) {
+      // Set the variations header to client_data_header rather than headers to
+      // be exempted from CORS checks.
+      resource_request_->client_data_header = variations_header_;
+    } else if (url_request_) {
+      url_request_->SetExtraRequestHeaderByName(kClientDataHeader,
+                                                variations_header_, false);
+    } else {
+      NOTREACHED();
+      return false;
+    }
+    return true;
+  }
+
+ private:
+  static std::string CreateVariationsHeader(SignedIn signed_in) {
+    return VariationsHttpHeaderProvider::GetInstance()->GetClientDataHeader(
+        signed_in == SignedIn::kYes);
+  }
+
+  VariationsHeaderHelper(network::ResourceRequest* resource_request,
+                         net::URLRequest* url_request,
+                         std::string variations_header)
+      : resource_request_(resource_request), url_request_(url_request) {
+    DCHECK(resource_request_ || url_request_);
+    variations_header_ = std::move(variations_header);
+  }
+
+  network::ResourceRequest* resource_request_;
+  net::URLRequest* url_request_;
+  std::string variations_header_;
+
+  DISALLOW_COPY_AND_ASSIGN(VariationsHeaderHelper);
+};
 
 }  // namespace
 
-const char kClientDataHeader[] = "X-Client-Data";
-
-bool AppendVariationHeaders(const GURL& url,
+bool AppendVariationsHeader(const GURL& url,
                             InIncognito incognito,
                             SignedIn signed_in,
-                            net::HttpRequestHeaders* headers) {
-  // Note the criteria for attaching client experiment headers:
-  // 1. We only transmit to Google owned domains which can evaluate experiments.
-  //    1a. These include hosts which have a standard postfix such as:
-  //         *.doubleclick.net or *.googlesyndication.com or
-  //         exactly www.googleadservices.com or
-  //         international TLD domains *.google.<TLD> or *.youtube.<TLD>.
-  // 2. Only transmit for non-Incognito profiles.
-  // 3. For the X-Client-Data header, only include non-empty variation IDs.
-  if ((incognito == InIncognito::kYes) || !ShouldAppendVariationHeaders(url))
-    return false;
-
-  const std::string variation_ids_header =
-      VariationsHttpHeaderProvider::GetInstance()->GetClientDataHeader(
-          signed_in == SignedIn::kYes);
-  if (!variation_ids_header.empty()) {
-    // Note that prior to M33 this header was named X-Chrome-Variations.
-    headers->SetHeaderIfMissing(kClientDataHeader, variation_ids_header);
-    return true;
-  }
-  return false;
+                            network::ResourceRequest* request) {
+  return VariationsHeaderHelper(request, signed_in)
+      .AppendHeaderIfNeeded(url, incognito);
 }
 
-bool AppendVariationHeadersUnknownSignedIn(const GURL& url,
+bool AppendVariationsHeader(const GURL& url,
+                            InIncognito incognito,
+                            SignedIn signed_in,
+                            net::URLRequest* request) {
+  return VariationsHeaderHelper(request, signed_in)
+      .AppendHeaderIfNeeded(url, incognito);
+}
+
+bool AppendVariationsHeaderWithCustomValue(const GURL& url,
                                            InIncognito incognito,
-                                           net::HttpRequestHeaders* headers) {
-  // Note: It's OK to pass SignedIn::kNo if it's unknown, as it does not affect
-  // transmission of experiments coming from the variations server.
-  return AppendVariationHeaders(url, incognito, SignedIn::kNo, headers);
+                                           const std::string& variations_header,
+                                           network::ResourceRequest* request) {
+  return VariationsHeaderHelper(request, variations_header)
+      .AppendHeaderIfNeeded(url, incognito);
 }
 
-void StripVariationHeaderIfNeeded(const GURL& new_location,
-                                  net::URLRequest* request) {
-  if (!ShouldAppendVariationHeaders(new_location))
+bool AppendVariationsHeaderUnknownSignedIn(const GURL& url,
+                                           InIncognito incognito,
+                                           network::ResourceRequest* request) {
+  return VariationsHeaderHelper(request).AppendHeaderIfNeeded(url, incognito);
+}
+
+bool AppendVariationsHeaderUnknownSignedIn(const GURL& url,
+                                           InIncognito incognito,
+                                           net::URLRequest* request) {
+  return VariationsHeaderHelper(request).AppendHeaderIfNeeded(url, incognito);
+}
+
+void RemoveVariationsHeaderIfNeeded(
+    const net::RedirectInfo& redirect_info,
+    const network::ResourceResponseHead& response_head,
+    std::vector<std::string>* to_be_removed_headers) {
+  if (!ShouldAppendVariationsHeader(redirect_info.new_url))
+    to_be_removed_headers->push_back(kClientDataHeader);
+}
+
+void StripVariationsHeaderIfNeeded(const GURL& new_location,
+                                   net::URLRequest* request) {
+  if (!ShouldAppendVariationsHeader(new_location))
     request->RemoveRequestHeaderByName(kClientDataHeader);
 }
 
 std::unique_ptr<network::SimpleURLLoader>
-CreateSimpleURLLoaderWithVariationsHeaders(
+CreateSimpleURLLoaderWithVariationsHeader(
     std::unique_ptr<network::ResourceRequest> request,
     InIncognito incognito,
     SignedIn signed_in,
     const net::NetworkTrafficAnnotationTag& annotation_tag) {
-  bool variation_headers_added = AppendVariationHeaders(
-      request->url, incognito, signed_in, &request->headers);
+  bool variation_headers_added =
+      AppendVariationsHeader(request->url, incognito, signed_in, request.get());
   std::unique_ptr<network::SimpleURLLoader> simple_url_loader =
       network::SimpleURLLoader::Create(std::move(request), annotation_tag);
   if (variation_headers_added) {
     simple_url_loader->SetOnRedirectCallback(
-        base::BindRepeating(&RemoveVariationsHeader));
+        base::BindRepeating(&RemoveVariationsHeaderIfNeeded));
   }
   return simple_url_loader;
 }
 
 std::unique_ptr<network::SimpleURLLoader>
-CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn(
+CreateSimpleURLLoaderWithVariationsHeaderUnknownSignedIn(
     std::unique_ptr<network::ResourceRequest> request,
     InIncognito incognito,
     const net::NetworkTrafficAnnotationTag& annotation_tag) {
-  return CreateSimpleURLLoaderWithVariationsHeaders(
+  return CreateSimpleURLLoaderWithVariationsHeader(
       std::move(request), incognito, SignedIn::kNo, annotation_tag);
 }
 
-bool ShouldAppendVariationHeaders(const GURL& url) {
+bool IsVariationsHeader(const std::string& header_name) {
+  return header_name == kClientDataHeader;
+}
+
+bool HasVariationsHeader(const network::ResourceRequest& request) {
+  return !request.client_data_header.empty();
+}
+
+bool ShouldAppendVariationsHeader(const GURL& url) {
   if (!url.is_valid()) {
     LogUrlValidationHistogram(INVALID_URL);
     return false;
diff --git a/components/variations/net/variations_http_headers.h b/components/variations/net/variations_http_headers.h
index 4530a6d..ab62563 100644
--- a/components/variations/net/variations_http_headers.h
+++ b/components/variations/net/variations_http_headers.h
@@ -8,15 +8,17 @@
 #include <memory>
 #include <set>
 #include <string>
+#include <vector>
 
 namespace net {
-class HttpRequestHeaders;
 struct NetworkTrafficAnnotationTag;
+struct RedirectInfo;
 class URLRequest;
 }
 
 namespace network {
 struct ResourceRequest;
+struct ResourceResponseHead;
 class SimpleURLLoader;
 }  // namespace network
 
@@ -28,10 +30,7 @@
 
 enum class SignedIn { kNo, kYes };
 
-// The name string for the header for variations information.
-extern const char kClientDataHeader[];
-
-// Adds Chrome experiment and metrics state as custom headers to |headers|.
+// Adds Chrome experiment and metrics state as custom headers to |request|.
 // The content of the headers will depend on |incognito| and |signed_in|
 // parameters. It is fine to pass SignedIn::NO if the state is not known to the
 // caller. This will prevent addition of ids of type
@@ -39,46 +38,80 @@
 // from the variations server. These headers are never transmitted to non-Google
 // web sites, which is checked based on the destination |url|.
 // Returns true if custom headers are added. Returns false otherwise.
-bool AppendVariationHeaders(const GURL& url,
+bool AppendVariationsHeader(const GURL& url,
                             InIncognito incognito,
                             SignedIn signed_in,
-                            net::HttpRequestHeaders* headers);
+                            network::ResourceRequest* request);
 
-// Adds Chrome experiment and metrics state as custom headers to |headers|
-// when the signed-in state is not known to the caller; See above for details.
-bool AppendVariationHeadersUnknownSignedIn(const GURL& url,
+// TODO(toyoshim): Remove this deprecated API that takes net::URLRequest* once
+// all callers are removed after NetworkService being fully enabled, or migrated
+// to use SimpleURLLoader. See, crbug.com/773295.
+bool AppendVariationsHeader(const GURL& url,
+                            InIncognito incognito,
+                            SignedIn signed_in,
+                            net::URLRequest* request);
+
+// Similar to functions above, but uses specified |variations_header| as the
+// custom header value. You should not generally need to use this.
+bool AppendVariationsHeaderWithCustomValue(const GURL& url,
                                            InIncognito incognito,
-                                           net::HttpRequestHeaders* headers);
+                                           const std::string& variations_header,
+                                           network::ResourceRequest* request);
 
-// Strips the variation header if |new_location| does not point to a location
+// Adds Chrome experiment and metrics state as a custom header to |request|
+// when the signed-in state is not known to the caller; See above for details.
+bool AppendVariationsHeaderUnknownSignedIn(const GURL& url,
+                                           InIncognito incognito,
+                                           network::ResourceRequest* request);
+
+// TODO(toyoshim): Remove this deprecated API that takes net::URLRequest* once
+// all callers are removed after NetworkService being fully enabled, or migrated
+// to use SimpleURLLoader. See, crbug.com/773295.
+bool AppendVariationsHeaderUnknownSignedIn(const GURL& url,
+                                           InIncognito incognito,
+                                           net::URLRequest* request);
+
+// Removes the variations header for requests when a redirect to a non-Google
+// URL occurs.
+void RemoveVariationsHeaderIfNeeded(
+    const net::RedirectInfo& redirect_info,
+    const network::ResourceResponseHead& response_head,
+    std::vector<std::string>* to_be_removed_headers);
+
+// Strips the variations header if |new_location| does not point to a location
 // that should receive it. This is being called by the ChromeNetworkDelegate.
-// Components calling AppendVariationsHeaders() don't need to take care of this.
-void StripVariationHeaderIfNeeded(const GURL& new_location,
-                                  net::URLRequest* request);
+// Components calling AppendVariationsHeader() don't need to take care of this.
+void StripVariationsHeaderIfNeeded(const GURL& new_location,
+                                   net::URLRequest* request);
 
-// Creates a SimpleURLLoader that will include variations headers for requests
-// to Google and ensures they're removed if a redirect to a non-Google URL
-// occurs.
+// Creates a SimpleURLLoader that will include the variations header for
+// requests to Google and ensures they're removed if a redirect to a non-Google
+// URL occurs.
 std::unique_ptr<network::SimpleURLLoader>
-CreateSimpleURLLoaderWithVariationsHeaders(
+CreateSimpleURLLoaderWithVariationsHeader(
     std::unique_ptr<network::ResourceRequest> request,
     InIncognito incognito,
     SignedIn signed_in,
     const net::NetworkTrafficAnnotationTag& annotation_tag);
 
-// Creates a SimpleURLLoader that will include variations headers for requests
-// to Google when the signed-in state is unknown and ensures they're removed
-// if a redirect to a non-Google URL occurs.
+// Creates a SimpleURLLoader that will include the variations header for
+// requests to Google when the signed-in state is unknown and ensures they're
+// removed if a redirect to a non-Google URL occurs.
 std::unique_ptr<network::SimpleURLLoader>
-CreateSimpleURLLoaderWithVariationsHeadersUnknownSignedIn(
+CreateSimpleURLLoaderWithVariationsHeaderUnknownSignedIn(
     std::unique_ptr<network::ResourceRequest> request,
     InIncognito incognito,
     const net::NetworkTrafficAnnotationTag& annotation_tag);
 
+// Checks if |header_name| is one for the variations header.
+bool IsVariationsHeader(const std::string& header_name);
+
+// Checks if |request| contains the variations header.
+bool HasVariationsHeader(const network::ResourceRequest& request);
+
 // Checks whether variation headers should be appended to requests to the
-// specified |url|. Returns true for google.<TLD> and youtube.<TLD> URLs with
-// the https scheme.
-bool ShouldAppendVariationHeaders(const GURL& url);
+// specified |url|. Returns true for Google's hosts served over secure schemes.
+bool ShouldAppendVariationsHeader(const GURL& url);
 
 }  // namespace variations
 
diff --git a/components/variations/net/variations_http_headers_unittest.cc b/components/variations/net/variations_http_headers_unittest.cc
index 87dfc2d..56d8c9eb 100644
--- a/components/variations/net/variations_http_headers_unittest.cc
+++ b/components/variations/net/variations_http_headers_unittest.cc
@@ -13,7 +13,7 @@
 
 namespace variations {
 
-TEST(VariationsHttpHeadersTest, ShouldAppendHeaders) {
+TEST(VariationsHttpHeadersTest, ShouldAppendVariationsHeader) {
   struct {
     const char* url;
     bool should_append_headers;
@@ -154,7 +154,7 @@
 
   for (size_t i = 0; i < base::size(cases); ++i) {
     const GURL url(cases[i].url);
-    EXPECT_EQ(cases[i].should_append_headers, ShouldAppendVariationHeaders(url))
+    EXPECT_EQ(cases[i].should_append_headers, ShouldAppendVariationsHeader(url))
         << url;
   }
 }
diff --git a/components/variations/variations_http_header_provider.cc b/components/variations/variations_http_header_provider.cc
index 2908461..67086009 100644
--- a/components/variations/variations_http_header_provider.cc
+++ b/components/variations/variations_http_header_provider.cc
@@ -40,7 +40,7 @@
 // ChromeContentBrowserClient::CreateURLLoaderThrottles() by also adding a
 // GoogleURLLoaderThrottle to a content::URLLoaderThrottle vector.
 // 3. SimpleURLLoader in browser, it is implemented in a SimpleURLLoader wrapper
-// function variations::CreateSimpleURLLoaderWithVariationsHeaders().
+// function variations::CreateSimpleURLLoaderWithVariationsHeader().
 
 // static
 VariationsHttpHeaderProvider* VariationsHttpHeaderProvider::GetInstance() {
diff --git a/components/viz/common/quads/shared_quad_state.h b/components/viz/common/quads/shared_quad_state.h
index aa1cc84b..1de514c1 100644
--- a/components/viz/common/quads/shared_quad_state.h
+++ b/components/viz/common/quads/shared_quad_state.h
@@ -60,6 +60,13 @@
   float opacity;
   SkBlendMode blend_mode;
   int sorting_context_id;
+  // An internal flag used only in the SurfaceAggregator and the
+  // OverlayProcessor. If set to true surface's compositor frame was updated in
+  // current aggregation, and if set to false the surface has not changed since
+  // the previous frame. It can be used for underlay optimization when the quads
+  // on top are not damaged. SetAll() doesn't update this flag. It has to be set
+  // sepaerately.
+  bool has_surface_damage = false;
 };
 
 }  // namespace viz
diff --git a/components/viz/common/surfaces/child_local_surface_id_allocator.cc b/components/viz/common/surfaces/child_local_surface_id_allocator.cc
index b7ab824..ccd0bd3 100644
--- a/components/viz/common/surfaces/child_local_surface_id_allocator.cc
+++ b/components/viz/common/surfaces/child_local_surface_id_allocator.cc
@@ -92,4 +92,13 @@
       current_local_surface_id_allocation_.local_surface_id_.ToString());
 }
 
+void ChildLocalSurfaceIdAllocator::GenerateIdOrIncrementChild() {
+  if (current_local_surface_id_allocation_.IsValid()) {
+    GenerateId();
+  } else {
+    ++current_local_surface_id_allocation_.local_surface_id_
+          .child_sequence_number_;
+  }
+}
+
 }  // namespace viz
diff --git a/components/viz/common/surfaces/child_local_surface_id_allocator.h b/components/viz/common/surfaces/child_local_surface_id_allocator.h
index 92f5e15..653cbf5 100644
--- a/components/viz/common/surfaces/child_local_surface_id_allocator.h
+++ b/components/viz/common/surfaces/child_local_surface_id_allocator.h
@@ -43,6 +43,10 @@
 
   void GenerateId();
 
+  // If UpdateFromParent() has been called this calls GenerateId(), otherwise
+  // the child sequence number is advanced.
+  void GenerateIdOrIncrementChild();
+
   const LocalSurfaceIdAllocation& GetCurrentLocalSurfaceIdAllocation() const {
     return current_local_surface_id_allocation_;
   }
diff --git a/components/viz/service/BUILD.gn b/components/viz/service/BUILD.gn
index 3ecb373d..ffd67e69 100644
--- a/components/viz/service/BUILD.gn
+++ b/components/viz/service/BUILD.gn
@@ -98,6 +98,8 @@
     "display_embedder/compositing_mode_reporter_impl.cc",
     "display_embedder/compositing_mode_reporter_impl.h",
     "display_embedder/compositor_overlay_candidate_validator.h",
+    "display_embedder/direct_context_provider.cc",
+    "display_embedder/direct_context_provider.h",
     "display_embedder/display_provider.h",
     "display_embedder/gl_output_surface.cc",
     "display_embedder/gl_output_surface.h",
diff --git a/components/viz/service/display/dc_layer_overlay.cc b/components/viz/service/display/dc_layer_overlay.cc
index 2a4e470..bcaf4678 100644
--- a/components/viz/service/display/dc_layer_overlay.cc
+++ b/components/viz/service/display/dc_layer_overlay.cc
@@ -13,6 +13,7 @@
 #include "components/viz/service/display/display_resource_provider.h"
 #include "components/viz/service/display/output_surface.h"
 #include "gpu/GLES2/gl2extchromium.h"
+#include "gpu/config/gpu_finch_features.h"
 #include "ui/gfx/geometry/rect_conversions.h"
 #include "ui/gl/gl_switches.h"
 
@@ -104,12 +105,15 @@
   return quad_rect;
 }
 
-// Find a rectangle containing all the quads in a list that occlude the area
-// in target_quad.
+// GetOcclusionBounds() - Find a rectangle containing all the quads in a list
+// that occlude the area in target_quad.
+// |has_occluding_surface_damage| - used for underlay power optimization.
 gfx::RectF GetOcclusionBounds(const gfx::RectF& target_quad,
                               QuadList::ConstIterator quad_list_begin,
-                              QuadList::ConstIterator quad_list_end) {
+                              QuadList::ConstIterator quad_list_end,
+                              bool* has_occluding_surface_damage) {
   gfx::RectF occlusion_bounding_box;
+  *has_occluding_surface_damage = false;
   for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end;
        ++overlap_iter) {
     float opacity = overlap_iter->shared_quad_state->opacity;
@@ -127,6 +131,8 @@
     overlap_rect.Intersect(target_quad);
     if (!overlap_rect.IsEmpty()) {
       occlusion_bounding_box.Union(overlap_rect);
+      *has_occluding_surface_damage |=
+          overlap_iter->shared_quad_state->has_surface_damage;
     }
   }
   return occlusion_bounding_box;
@@ -340,8 +346,11 @@
 
     // These rects are in quad target space.
     gfx::Rect quad_rectangle = gfx::ToEnclosingRect(ClippedQuadRectangle(*it));
+    bool has_occluding_surface_damage = false;
     gfx::RectF occlusion_bounding_box =
-        GetOcclusionBounds(gfx::RectF(quad_rectangle), quad_list->begin(), it);
+        GetOcclusionBounds(gfx::RectF(quad_rectangle), quad_list->begin(), it,
+                           &has_occluding_surface_damage);
+
     bool processed_overlay = false;
 
     // Underlays are less efficient, so attempt regular overlays first. Only
@@ -362,7 +371,8 @@
       processed_overlay = true;
     } else if (ProcessForUnderlay(display_rect, render_pass, quad_rectangle,
                                   occlusion_bounding_box, it, is_root,
-                                  damage_rect, &this_frame_underlay_rect,
+                                  has_occluding_surface_damage, damage_rect,
+                                  &this_frame_underlay_rect,
                                   &this_frame_underlay_occlusion, &dc_layer)) {
       processed_overlay = true;
     }
@@ -418,6 +428,7 @@
     const gfx::RectF& occlusion_bounding_box,
     const QuadList::Iterator& it,
     bool is_root,
+    bool has_occluding_surface_damage,
     gfx::Rect* damage_rect,
     gfx::Rect* this_frame_underlay_rect,
     gfx::Rect* this_frame_underlay_occlusion,
@@ -503,13 +514,21 @@
     gfx::Rect occluding_damage_rect = *damage_rect;
     damage_rect->Subtract(quad_rectangle);
 
-    gfx::Rect occlusion = gfx::ToEnclosingRect(occlusion_bounding_box);
-    occlusion.Union(previous_frame_underlay_occlusion_);
+    // If none of the quads on top give any damage, we can skip compositing
+    // these quads when the incoming damage rect is smaller or equal to the
+    // video quad. After subtraction, the resulting output damage rect for GL
+    // compositor will be empty. If the incoming damage rect is bigger than the
+    // video quad, we don't have an oppertunity for power optimization even if
+    // no damage on top. The output damage rect will not be empty in this case.
+    if (has_occluding_surface_damage) {
+      gfx::Rect occlusion = gfx::ToEnclosingRect(occlusion_bounding_box);
+      occlusion.Union(previous_frame_underlay_occlusion_);
 
-    occluding_damage_rect.Intersect(quad_rectangle);
-    occluding_damage_rect.Intersect(occlusion);
+      occluding_damage_rect.Intersect(quad_rectangle);
+      occluding_damage_rect.Intersect(occlusion);
 
-    damage_rect->Union(occluding_damage_rect);
+      damage_rect->Union(occluding_damage_rect);
+    }
   } else {
     // Entire replacement quad must be redrawn.
     // TODO(sunnyps): We should avoid this extra damage if we knew that the
diff --git a/components/viz/service/display/dc_layer_overlay.h b/components/viz/service/display/dc_layer_overlay.h
index 3e9bdbd..8f70836c 100644
--- a/components/viz/service/display/dc_layer_overlay.h
+++ b/components/viz/service/display/dc_layer_overlay.h
@@ -109,6 +109,7 @@
                           const gfx::RectF& occlusion_bounding_box,
                           const QuadList::Iterator& it,
                           bool is_root,
+                          bool has_occluding_surface_damage,
                           gfx::Rect* damage_rect,
                           gfx::Rect* this_frame_underlay_rect,
                           gfx::Rect* this_frame_underlay_occlusion,
diff --git a/components/viz/service/display/gl_renderer_unittest.cc b/components/viz/service/display/gl_renderer_unittest.cc
index 0fc3f992..51be581 100644
--- a/components/viz/service/display/gl_renderer_unittest.cc
+++ b/components/viz/service/display/gl_renderer_unittest.cc
@@ -41,6 +41,7 @@
 #include "components/viz/test/test_shared_bitmap_manager.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/context_support.h"
+#include "gpu/config/gpu_finch_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkMatrix.h"
diff --git a/components/viz/service/display/overlay_unittest.cc b/components/viz/service/display/overlay_unittest.cc
index 43db1b4..645fee87 100644
--- a/components/viz/service/display/overlay_unittest.cc
+++ b/components/viz/service/display/overlay_unittest.cc
@@ -35,6 +35,7 @@
 #include "components/viz/test/test_context_provider.h"
 #include "components/viz/test/test_gles2_interface.h"
 #include "components/viz/test/test_shared_bitmap_manager.h"
+#include "gpu/config/gpu_finch_features.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/gfx/geometry/rect_conversions.h"
@@ -254,6 +255,7 @@
 
   SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
   shared_state->opacity = 1.f;
+  shared_state->has_surface_damage = true;
   return pass;
 }
 
@@ -2672,6 +2674,56 @@
   }
 }
 
+TEST_F(DCLayerOverlayTest, UnderlayDamageRectWithQuadOnTopUnchanged) {
+  base::test::ScopedFeatureList feature_list;
+  feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays);
+
+  for (int i = 0; i < 3; i++) {
+    std::unique_ptr<RenderPass> pass = CreateRenderPass();
+    // Add a solid color quad on top
+    SharedQuadState* shared_state_on_top = pass->shared_quad_state_list.back();
+    CreateSolidColorQuadAt(shared_state_on_top, SK_ColorRED, pass.get(),
+                           kOverlayBottomRightRect);
+
+    SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState();
+    shared_state->opacity = 1.f;
+    CreateFullscreenCandidateYUVVideoQuad(
+        resource_provider_.get(), child_resource_provider_.get(),
+        child_provider_.get(), shared_state, pass.get());
+
+    DCLayerOverlayList dc_layer_list;
+    OverlayCandidateList overlay_list;
+    OverlayProcessor::FilterOperationsMap render_pass_filters;
+    OverlayProcessor::FilterOperationsMap render_pass_backdrop_filters;
+    RenderPassList pass_list;
+    pass_list.push_back(std::move(pass));
+    gfx::Rect damage_rect_ = kOverlayRect;
+
+    // The quad on top does not give damage on the third frame
+    if (i == 2)
+      shared_state_on_top->has_surface_damage = false;
+    else
+      shared_state_on_top->has_surface_damage = true;
+
+    overlay_processor_->ProcessForOverlays(
+        resource_provider_.get(), &pass_list, GetIdentityColorMatrix(),
+        render_pass_filters, render_pass_backdrop_filters, &overlay_list,
+        nullptr, &dc_layer_list, &damage_rect_, &content_bounds_);
+    EXPECT_EQ(0U, overlay_list.size());
+    EXPECT_EQ(1U, dc_layer_list.size());
+    EXPECT_EQ(0U, output_surface_->bind_framebuffer_count());
+    EXPECT_EQ(-1, dc_layer_list.back().z_order);
+    // Damage rect should be unchanged on initial frame, but should be reduced
+    // to the size of quad on top, and empty on the third frame.
+    if (i == 0)
+      EXPECT_EQ(kOverlayRect, damage_rect_);
+    else if (i == 1)
+      EXPECT_EQ(kOverlayBottomRightRect, damage_rect_);
+    else if (i == 2)
+      EXPECT_EQ(gfx::Rect(), damage_rect_);
+  }
+}
+
 class OverlayInfoRendererGL : public GLRenderer {
  public:
   OverlayInfoRendererGL(const RendererSettings* settings,
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index c43c243..f749b54 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -147,16 +147,25 @@
   }
 }
 
-gfx::Rect SurfaceAggregator::DamageRectForSurface(
-    const Surface* surface,
-    const RenderPass& source,
-    const gfx::Rect& full_rect) const {
+bool SurfaceAggregator::IsSurfaceFrameIndexSameAsPrevious(
+    const Surface* surface) const {
   auto it = previous_contained_surfaces_.find(surface->surface_id());
   if (it != previous_contained_surfaces_.end()) {
     uint64_t previous_index = it->second;
     if (previous_index == surface->GetActiveFrameIndex())
-      return gfx::Rect();
+      return true;
   }
+  return false;
+}
+
+gfx::Rect SurfaceAggregator::DamageRectForSurface(
+    const Surface* surface,
+    const RenderPass& source,
+    const gfx::Rect& full_rect) const {
+  if (IsSurfaceFrameIndexSameAsPrevious(surface))
+    return gfx::Rect();
+
+  auto it = previous_contained_surfaces_.find(surface->surface_id());
   const SurfaceId& previous_surface_id = surface->previous_frame_surface_id();
 
   if (surface->surface_id() != previous_surface_id) {
@@ -299,6 +308,7 @@
   }
 
   referenced_surfaces_.insert(surface_id);
+  bool has_surface_damage = !IsSurfaceFrameIndexSameAsPrevious(surface);
   // TODO(vmpstr): provider check is a hack for unittests that don't set up a
   // resource provider.
   std::unordered_map<ResourceId, ResourceId> empty_map;
@@ -347,7 +357,7 @@
     CopyQuadsToPass(source.quad_list, source.shared_quad_state_list,
                     surface->GetActiveFrame().device_scale_factor(),
                     child_to_parent_map, gfx::Transform(), ClipData(),
-                    copy_pass.get(), surface_id);
+                    copy_pass.get(), surface_id, has_surface_damage);
 
     // If the render pass has copy requests, or should be cached, or has
     // moving-pixel filters, or in a moving-pixel surface, we should damage the
@@ -401,7 +411,7 @@
     CopyQuadsToPass(quads, last_pass.shared_quad_state_list,
                     surface->GetActiveFrame().device_scale_factor(),
                     child_to_parent_map, surface_transform, quads_clip,
-                    dest_pass, surface_id);
+                    dest_pass, surface_id, has_surface_damage);
   } else {
     auto* shared_quad_state = CopyAndScaleSharedQuadState(
         source_sqs, scaled_quad_to_target_transform, target_transform,
@@ -411,7 +421,7 @@
         gfx::ScaleToEnclosingRect(source_sqs->visible_quad_layer_rect,
                                   layer_to_content_scale_x,
                                   layer_to_content_scale_y),
-        clip_rect, dest_pass);
+        clip_rect, dest_pass, has_surface_damage);
 
     gfx::Rect scaled_rect(gfx::ScaleToEnclosingRect(
         source_rect, layer_to_content_scale_x, layer_to_content_scale_y));
@@ -442,8 +452,9 @@
   // surface specified so create a SolidColorDrawQuad with the default
   // background color.
   SkColor background_color = surface_quad->default_background_color;
-  auto* shared_quad_state = CopySharedQuadState(
-      surface_quad->shared_quad_state, target_transform, clip_rect, dest_pass);
+  auto* shared_quad_state =
+      CopySharedQuadState(surface_quad->shared_quad_state, target_transform,
+                          clip_rect, dest_pass, /*has_surface_damage*/ true);
   auto* solid_color_quad =
       dest_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
   solid_color_quad->SetNew(shared_quad_state, surface_quad->rect,
@@ -474,7 +485,8 @@
     SharedQuadState* shared_quad_state = CopyAndScaleSharedQuadState(
         primary_shared_quad_state,
         primary_shared_quad_state->quad_to_target_transform, target_transform,
-        right_gutter_rect, right_gutter_rect, clip_rect, dest_pass);
+        right_gutter_rect, right_gutter_rect, clip_rect, dest_pass,
+        /*has_surface_damage*/ true);
 
     auto* right_gutter =
         dest_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -490,7 +502,8 @@
     SharedQuadState* shared_quad_state = CopyAndScaleSharedQuadState(
         primary_shared_quad_state,
         primary_shared_quad_state->quad_to_target_transform, target_transform,
-        bottom_gutter_rect, bottom_gutter_rect, clip_rect, dest_pass);
+        bottom_gutter_rect, bottom_gutter_rect, clip_rect, dest_pass,
+        /*has_surface_damage*/ true);
 
     auto* bottom_gutter =
         dest_pass->CreateAndAppendDrawQuad<SolidColorDrawQuad>();
@@ -543,11 +556,12 @@
     const SharedQuadState* source_sqs,
     const gfx::Transform& target_transform,
     const ClipData& clip_rect,
-    RenderPass* dest_render_pass) {
+    RenderPass* dest_render_pass,
+    bool has_surface_damage) {
   return CopyAndScaleSharedQuadState(
       source_sqs, source_sqs->quad_to_target_transform, target_transform,
       source_sqs->quad_layer_rect, source_sqs->visible_quad_layer_rect,
-      clip_rect, dest_render_pass);
+      clip_rect, dest_render_pass, has_surface_damage);
 }
 
 SharedQuadState* SurfaceAggregator::CopyAndScaleSharedQuadState(
@@ -557,7 +571,8 @@
     const gfx::Rect& quad_layer_rect,
     const gfx::Rect& visible_quad_layer_rect,
     const ClipData& clip_rect,
-    RenderPass* dest_render_pass) {
+    RenderPass* dest_render_pass,
+    bool has_surface_damage) {
   auto* shared_quad_state = dest_render_pass->CreateAndAppendSharedQuadState();
   ClipData new_clip_rect = CalculateClipRect(
       clip_rect, ClipData(source_sqs->is_clipped, source_sqs->clip_rect),
@@ -577,6 +592,7 @@
       new_clip_rect.rect, new_clip_rect.is_clipped,
       source_sqs->are_contents_opaque, source_sqs->opacity,
       source_sqs->blend_mode, source_sqs->sorting_context_id);
+  shared_quad_state->has_surface_damage = has_surface_damage;
 
   return shared_quad_state;
 }
@@ -589,7 +605,8 @@
     const gfx::Transform& target_transform,
     const ClipData& clip_rect,
     RenderPass* dest_pass,
-    const SurfaceId& surface_id) {
+    const SurfaceId& surface_id,
+    bool has_surface_damage) {
   const SharedQuadState* last_copied_source_shared_quad_state = nullptr;
   // If the current frame has copy requests or cached render passes, then
   // aggregate the entire thing, as otherwise parts of the copy requests may be
@@ -632,8 +649,9 @@
                         &damage_rect_in_quad_space_valid);
     } else {
       if (quad->shared_quad_state != last_copied_source_shared_quad_state) {
-        const SharedQuadState* dest_shared_quad_state = CopySharedQuadState(
-            quad->shared_quad_state, target_transform, clip_rect, dest_pass);
+        const SharedQuadState* dest_shared_quad_state =
+            CopySharedQuadState(quad->shared_quad_state, target_transform,
+                                clip_rect, dest_pass, has_surface_damage);
         last_copied_source_shared_quad_state = quad->shared_quad_state;
         if (ignore_undamaged) {
           damage_rect_in_quad_space_valid = CalculateQuadSpaceDamageRect(
@@ -707,6 +725,9 @@
   if (!valid_surfaces_.count(surface->surface_id()))
     return;
 
+  // No changes in the target surface if it's the same as the previous frame
+  // This information will be used later in overlay processing.
+  bool has_surface_damage = !IsSurfaceFrameIndexSameAsPrevious(surface);
   // TODO(vmpstr): provider check is a hack for unittests that don't set up a
   // resource provider.
   std::unordered_map<ResourceId, ResourceId> empty_map;
@@ -736,7 +757,7 @@
     CopyQuadsToPass(source.quad_list, source.shared_quad_state_list,
                     frame.device_scale_factor(), child_to_parent_map,
                     gfx::Transform(), ClipData(), copy_pass.get(),
-                    surface->surface_id());
+                    surface->surface_id(), has_surface_damage);
 
     // If the render pass has copy requests, or should be cached, or has
     // moving-pixel filters, or in a moving-pixel surface, we should damage the
diff --git a/components/viz/service/display/surface_aggregator.h b/components/viz/service/display/surface_aggregator.h
index 335d6d28..352a124c 100644
--- a/components/viz/service/display/surface_aggregator.h
+++ b/components/viz/service/display/surface_aggregator.h
@@ -130,7 +130,8 @@
   SharedQuadState* CopySharedQuadState(const SharedQuadState* source_sqs,
                                        const gfx::Transform& target_transform,
                                        const ClipData& clip_rect,
-                                       RenderPass* dest_render_pass);
+                                       RenderPass* dest_render_pass,
+                                       bool has_surface_damage);
 
   SharedQuadState* CopyAndScaleSharedQuadState(
       const SharedQuadState* source_sqs,
@@ -139,7 +140,8 @@
       const gfx::Rect& quad_layer_rect,
       const gfx::Rect& visible_quad_layer_rect,
       const ClipData& clip_rect,
-      RenderPass* dest_render_pass);
+      RenderPass* dest_render_pass,
+      bool has_surface_damage);
 
   void CopyQuadsToPass(
       const QuadList& source_quad_list,
@@ -149,7 +151,8 @@
       const gfx::Transform& target_transform,
       const ClipData& clip_rect,
       RenderPass* dest_pass,
-      const SurfaceId& surface_id);
+      const SurfaceId& surface_id,
+      bool has_surface_damage);
   gfx::Rect PrewalkTree(Surface* surface,
                         bool in_moved_pixel_surface,
                         int parent_pass,
@@ -168,6 +171,7 @@
   void PropagateCopyRequestPasses();
 
   int ChildIdForSurface(Surface* surface);
+  bool IsSurfaceFrameIndexSameAsPrevious(const Surface* surface) const;
   gfx::Rect DamageRectForSurface(const Surface* surface,
                                  const RenderPass& source,
                                  const gfx::Rect& full_rect) const;
diff --git a/components/viz/service/display/surface_aggregator_unittest.cc b/components/viz/service/display/surface_aggregator_unittest.cc
index 9c1d21e..cfaae33 100644
--- a/components/viz/service/display/surface_aggregator_unittest.cc
+++ b/components/viz/service/display/surface_aggregator_unittest.cc
@@ -9,6 +9,7 @@
 
 #include <set>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/macros.h"
@@ -4675,5 +4676,95 @@
   }
 }
 
+TEST_F(SurfaceAggregatorValidSurfaceTest, NoDamageIfNoFrameChange) {
+  // child surface
+  std::vector<Quad> child_surface_quads = {
+      Quad::SolidColorQuad(SK_ColorRED, gfx::Rect(SurfaceSize()))};
+  std::vector<Pass> child_surface_passes = {
+      Pass(child_surface_quads, 1, SurfaceSize())};
+
+  CompositorFrame child_surface_frame = MakeEmptyCompositorFrame();
+  AddPasses(&child_surface_frame.render_pass_list, child_surface_passes,
+            &child_surface_frame.metadata.referenced_surfaces);
+
+  allocator_.GenerateId();
+  LocalSurfaceId child_local_surface_id =
+      allocator_.GetCurrentLocalSurfaceIdAllocation().local_surface_id();
+  SurfaceId child_surface_id(child_support_->frame_sink_id(),
+                             child_local_surface_id);
+  child_support_->SubmitCompositorFrame(child_local_surface_id,
+                                        std::move(child_surface_frame));
+
+  // root surface
+  std::vector<Quad> root_surface_quads = {
+      Quad::SurfaceQuad(SurfaceRange(base::nullopt, child_surface_id),
+                        SK_ColorWHITE, gfx::Rect(SurfaceSize()),
+                        /*stretch_content_to_fill_bounds=*/false,
+                        /*ignores_input_event=*/false),
+      Quad::SolidColorQuad(SK_ColorGREEN, gfx::Rect(SurfaceSize()))};
+
+  std::vector<Pass> root_passes = {Pass(root_surface_quads, 1, SurfaceSize())};
+
+  CompositorFrame root_frame = MakeEmptyCompositorFrame();
+  AddPasses(&root_frame.render_pass_list, root_passes,
+            &root_frame.metadata.referenced_surfaces);
+
+  SurfaceId root_surface_id(support_->frame_sink_id(), root_local_surface_id_);
+  support_->SubmitCompositorFrame(root_local_surface_id_,
+                                  std::move(root_frame));
+  // The first frame - both root and child surfaces cause damages
+  CompositorFrame aggregated_frame =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+
+  auto* root_render_pass = aggregated_frame.render_pass_list[0].get();
+  auto* root_surface_quad_sqs = root_render_pass->shared_quad_state_list.back();
+  auto* child_surface_quad_sqs =
+      root_render_pass->shared_quad_state_list.front();
+
+  ASSERT_EQ(1u, aggregated_frame.render_pass_list.size());
+  ASSERT_EQ(2u, root_render_pass->shared_quad_state_list.size());
+  EXPECT_EQ(true, root_surface_quad_sqs->has_surface_damage);
+  EXPECT_EQ(true, child_surface_quad_sqs->has_surface_damage);
+
+  // The second frame - only root surface changed
+  CompositorFrame root_frame2 = MakeEmptyCompositorFrame();
+  AddPasses(&root_frame2.render_pass_list, root_passes,
+            &root_frame2.metadata.referenced_surfaces);
+
+  support_->SubmitCompositorFrame(root_local_surface_id_,
+                                  std::move(root_frame2));
+
+  CompositorFrame aggregated_frame2 =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+
+  auto* root_render_pass2 = aggregated_frame2.render_pass_list[0].get();
+  auto* root_surface_quad_sqs2 =
+      root_render_pass2->shared_quad_state_list.back();
+  auto* child_surface_quad_sqs2 =
+      root_render_pass2->shared_quad_state_list.front();
+
+  EXPECT_EQ(true, root_surface_quad_sqs2->has_surface_damage);
+  EXPECT_EQ(false, child_surface_quad_sqs2->has_surface_damage);
+
+  // The third frame - only child surface changed
+  CompositorFrame child_surface_frame3 = MakeEmptyCompositorFrame();
+  AddPasses(&child_surface_frame3.render_pass_list, child_surface_passes,
+            &child_surface_frame3.metadata.referenced_surfaces);
+  child_support_->SubmitCompositorFrame(child_local_surface_id,
+                                        std::move(child_surface_frame3));
+
+  CompositorFrame aggregated_frame3 =
+      aggregator_.Aggregate(root_surface_id, GetNextDisplayTimeAndIncrement());
+
+  auto* root_render_pass3 = aggregated_frame3.render_pass_list[0].get();
+  auto* root_surface_quad_sqs3 =
+      root_render_pass3->shared_quad_state_list.back();
+  auto* child_surface_quad_sqs3 =
+      root_render_pass3->shared_quad_state_list.front();
+
+  EXPECT_EQ(false, root_surface_quad_sqs3->has_surface_damage);
+  EXPECT_EQ(true, child_surface_quad_sqs3->has_surface_damage);
+}
+
 }  // namespace
 }  // namespace viz
diff --git a/components/viz/service/display_embedder/direct_context_provider.cc b/components/viz/service/display_embedder/direct_context_provider.cc
new file mode 100644
index 0000000..aaf1862
--- /dev/null
+++ b/components/viz/service/display_embedder/direct_context_provider.cc
@@ -0,0 +1,290 @@
+// 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/viz/service/display_embedder/direct_context_provider.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/trace_event/memory_dump_manager.h"
+#include "build/build_config.h"
+#include "components/viz/common/gpu/context_lost_observer.h"
+#include "components/viz/common/gpu/context_lost_reason.h"
+#include "gpu/command_buffer/client/gles2_cmd_helper.h"
+#include "gpu/command_buffer/client/gles2_implementation.h"
+#include "gpu/command_buffer/client/shared_memory_limits.h"
+#include "gpu/command_buffer/client/transfer_buffer.h"
+#include "gpu/command_buffer/common/context_creation_attribs.h"
+#include "gpu/command_buffer/service/command_buffer_direct.h"
+#include "gpu/command_buffer/service/gles2_cmd_decoder.h"
+#include "gpu/command_buffer/service/memory_tracking.h"
+#include "gpu/command_buffer/service/transfer_buffer_manager.h"
+#include "gpu/config/gpu_feature_info.h"
+#include "ui/gl/gl_context.h"
+#include "ui/gl/gl_surface.h"
+
+namespace viz {
+
+DirectContextProvider::DirectContextProvider(
+    scoped_refptr<gl::GLContext> gl_context,
+    scoped_refptr<gl::GLSurface> gl_surface,
+    bool supports_alpha,
+    const gpu::GpuPreferences& gpu_preferences,
+    gpu::gles2::FeatureInfo* feature_info)
+    : translator_cache_(gpu_preferences) {
+  DCHECK(gl_context->IsCurrent(gl_surface.get()));
+
+  auto limits = gpu::SharedMemoryLimits::ForMailboxContext();
+  auto group = base::MakeRefCounted<gpu::gles2::ContextGroup>(
+      gpu_preferences, true, &mailbox_manager_, /*memory_tracker=*/nullptr,
+      &translator_cache_, &completeness_cache_, feature_info, true,
+      &image_manager_, /*image_factory=*/nullptr,
+      /*progress_reporter=*/nullptr, gpu_feature_info_, &discardable_manager_,
+      &passthrough_discardable_manager_, &shared_image_manager_);
+
+  transfer_buffer_manager_ =
+      std::make_unique<gpu::TransferBufferManager>(nullptr);
+  auto command_buffer = std::make_unique<gpu::CommandBufferDirect>(
+      transfer_buffer_manager_.get());
+
+  std::unique_ptr<gpu::gles2::GLES2Decoder> decoder(
+      gpu::gles2::GLES2Decoder::Create(command_buffer.get(),
+                                       command_buffer->service(), &outputter_,
+                                       group.get()));
+
+  command_buffer->set_handler(decoder.get());
+
+  gpu::ContextCreationAttribs attribs;
+  attribs.alpha_size = supports_alpha ? 8 : 0;
+  attribs.buffer_preserved = false;
+  attribs.bind_generates_resource = true;
+  attribs.fail_if_major_perf_caveat = false;
+  attribs.lose_context_when_out_of_memory = true;
+  attribs.context_type = gpu::CONTEXT_TYPE_OPENGLES2;
+
+  context_result_ =
+      decoder->Initialize(gl_surface, gl_context, gl_surface->IsOffscreen(),
+                          gpu::gles2::DisallowedFeatures(), attribs);
+  if (context_result_ != gpu::ContextResult::kSuccess)
+    return;
+
+  auto gles2_cmd_helper =
+      std::make_unique<gpu::gles2::GLES2CmdHelper>(command_buffer.get());
+  context_result_ = gles2_cmd_helper->Initialize(limits.command_buffer_size);
+  if (context_result_ != gpu::ContextResult::kSuccess) {
+    decoder->Destroy(true);
+    return;
+  }
+  // Client side Capabilities queries return reference, service side return
+  // value. Here two sides are joined together.
+  capabilities_ = decoder->GetCapabilities();
+
+  auto transfer_buffer =
+      std::make_unique<gpu::TransferBuffer>(gles2_cmd_helper.get());
+
+  gles2_cmd_helper_ = std::move(gles2_cmd_helper);
+  transfer_buffer_ = std::move(transfer_buffer);
+  command_buffer_ = std::move(command_buffer);
+  decoder_ = std::move(decoder);
+  gl_context_ = std::move(gl_context);
+
+  gles2_implementation_ = std::make_unique<gpu::gles2::GLES2Implementation>(
+      gles2_cmd_helper_.get(), nullptr, transfer_buffer_.get(),
+      attribs.bind_generates_resource, attribs.lose_context_when_out_of_memory,
+      /*kSupportClientSideArrays=*/false, this);
+
+  context_result_ = gles2_implementation_->Initialize(limits);
+  if (context_result_ != gpu::ContextResult::kSuccess) {
+    Destroy();
+    return;
+  }
+
+  base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
+      this, "viz::DirectContextProvider", base::ThreadTaskRunnerHandle::Get());
+}
+
+DirectContextProvider::~DirectContextProvider() {
+  if (decoder_)
+    Destroy();
+}
+
+void DirectContextProvider::Destroy() {
+  DCHECK(decoder_);
+  // The client gl interface might still be set to current global
+  // interface. This will be cleaned up in ApplyContextReleased
+  // with AutoCurrentContextRestore.
+  gles2_implementation_.reset();
+  gl_context_.reset();
+  transfer_buffer_.reset();
+  gles2_cmd_helper_.reset();
+  command_buffer_.reset();
+
+  bool have_context = !decoder_->WasContextLost();
+  decoder_->Destroy(have_context);
+  decoder_.reset();
+}
+
+void DirectContextProvider::SetGLRendererCopierRequiredState() {
+  gles2_implementation_->BindFramebuffer(GL_FRAMEBUFFER, 0);
+  gles2_implementation_->Disable(GL_SCISSOR_TEST);
+  gles2_implementation_->Disable(GL_STENCIL_TEST);
+  gles2_implementation_->Disable(GL_BLEND);
+}
+
+void DirectContextProvider::AddRef() const {
+  base::RefCountedThreadSafe<DirectContextProvider>::AddRef();
+}
+
+void DirectContextProvider::Release() const {
+  base::RefCountedThreadSafe<DirectContextProvider>::Release();
+}
+
+gpu::ContextResult DirectContextProvider::BindToCurrentThread() {
+  return context_result_;
+}
+
+gpu::gles2::GLES2Interface* DirectContextProvider::ContextGL() {
+  return gles2_implementation_.get();
+}
+
+gpu::ContextSupport* DirectContextProvider::ContextSupport() {
+  return gles2_implementation_.get();
+}
+
+class GrContext* DirectContextProvider::GrContext() {
+  NOTREACHED();
+  return nullptr;
+}
+
+gpu::SharedImageInterface* DirectContextProvider::SharedImageInterface() {
+  NOTREACHED();
+  return nullptr;
+}
+
+ContextCacheController* DirectContextProvider::CacheController() {
+  NOTREACHED();
+  return nullptr;
+}
+
+base::Lock* DirectContextProvider::GetLock() {
+  NOTREACHED();
+  return nullptr;
+}
+
+const gpu::Capabilities& DirectContextProvider::ContextCapabilities() const {
+  return capabilities_;
+}
+
+const gpu::GpuFeatureInfo& DirectContextProvider::GetGpuFeatureInfo() const {
+  return gpu_feature_info_;
+}
+
+void DirectContextProvider::AddObserver(ContextLostObserver* obs) {
+  observers_.AddObserver(obs);
+}
+
+void DirectContextProvider::RemoveObserver(ContextLostObserver* obs) {
+  observers_.RemoveObserver(obs);
+}
+
+void DirectContextProvider::OnContextLost() {
+  // TODO(https://crbug.com/927460): Instrument this with a context loss UMA
+  // stat shared with SkiaRenderer.
+  for (auto& observer : observers_)
+    observer.OnContextLost();
+}
+
+bool DirectContextProvider::OnMemoryDump(
+    const base::trace_event::MemoryDumpArgs& args,
+    base::trace_event::ProcessMemoryDump* pmd) {
+  DCHECK_EQ(context_result_, gpu::ContextResult::kSuccess);
+
+  gles2_implementation_->OnMemoryDump(args, pmd);
+  gles2_cmd_helper_->OnMemoryDump(args, pmd);
+
+  return true;
+}
+
+void DirectContextProvider::SetGpuControlClient(gpu::GpuControlClient*) {
+  // The client is not currently called, so don't store it.
+}
+
+const gpu::Capabilities& DirectContextProvider::GetCapabilities() const {
+  return capabilities_;
+}
+
+int32_t DirectContextProvider::CreateImage(ClientBuffer buffer,
+                                           size_t width,
+                                           size_t height) {
+  NOTREACHED();
+  return -1;
+}
+
+void DirectContextProvider::DestroyImage(int32_t id) {
+  NOTREACHED();
+}
+
+void DirectContextProvider::SignalQuery(uint32_t query,
+                                        base::OnceClosure callback) {
+  decoder_->SetQueryCallback(query, std::move(callback));
+}
+
+void DirectContextProvider::CreateGpuFence(uint32_t gpu_fence_id,
+                                           ClientGpuFence source) {
+  NOTREACHED();
+}
+
+void DirectContextProvider::GetGpuFence(
+    uint32_t gpu_fence_id,
+    base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)> callback) {
+  NOTREACHED();
+}
+
+void DirectContextProvider::SetLock(base::Lock*) {
+  NOTREACHED();
+}
+
+void DirectContextProvider::EnsureWorkVisible() {
+  NOTREACHED();
+}
+
+gpu::CommandBufferNamespace DirectContextProvider::GetNamespaceID() const {
+  return gpu::CommandBufferNamespace::INVALID;
+}
+
+gpu::CommandBufferId DirectContextProvider::GetCommandBufferID() const {
+  return gpu::CommandBufferId();
+}
+
+void DirectContextProvider::FlushPendingWork() {
+  NOTREACHED();
+}
+
+uint64_t DirectContextProvider::GenerateFenceSyncRelease() {
+  NOTREACHED();
+  return 0;
+}
+
+bool DirectContextProvider::IsFenceSyncReleased(uint64_t release) {
+  NOTREACHED();
+  return false;
+}
+
+void DirectContextProvider::SignalSyncToken(const gpu::SyncToken& sync_token,
+                                            base::OnceClosure callback) {
+  NOTREACHED();
+}
+
+void DirectContextProvider::WaitSyncToken(const gpu::SyncToken& sync_token) {
+  NOTREACHED();
+}
+
+bool DirectContextProvider::CanWaitUnverifiedSyncToken(
+    const gpu::SyncToken& sync_token) {
+  return false;
+}
+
+}  // namespace viz
diff --git a/components/viz/service/display_embedder/direct_context_provider.h b/components/viz/service/display_embedder/direct_context_provider.h
new file mode 100644
index 0000000..f460ae9
--- /dev/null
+++ b/components/viz/service/display_embedder/direct_context_provider.h
@@ -0,0 +1,141 @@
+// 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_VIZ_SERVICE_DISPLAY_EMBEDDER_DIRECT_CONTEXT_PROVIDER_H_
+#define COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DIRECT_CONTEXT_PROVIDER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/observer_list.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "components/viz/common/gpu/context_provider.h"
+#include "components/viz/service/viz_service_export.h"
+#include "gpu/command_buffer/client/gpu_control.h"
+#include "gpu/command_buffer/common/context_result.h"
+#include "gpu/command_buffer/service/gpu_tracer.h"
+#include "gpu/command_buffer/service/image_manager.h"
+#include "gpu/command_buffer/service/mailbox_manager_impl.h"
+#include "gpu/command_buffer/service/passthrough_discardable_manager.h"
+#include "gpu/command_buffer/service/service_discardable_manager.h"
+#include "gpu/command_buffer/service/shared_image_manager.h"
+class GrContext;
+
+namespace gpu {
+class CommandBufferDirect;
+class DecoderContext;
+class TransferBuffer;
+struct GpuPreferences;
+
+namespace gles2 {
+class GLES2CmdHelper;
+class GLES2Implementation;
+class GLES2Interface;
+}  // namespace gles2
+}  // namespace gpu
+
+namespace viz {
+class ContextLostObserver;
+
+// DirectContextProvider provides a GLES2Interface by running cross process code
+// (e.g. GLES2Implementation and GLES2Decoder) within a single thread. It is
+// suitable for easily porting code relying on GLES2Interface, but is less
+// efficient than calling native GL because it serializes/deserializes command
+// streams, validates command streams, and has unnecessary copies through shared
+// memory (e.g. glReadPixels frame buffer). Parts of GLES2Interface are
+// NOTIMPLEMENTED().
+class VIZ_SERVICE_EXPORT DirectContextProvider
+    : public base::RefCountedThreadSafe<DirectContextProvider>,
+      public ContextProvider,
+      public gpu::GpuControl,
+      public base::trace_event::MemoryDumpProvider {
+ public:
+  DirectContextProvider(scoped_refptr<gl::GLContext> gl_context,
+                        scoped_refptr<gl::GLSurface> gl_surface,
+                        bool supports_alpha,
+                        const gpu::GpuPreferences& gpu_preferences,
+                        gpu::gles2::FeatureInfo* feature_info);
+  gpu::DecoderContext* decoder() { return decoder_.get(); }
+  void SetGLRendererCopierRequiredState();
+
+  // ContextProvider implementation.
+  void AddRef() const override;
+  void Release() const override;
+  gpu::ContextResult BindToCurrentThread() override;
+  gpu::gles2::GLES2Interface* ContextGL() override;
+  gpu::ContextSupport* ContextSupport() override;
+  class GrContext* GrContext() override;
+  gpu::SharedImageInterface* SharedImageInterface() override;
+  ContextCacheController* CacheController() override;
+  base::Lock* GetLock() override;
+  const gpu::Capabilities& ContextCapabilities() const override;
+  const gpu::GpuFeatureInfo& GetGpuFeatureInfo() const override;
+  void AddObserver(ContextLostObserver* obs) override;
+  void RemoveObserver(ContextLostObserver* obs) override;
+
+  // GpuControl implementation.
+  void SetGpuControlClient(gpu::GpuControlClient*) override;
+  const gpu::Capabilities& GetCapabilities() const override;
+  int32_t CreateImage(ClientBuffer buffer,
+                      size_t width,
+                      size_t height) override;
+  void DestroyImage(int32_t id) override;
+  void SignalQuery(uint32_t query, base::OnceClosure callback) override;
+  void CreateGpuFence(uint32_t gpu_fence_id, ClientGpuFence source) override;
+  void GetGpuFence(uint32_t gpu_fence_id,
+                   base::OnceCallback<void(std::unique_ptr<gfx::GpuFence>)>
+                       callback) override;
+  void SetLock(base::Lock*) override;
+  void EnsureWorkVisible() override;
+  gpu::CommandBufferNamespace GetNamespaceID() const override;
+  gpu::CommandBufferId GetCommandBufferID() const override;
+  void FlushPendingWork() override;
+  uint64_t GenerateFenceSyncRelease() override;
+  bool IsFenceSyncReleased(uint64_t release) override;
+  void SignalSyncToken(const gpu::SyncToken& sync_token,
+                       base::OnceClosure callback) override;
+  void WaitSyncToken(const gpu::SyncToken& sync_token) override;
+  bool CanWaitUnverifiedSyncToken(const gpu::SyncToken& sync_token) override;
+
+ private:
+  friend class base::RefCountedThreadSafe<DirectContextProvider>;
+  ~DirectContextProvider() override;
+
+  void Destroy();
+  void OnContextLost();
+
+  // base::trace_event::MemoryDumpProvider implementation.
+  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
+                    base::trace_event::ProcessMemoryDump* pmd) override;
+
+  gpu::gles2::MailboxManagerImpl mailbox_manager_;
+  gpu::gles2::TraceOutputter outputter_;
+  gpu::gles2::ImageManager image_manager_;
+  gpu::ServiceDiscardableManager discardable_manager_;
+  gpu::PassthroughDiscardableManager passthrough_discardable_manager_;
+  gpu::SharedImageManager shared_image_manager_;
+  gpu::gles2::ShaderTranslatorCache translator_cache_;
+  gpu::gles2::FramebufferCompletenessCache completeness_cache_;
+  gpu::GpuFeatureInfo gpu_feature_info_;
+  gpu::Capabilities capabilities_;
+  gpu::ContextResult context_result_ = gpu::ContextResult::kSuccess;
+
+  // Only non-null if BindToCurrentThread() == ContextResult::kSuccess.
+  std::unique_ptr<gpu::TransferBufferManager> transfer_buffer_manager_;
+  std::unique_ptr<gpu::CommandBufferDirect> command_buffer_;
+  std::unique_ptr<gpu::gles2::GLES2CmdHelper> gles2_cmd_helper_;
+  std::unique_ptr<gpu::gles2::GLES2Decoder> decoder_;
+  std::unique_ptr<gpu::TransferBuffer> transfer_buffer_;
+  scoped_refptr<gl::GLContext> gl_context_;
+  std::unique_ptr<gpu::gles2::GLES2Implementation> gles2_implementation_;
+
+  base::ObserverList<ContextLostObserver>::Unchecked observers_;
+};
+
+}  // namespace viz
+
+#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_EMBEDDER_DIRECT_CONTEXT_PROVIDER_H_
diff --git a/components/viz/service/display_embedder/gpu_display_provider.cc b/components/viz/service/display_embedder/gpu_display_provider.cc
index 32f5330..53053fc 100644
--- a/components/viz/service/display_embedder/gpu_display_provider.cc
+++ b/components/viz/service/display_embedder/gpu_display_provider.cc
@@ -33,7 +33,6 @@
 #if defined(OS_WIN)
 #include "components/viz/service/display_embedder/gl_output_surface_win.h"
 #include "components/viz/service/display_embedder/software_output_device_win.h"
-#include "ui/gfx/win/rendering_window_manager.h"
 #endif
 
 #if defined(OS_ANDROID)
@@ -246,25 +245,8 @@
     return std::make_unique<SoftwareOutputDevice>();
 
 #if defined(OS_WIN)
-  HWND child_hwnd;
-  auto device = CreateSoftwareOutputDeviceWinGpu(
-      surface_handle, &output_device_backing_, display_client, &child_hwnd);
-
-  // If |child_hwnd| isn't null then a new child HWND was created.
-  if (child_hwnd) {
-    if (gpu_channel_manager_delegate_) {
-      // Send an IPC to browser process for SetParent().
-      gpu_channel_manager_delegate_->SendCreatedChildWindow(surface_handle,
-                                                            child_hwnd);
-    } else {
-      // We are already in the browser process.
-      gfx::RenderingWindowManager::GetInstance()->RegisterChild(
-          surface_handle, child_hwnd,
-          /*expected_child_process_id=*/::GetCurrentProcessId());
-    }
-  }
-
-  return device;
+  return CreateSoftwareOutputDeviceWinGpu(
+      surface_handle, &output_device_backing_, display_client);
 #elif defined(OS_MACOSX)
   return std::make_unique<SoftwareOutputDeviceMac>(task_runner_);
 #elif defined(OS_ANDROID)
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index d4a58e89..aeadd129 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -367,6 +367,12 @@
                                     const gfx::ColorSpace& color_space,
                                     bool has_alpha,
                                     bool use_stencil) {
+  reshape_surface_size_ = size;
+  reshape_device_scale_factor_ = device_scale_factor;
+  reshape_color_space_ = color_space;
+  reshape_has_alpha_ = has_alpha;
+  reshape_use_stencil_ = use_stencil;
+
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
   if (initialize_waitable_event_) {
     initialize_waitable_event_->Wait();
@@ -374,15 +380,23 @@
   }
 
   SkSurfaceCharacterization* characterization = nullptr;
-  if (characterization_.isValid()) {
-    // TODO(weiliang): suppoot color space. https://crbug.com/795132
-    characterization_ =
-        characterization_.createResized(size.width(), size.height());
-  } else {
+  // If the render target is changed between GL fbo zero and non-zero, we cannot
+  // recreate SkSurface characterization from the existing characterization. So
+  // we have to request a new SkSurface characterization from
+  // SkiaOutputSurfaceImplOnGpu.
+  backing_framebuffer_object_ =
+      gl_surface_ ? gl_surface_->GetBackingFramebufferObject() : 0;
+  if (!characterization_.isValid() ||
+      (!is_using_vulkan_ &&
+       characterization_.usesGLFBO0() != (backing_framebuffer_object_ == 0))) {
     characterization = &characterization_;
     initialize_waitable_event_ = std::make_unique<base::WaitableEvent>(
         base::WaitableEvent::ResetPolicy::MANUAL,
         base::WaitableEvent::InitialState::NOT_SIGNALED);
+  } else {
+    // TODO(weiliang): support color space. https://crbug.com/795132
+    characterization_ =
+        characterization_.createResized(size.width(), size.height());
   }
 
   // impl_on_gpu_ is released on the GPU thread by a posted task from
@@ -464,6 +478,15 @@
   }
 
   DCHECK(characterization_.isValid());
+  // The fbo is changed, in that case we need notify SkiaOutputSurfaceImplOnGpu,
+  // so it can recreate SkSurface from the new fbo, and return a updated
+  // SkSurfaceCharacterization if necessary.
+  if (gl_surface_ && backing_framebuffer_object_ !=
+                         gl_surface_->GetBackingFramebufferObject()) {
+    Reshape(reshape_surface_size_, reshape_device_scale_factor_,
+            reshape_color_space_, reshape_has_alpha_, reshape_use_stencil_);
+  }
+
   recorder_.emplace(characterization_);
   if (!show_overdraw_feedback_)
     return recorder_->getCanvas();
@@ -680,10 +703,9 @@
 
   if (task_executor_) {
     impl_on_gpu_ = std::make_unique<SkiaOutputSurfaceImplOnGpu>(
-        task_executor_.get(), std::move(gl_surface_),
-        std::move(shared_context_state_), sequence_->GetSequenceId(),
-        did_swap_buffer_complete_callback, buffer_presented_callback,
-        context_lost_callback);
+        task_executor_.get(), gl_surface_, std::move(shared_context_state_),
+        sequence_->GetSequenceId(), did_swap_buffer_complete_callback,
+        buffer_presented_callback, context_lost_callback);
   } else {
     impl_on_gpu_ = std::make_unique<SkiaOutputSurfaceImplOnGpu>(
         gpu_service_, surface_handle_, did_swap_buffer_complete_callback,
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.h b/components/viz/service/display_embedder/skia_output_surface_impl.h
index 4476db3a..cd71581 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.h
@@ -148,6 +148,13 @@
   SyntheticBeginFrameSource* const synthetic_begin_frame_source_;
   OutputSurfaceClient* client_ = nullptr;
 
+  unsigned int backing_framebuffer_object_ = 0;
+  gfx::Size reshape_surface_size_;
+  float reshape_device_scale_factor_ = 0.f;
+  gfx::ColorSpace reshape_color_space_;
+  bool reshape_has_alpha_ = false;
+  bool reshape_use_stencil_ = false;
+
   std::unique_ptr<base::WaitableEvent> initialize_waitable_event_;
   SkSurfaceCharacterization characterization_;
   base::Optional<SkDeferredDisplayListRecorder> recorder_;
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 4fdb435..716cf11 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
@@ -12,6 +12,7 @@
 #include "components/viz/common/frame_sinks/copy_output_request.h"
 #include "components/viz/common/skia_helper.h"
 #include "components/viz/service/display/output_surface_frame.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/swap_buffers_complete_params.h"
 #include "gpu/command_buffer/service/context_state.h"
@@ -137,6 +138,32 @@
 SkiaOutputSurfaceImplOnGpu::OffscreenSurface::operator=(
     OffscreenSurface&& offscreen_surface) = default;
 
+SkiaOutputSurfaceImplOnGpu::ScopedUseContextProvider::ScopedUseContextProvider(
+    SkiaOutputSurfaceImplOnGpu* impl_on_gpu)
+    : impl_on_gpu_(impl_on_gpu) {
+  if (!impl_on_gpu_->MakeCurrent()) {
+    valid_ = false;
+    return;
+  }
+
+  // GLRendererCopier uses context_provider_->ContextGL(), which caches GL state
+  // and removes state setting calls that it considers redundant. To get to a
+  // safe known GL state, we first call the client side to set the cached state,
+  // then we make driver GL state consistent with that.
+  impl_on_gpu_->context_provider_->SetGLRendererCopierRequiredState();
+  auto* api = impl_on_gpu_->api_;
+  api->glBindFramebufferEXTFn(GL_FRAMEBUFFER, 0);
+  api->glDisableFn(GL_SCISSOR_TEST);
+  api->glDisableFn(GL_STENCIL_TEST);
+  api->glDisableFn(GL_BLEND);
+}
+
+SkiaOutputSurfaceImplOnGpu::ScopedUseContextProvider::
+    ~ScopedUseContextProvider() {
+  if (valid_)
+    impl_on_gpu_->gr_context()->resetContext();
+}
+
 SkiaOutputSurfaceImplOnGpu::SkiaOutputSurfaceImplOnGpu(
     gpu::SurfaceHandle surface_handle,
     scoped_refptr<gpu::gles2::FeatureInfo> feature_info,
@@ -219,6 +246,10 @@
 
 SkiaOutputSurfaceImplOnGpu::~SkiaOutputSurfaceImplOnGpu() {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // ~DirectContextProvider wants either the context to be lost or made current.
+  MakeCurrent();
+
 #if BUILDFLAG(ENABLE_VULKAN)
   if (vulkan_surface_) {
     vulkan_surface_->Destroy();
@@ -322,9 +353,6 @@
     if (gr_shader_cache_) {
       cache_use.emplace(gr_shader_cache_, gpu::kInProcessCommandBufferClientId);
     }
-    if (backing_framebuffer_object_ !=
-        gl_surface_->GetBackingFramebufferObject())
-      CreateSkSurfaceForGL();
     sk_surface_->draw(ddl.get());
     gr_context()->flush();
   }
@@ -463,6 +491,25 @@
   auto* surface =
       id ? offscreen_surfaces_[id].surface.get() : sk_surface_.get();
 
+  if (!is_using_vulkan()) {
+    if (!context_provider_) {
+      context_provider_ = base::MakeRefCounted<DirectContextProvider>(
+          context_state_->context(), gl_surface_, supports_alpha_,
+          gpu_preferences_, feature_info_.get());
+      context_provider_->BindToCurrentThread();
+    }
+    ScopedUseContextProvider use_context_provider(this);
+
+    // TODO(crbug.com/914502): Do this on the GPU instead of CPU with GL.
+    // copier_->CopyFromTextureOrFramebuffer(
+    //     std::move(request), output_rect,
+    //     internal_format, gl_id, surface_size, flipped, color_space);
+
+    // GLRendererCopier may have kicked off a glQuery.
+    if (decoder()->HasMoreIdleWork() || decoder()->HasPendingQueries())
+      ScheduleDelayedWork();
+  }
+
   SkBitmap bitmap;
   SkImageInfo copy_rect_info = SkImageInfo::Make(
       copy_rect.width(), copy_rect.height(), SkColorType::kN32_SkColorType,
@@ -514,6 +561,36 @@
       result_format, result_rect, bitmap));
 }
 
+gpu::DecoderContext* SkiaOutputSurfaceImplOnGpu::decoder() {
+  return context_provider_->decoder();
+}
+
+void SkiaOutputSurfaceImplOnGpu::ScheduleDelayedWork() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  if (delayed_work_pending_)
+    return;
+  delayed_work_pending_ = true;
+  base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE,
+      base::BindOnce(&SkiaOutputSurfaceImplOnGpu::PerformDelayedWork,
+                     weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(2));
+}
+
+void SkiaOutputSurfaceImplOnGpu::PerformDelayedWork() {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  ScopedUseContextProvider use_context_provider(this);
+
+  delayed_work_pending_ = false;
+  if (MakeCurrent()) {
+    decoder()->PerformIdleWork();
+    decoder()->ProcessPendingQueries(false);
+    if (decoder()->HasMoreIdleWork() || decoder()->HasPendingQueries()) {
+      ScheduleDelayedWork();
+    }
+  }
+}
+
 sk_sp<SkPromiseImageTexture> SkiaOutputSurfaceImplOnGpu::FulfillPromiseTexture(
     const gpu::MailboxHolder& mailbox_holder,
     const gfx::Size& size,
@@ -751,8 +828,7 @@
       SkSurfaceProps(0, SkSurfaceProps::kLegacyFontHost_InitType);
 
   GrGLFramebufferInfo framebuffer_info;
-  backing_framebuffer_object_ = gl_surface_->GetBackingFramebufferObject();
-  framebuffer_info.fFBOID = backing_framebuffer_object_;
+  framebuffer_info.fFBOID = gl_surface_->GetBackingFramebufferObject();
   if (supports_alpha_) {
     framebuffer_info.fFormat =
         gl_version_info_->is_es ? GL_BGRA8_EXT : GL_RGBA8;
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
index e4c85ba..c6cd63a 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
+++ b/components/viz/service/display_embedder/skia_output_surface_impl_on_gpu.h
@@ -60,6 +60,7 @@
 
 namespace viz {
 
+class DirectContextProvider;
 class GpuServiceImpl;
 
 // The SkiaOutputSurface implementation running on the GPU thread. This class
@@ -156,6 +157,24 @@
 
   bool was_context_lost() { return context_state_->context_lost(); }
 
+  // Skia gr_context() and |context_provider_| share an underlying GLContext.
+  // Each of them caches some GL state. Interleaving usage could make cached
+  // state inconsistent with GL state. Using a ScopedUseContextProvider whenever
+  // |context_provider_| could be accessed (e.g. processing completed queries),
+  // will keep cached state consistent with driver GL state.
+  class ScopedUseContextProvider {
+   public:
+    explicit ScopedUseContextProvider(SkiaOutputSurfaceImplOnGpu* impl_on_gpu);
+    ~ScopedUseContextProvider();
+    bool valid() { return valid_; }
+
+   private:
+    SkiaOutputSurfaceImplOnGpu* const impl_on_gpu_;
+    bool valid_ = true;
+
+    DISALLOW_COPY_AND_ASSIGN(ScopedUseContextProvider);
+  };
+
  private:
 // gpu::ImageTransportSurfaceDelegate implementation:
 #if defined(OS_WIN)
@@ -194,6 +213,10 @@
   void ReleaseFenceSyncAndPushTextureUpdates(uint64_t sync_fence_release);
 
   GrContext* gr_context() { return context_state_->gr_context(); }
+  gpu::DecoderContext* decoder();
+
+  void ScheduleDelayedWork();
+  void PerformDelayedWork();
 
   SkColorType FramebufferColorType() {
     return supports_alpha_ ? kBGRA_8888_SkColorType : kRGB_888x_SkColorType;
@@ -222,7 +245,6 @@
   gfx::Size size_;
   gfx::ColorSpace color_space_;
   scoped_refptr<gl::GLSurface> gl_surface_;
-  unsigned int backing_framebuffer_object_ = 0;
   sk_sp<SkSurface> sk_surface_;
   scoped_refptr<gpu::SharedContextState> context_state_;
   const gl::GLVersionInfo* gl_version_info_ = nullptr;
@@ -257,6 +279,9 @@
 
   ui::LatencyTracker latency_tracker_;
 
+  scoped_refptr<DirectContextProvider> context_provider_;
+  bool delayed_work_pending_ = false;
+
   gl::GLApi* api_ = nullptr;
   bool supports_alpha_ = false;
 
diff --git a/components/viz/service/display_embedder/software_output_device_win.cc b/components/viz/service/display_embedder/software_output_device_win.cc
index 86797838..16b946d 100644
--- a/components/viz/service/display_embedder/software_output_device_win.cc
+++ b/components/viz/service/display_embedder/software_output_device_win.cc
@@ -348,10 +348,7 @@
 std::unique_ptr<SoftwareOutputDevice> CreateSoftwareOutputDeviceWinGpu(
     HWND hwnd,
     OutputDeviceBacking* backing,
-    mojom::DisplayClient* display_client,
-    HWND* out_child_hwnd) {
-  // TODO(kylechar): Remove |out_child_hwnd| parameter it's no longer used.
-
+    mojom::DisplayClient* display_client) {
   if (NeedsToUseLayerWindow(hwnd)) {
     DCHECK(display_client);
 
@@ -361,8 +358,6 @@
     display_client->CreateLayeredWindowUpdater(
         mojo::MakeRequest(&layered_window_updater));
 
-    *out_child_hwnd = nullptr;
-
     return std::make_unique<SoftwareOutputDeviceWinProxy>(
         hwnd, std::move(layered_window_updater));
   } else {
diff --git a/components/viz/service/display_embedder/software_output_device_win.h b/components/viz/service/display_embedder/software_output_device_win.h
index 60ce8c66..aa2dd64 100644
--- a/components/viz/service/display_embedder/software_output_device_win.h
+++ b/components/viz/service/display_embedder/software_output_device_win.h
@@ -27,8 +27,7 @@
 VIZ_SERVICE_EXPORT std::unique_ptr<SoftwareOutputDevice>
 CreateSoftwareOutputDeviceWinGpu(HWND hwnd,
                                  OutputDeviceBacking* backing,
-                                 mojom::DisplayClient* display_client,
-                                 HWND* out_child_hwnd);
+                                 mojom::DisplayClient* display_client);
 
 }  // namespace viz
 
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index b65ac6a..8599194 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -355,10 +355,7 @@
     "appcache/appcache_database.h",
     "appcache/appcache_disk_cache.cc",
     "appcache/appcache_disk_cache.h",
-    "appcache/appcache_dispatcher_host.cc",
-    "appcache/appcache_dispatcher_host.h",
     "appcache/appcache_entry.h",
-    "appcache/appcache_frontend.h",
     "appcache/appcache_frontend_proxy.cc",
     "appcache/appcache_frontend_proxy.h",
     "appcache/appcache_group.cc",
diff --git a/content/browser/accessibility/accessibility_tree_formatter_android.cc b/content/browser/accessibility/accessibility_tree_formatter_android.cc
index 7202c9e2..7d2e18a5 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_android.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_android.cc
@@ -172,7 +172,7 @@
   if (show_ids()) {
     int id_value;
     dict.GetInteger("id", &id_value);
-    WriteAttribute(true, base::IntToString16(id_value), &line);
+    WriteAttribute(true, base::NumberToString16(id_value), &line);
   }
 
   base::string16 class_value;
diff --git a/content/browser/accessibility/accessibility_tree_formatter_blink.cc b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
index 05f47a4..8dbd3a3 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_blink.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_blink.cc
@@ -306,7 +306,7 @@
   if (show_ids()) {
     int id_value;
     dict.GetInteger("id", &id_value);
-    WriteAttribute(true, base::IntToString16(id_value), &line);
+    WriteAttribute(true, base::NumberToString16(id_value), &line);
   }
 
   base::string16 role_value;
@@ -434,7 +434,7 @@
       } else {
         int int_value;
         value->GetInteger(i, &int_value);
-        attr_string += base::IntToString(int_value);
+        attr_string += base::NumberToString(int_value);
       }
     }
     WriteAttribute(false, attr_string, &line);
diff --git a/content/browser/accessibility/accessibility_tree_formatter_mac.mm b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
index 2f36e17b..a36d69e 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_mac.mm
+++ b/content/browser/accessibility/accessibility_tree_formatter_mac.mm
@@ -286,7 +286,7 @@
   if (show_ids()) {
     int id_value;
     dict.GetInteger("id", &id_value);
-    WriteAttribute(true, base::IntToString16(id_value), &line);
+    WriteAttribute(true, base::NumberToString16(id_value), &line);
   }
 
   NSArray* defaultAttributes =
diff --git a/content/browser/accessibility/accessibility_tree_formatter_stub.cc b/content/browser/accessibility/accessibility_tree_formatter_stub.cc
index 61a74486..cb7bb0a4d 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_stub.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_stub.cc
@@ -50,7 +50,7 @@
     base::DictionaryValue* filtered_dict_result) {
   int id_value;
   node.GetInteger("id", &id_value);
-  return base::IntToString16(id_value);
+  return base::NumberToString16(id_value);
 }
 
 const base::FilePath::StringType
diff --git a/content/browser/accessibility/accessibility_tree_formatter_win.cc b/content/browser/accessibility/accessibility_tree_formatter_win.cc
index 14c6537..fa5b650b 100644
--- a/content/browser/accessibility/accessibility_tree_formatter_win.cc
+++ b/content/browser/accessibility/accessibility_tree_formatter_win.cc
@@ -822,7 +822,7 @@
     if (hr == S_OK && temp_bstr && wcslen(temp_bstr)) {
       // Append offset:<number>.
       base::string16 offset_str =
-          base::ASCIIToUTF16("offset:") + base::IntToString16(start_offset);
+          base::ASCIIToUTF16("offset:") + base::NumberToString16(start_offset);
       text_attributes->AppendString(offset_str);
       // Append name:value pairs.
       std::vector<base::string16> name_val_pairs =
diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc
index 46e928e..ff6f11d 100644
--- a/content/browser/accessibility/accessibility_win_browsertest.cc
+++ b/content/browser/accessibility/accessibility_win_browsertest.cc
@@ -311,8 +311,8 @@
   AccessibilityNotificationWaiter waiter(
       shell()->web_contents(), ui::kAXModeComplete,
       ax::mojom::Event::kTextSelectionChanged);
-  std::wstring caret_offset = base::UTF16ToWide(
-      base::IntToString16(static_cast<int>(kContentsLength - 1)));
+  std::wstring caret_offset =
+      base::UTF16ToWide(base::NumberToString16(kContentsLength - 1));
   ExecuteScript(
       std::wstring(L"let textField = document.querySelector('input,textarea');"
                    L"textField.focus();"
@@ -369,8 +369,8 @@
   AccessibilityNotificationWaiter waiter(
       shell()->web_contents(), ui::kAXModeComplete,
       ax::mojom::Event::kTextSelectionChanged);
-  std::wstring caret_offset = base::UTF16ToWide(
-      base::IntToString16(static_cast<int>(kContentsLength - 1)));
+  std::wstring caret_offset =
+      base::UTF16ToWide(base::NumberToString16(kContentsLength - 1));
   ExecuteScript(
       std::wstring(L"var textField = document.querySelector('textarea');"
                    L"textField.focus();"
diff --git a/content/browser/accessibility/browser_accessibility_android.cc b/content/browser/accessibility/browser_accessibility_android.cc
index f405ebb..8c5949b 100644
--- a/content/browser/accessibility/browser_accessibility_android.cc
+++ b/content/browser/accessibility/browser_accessibility_android.cc
@@ -531,7 +531,7 @@
     int level = GetIntAttribute(ax::mojom::IntAttribute::kHierarchicalLevel);
     if (level >= 1 && level <= 6) {
       std::vector<base::string16> values;
-      values.push_back(base::IntToString16(level));
+      values.push_back(base::NumberToString16(level));
       return base::ReplaceStringPlaceholders(
           content_client->GetLocalizedString(IDS_AX_ROLE_HEADING_WITH_LEVEL),
           values, nullptr);
diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm
index d1e7164..b7887520 100644
--- a/content/browser/accessibility/browser_accessibility_cocoa.mm
+++ b/content/browser/accessibility/browser_accessibility_cocoa.mm
@@ -948,7 +948,8 @@
   }
 
   // If it's focusable but didn't have any other name or value, compute a name
-  // from its descendants.
+  // from its descendants. Note that this is a workaround because VoiceOver
+  // does not always present focus changes if the new focus lacks a name.
   base::string16 value = owner_->GetValue();
   if (owner_->HasState(ax::mojom::State::kFocusable) &&
       !ui::IsControl(owner_->GetRole()) && value.empty() &&
diff --git a/content/browser/accessibility/browser_accessibility_com_win.cc b/content/browser/accessibility/browser_accessibility_com_win.cc
index e82b0351..1183e151 100644
--- a/content/browser/accessibility/browser_accessibility_com_win.cc
+++ b/content/browser/accessibility/browser_accessibility_com_win.cc
@@ -1902,9 +1902,9 @@
     unsigned int blue = SkColorGetB(color);
     // Don't expose default value of pure white.
     if (alpha && (red != 255 || green != 255 || blue != 255)) {
-      base::string16 color_value = L"rgb(" + base::UintToString16(red) + L',' +
-                                   base::UintToString16(green) + L',' +
-                                   base::UintToString16(blue) + L')';
+      base::string16 color_value = L"rgb(" + base::NumberToString16(red) +
+                                   L',' + base::NumberToString16(green) + L',' +
+                                   base::NumberToString16(blue) + L')';
       SanitizeStringAttributeForIA2(color_value, &color_value);
       attributes.push_back(L"background-color:" + color_value);
     }
@@ -1916,9 +1916,9 @@
     unsigned int blue = SkColorGetB(color);
     // Don't expose default value of black.
     if (red || green || blue) {
-      base::string16 color_value = L"rgb(" + base::UintToString16(red) + L',' +
-                                   base::UintToString16(green) + L',' +
-                                   base::UintToString16(blue) + L')';
+      base::string16 color_value = L"rgb(" + base::NumberToString16(red) +
+                                   L',' + base::NumberToString16(green) + L',' +
+                                   base::NumberToString16(blue) + L')';
       SanitizeStringAttributeForIA2(color_value, &color_value);
       attributes.push_back(L"color:" + color_value);
     }
diff --git a/content/browser/accessibility/dump_accessibility_events_browsertest.cc b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
index a283d6e..4437c98fa 100644
--- a/content/browser/accessibility/dump_accessibility_events_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_events_browsertest.cc
@@ -502,4 +502,19 @@
   RunEventTest(FILE_PATH_LITERAL("aria-pressed-changed.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
+                       AccessibilityEventsTheadFocus) {
+  RunEventTest(FILE_PATH_LITERAL("thead-focus.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
+                       AccessibilityEventsTfootFocus) {
+  RunEventTest(FILE_PATH_LITERAL("tfoot-focus.html"));
+}
+
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityEventsTest,
+                       AccessibilityEventsTbodyFocus) {
+  RunEventTest(FILE_PATH_LITERAL("tbody-focus.html"));
+}
+
 }  // namespace content
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 119c858..3603e0d 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -893,6 +893,10 @@
   RunAriaTest(FILE_PATH_LITERAL("aria-setsize.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityAriaSetCountsWithHiddenItems) {
+  RunAriaTest(FILE_PATH_LITERAL("aria-set-counts-with-hidden-items.html"));
+}
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityAriaSlider) {
   RunAriaTest(FILE_PATH_LITERAL("aria-slider.html"));
 }
@@ -1860,6 +1864,11 @@
   RunHtmlTest(FILE_PATH_LITERAL("table-thead-tbody-tfoot.html"));
 }
 
+IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest,
+                       AccessibilityTableFocusableSections) {
+  RunHtmlTest(FILE_PATH_LITERAL("table-focusable-sections.html"));
+}
+
 IN_PROC_BROWSER_TEST_F(DumpAccessibilityTreeTest, AccessibilityTableSpans) {
   RunHtmlTest(FILE_PATH_LITERAL("table-spans.html"));
 }
diff --git a/content/browser/accessibility/touch_accessibility_aura_browsertest.cc b/content/browser/accessibility/touch_accessibility_aura_browsertest.cc
index 0ec7810..bbecb36 100644
--- a/content/browser/accessibility/touch_accessibility_aura_browsertest.cc
+++ b/content/browser/accessibility/touch_accessibility_aura_browsertest.cc
@@ -75,7 +75,7 @@
   for (int row = 0; row < 5; ++row) {
     html_url += "<tr>";
     for (int col = 0; col < 7; ++col) {
-      html_url += "<td>" + base::IntToString(cell) + "</td>";
+      html_url += "<td>" + base::NumberToString(cell) + "</td>";
       ++cell;
     }
     html_url += "</tr>";
@@ -98,7 +98,7 @@
       shell()->web_contents(), ui::kAXModeComplete, ax::mojom::Event::kHover);
   for (int row = 0; row < 5; ++row) {
     for (int col = 0; col < 7; ++col) {
-      std::string expected_cell_text = base::IntToString(row * 7 + col);
+      std::string expected_cell_text = base::NumberToString(row * 7 + col);
       VLOG(1) << "Sending event in row " << row << " col " << col
               << " with text " << expected_cell_text;
       SendTouchExplorationEvent(50 * col + 25, 50 * row + 25);
diff --git a/content/browser/android/java/gin_java_script_to_java_types_coercion.cc b/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
index af82718..dabd66b 100644
--- a/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
+++ b/content/browser/android/java/gin_java_script_to_java_types_coercion.cc
@@ -106,10 +106,10 @@
       result.l = NULL;
       break;
     case JavaType::TypeString:
-      result.l = coerce_to_string
-                     ? ConvertUTF8ToJavaString(
-                           env, base::Int64ToString(int_value)).Release()
-                     : NULL;
+      result.l = coerce_to_string ? ConvertUTF8ToJavaString(
+                                        env, base::NumberToString(int_value))
+                                        .Release()
+                                  : NULL;
       break;
     case JavaType::TypeBoolean:
       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
@@ -539,7 +539,7 @@
   }
   auto null_value = std::make_unique<base::Value>();
   for (jsize i = 0; i < length; ++i) {
-    const std::string key(base::IntToString(i));
+    const std::string key(base::NumberToString(i));
     const base::Value* value_element = null_value.get();
     if (dictionary_value->HasKey(key)) {
       dictionary_value->Get(key, &value_element);
diff --git a/content/browser/appcache/appcache_backend_impl.cc b/content/browser/appcache/appcache_backend_impl.cc
index 26bcd38..485195a 100644
--- a/content/browser/appcache/appcache_backend_impl.cc
+++ b/content/browser/appcache/appcache_backend_impl.cc
@@ -4,125 +4,154 @@
 
 #include "content/browser/appcache/appcache_backend_impl.h"
 
+#include <memory>
+#include <vector>
+
 #include "content/browser/appcache/appcache.h"
 #include "content/browser/appcache/appcache_group.h"
+#include "content/browser/appcache/appcache_navigation_handle_core.h"
 #include "content/browser/appcache/appcache_service_impl.h"
 #include "content/public/browser/browser_thread.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 
 namespace content {
 
-AppCacheBackendImpl::AppCacheBackendImpl()
-    : service_(nullptr), frontend_(nullptr), process_id_(0) {}
-
-AppCacheBackendImpl::~AppCacheBackendImpl() {
-  hosts_.clear();
-  if (service_)
-    service_->UnregisterBackend(this);
-}
-
-void AppCacheBackendImpl::Initialize(AppCacheServiceImpl* service,
-                                     AppCacheFrontend* frontend,
-                                     int process_id) {
-  DCHECK(!service_ && !frontend_ && frontend && service);
-  service_ = service;
-  frontend_ = frontend;
-  process_id_ = process_id;
+AppCacheBackendImpl::AppCacheBackendImpl(AppCacheServiceImpl* service,
+                                         int process_id)
+    : service_(service),
+      frontend_proxy_(process_id),
+      frontend_(&frontend_proxy_),
+      process_id_(process_id) {
+  DCHECK(service);
   service_->RegisterBackend(this);
 }
 
-bool AppCacheBackendImpl::RegisterHost(int id) {
-  if (GetHost(id))
-    return false;
+AppCacheBackendImpl::~AppCacheBackendImpl() {
+  hosts_.clear();
+  service_->UnregisterBackend(this);
+}
+
+void AppCacheBackendImpl::RegisterHost(int32_t id) {
+  // The AppCacheHost could have been precreated in which case we want to
+  // register it with the backend here.
+  std::unique_ptr<AppCacheHost> host =
+      AppCacheNavigationHandleCore::GetPrecreatedHost(id);
+  if (host) {
+    RegisterPrecreatedHost(std::move(host));
+    return;
+  }
+
+  if (GetHost(id)) {
+    mojo::ReportBadMessage("ACDH_REGISTER");
+    return;
+  }
 
   hosts_[id] =
       std::make_unique<AppCacheHost>(id, process_id(), frontend_, service_);
-  return true;
 }
 
-bool AppCacheBackendImpl::UnregisterHost(int id) {
-  return hosts_.erase(id) > 0;
+void AppCacheBackendImpl::UnregisterHost(int32_t id) {
+  if (!hosts_.erase(id))
+    mojo::ReportBadMessage("ACDH_UNREGISTER");
 }
 
-bool AppCacheBackendImpl::SetSpawningHostId(
-    int host_id,
-    int spawning_host_id) {
+void AppCacheBackendImpl::SetSpawningHostId(int32_t host_id,
+                                            int32_t spawning_host_id) {
   AppCacheHost* host = GetHost(host_id);
-  if (!host)
-    return false;
+  if (!host) {
+    mojo::ReportBadMessage("ACDH_SET_SPAWNING");
+    return;
+  }
   host->SetSpawningHostId(process_id_, spawning_host_id);
-  return true;
 }
 
-bool AppCacheBackendImpl::SelectCache(
-    int host_id,
+void AppCacheBackendImpl::SelectCache(
+    int32_t host_id,
     const GURL& document_url,
     const int64_t cache_document_was_loaded_from,
     const GURL& manifest_url) {
   AppCacheHost* host = GetHost(host_id);
-  if (!host)
-    return false;
-
-  return host->SelectCache(document_url, cache_document_was_loaded_from,
-                    manifest_url);
+  if (!host) {
+    mojo::ReportBadMessage("ACDH_SELECT_CACHE");
+    return;
+  }
+  host->SelectCache(document_url, cache_document_was_loaded_from, manifest_url);
 }
 
-bool AppCacheBackendImpl::SelectCacheForSharedWorker(int host_id,
+void AppCacheBackendImpl::SelectCacheForSharedWorker(int32_t host_id,
                                                      int64_t appcache_id) {
   AppCacheHost* host = GetHost(host_id);
-  if (!host)
-    return false;
-
-  return host->SelectCacheForSharedWorker(appcache_id);
+  if (!host) {
+    mojo::ReportBadMessage("ACDH_SELECT_CACHE_FOR_SHARED_WORKER");
+    return;
+  }
+  host->SelectCacheForSharedWorker(appcache_id);
 }
 
-bool AppCacheBackendImpl::MarkAsForeignEntry(
-    int host_id,
+void AppCacheBackendImpl::MarkAsForeignEntry(
+    int32_t host_id,
     const GURL& document_url,
     int64_t cache_document_was_loaded_from) {
   AppCacheHost* host = GetHost(host_id);
-  if (!host)
-    return false;
-
-  return host->MarkAsForeignEntry(document_url, cache_document_was_loaded_from);
-}
-
-bool AppCacheBackendImpl::GetStatusWithCallback(int host_id,
-                                                GetStatusCallback* callback) {
-  AppCacheHost* host = GetHost(host_id);
-  if (!host)
-    return false;
-
-  return host->GetStatusWithCallback(std::move(*callback));
-}
-
-bool AppCacheBackendImpl::StartUpdateWithCallback(
-    int host_id,
-    StartUpdateCallback* callback) {
-  AppCacheHost* host = GetHost(host_id);
-  if (!host)
-    return false;
-
-  return host->StartUpdateWithCallback(std::move(*callback));
-}
-
-bool AppCacheBackendImpl::SwapCacheWithCallback(int host_id,
-                                                SwapCacheCallback* callback) {
-  AppCacheHost* host = GetHost(host_id);
-  if (!host)
-    return false;
-
-  return host->SwapCacheWithCallback(std::move(*callback));
-}
-
-void AppCacheBackendImpl::GetResourceList(
-    int host_id,
-    std::vector<blink::mojom::AppCacheResourceInfo>* resource_infos) {
-  AppCacheHost* host = GetHost(host_id);
-  if (!host)
+  if (!host) {
+    mojo::ReportBadMessage("ACDH_MARK_AS_FOREIGN_ENTRY");
     return;
+  }
+  host->MarkAsForeignEntry(document_url, cache_document_was_loaded_from);
+}
 
-  host->GetResourceList(resource_infos);
+void AppCacheBackendImpl::GetStatus(int32_t host_id,
+                                    GetStatusCallback callback) {
+  AppCacheHost* host = GetHost(host_id);
+  if (!host) {
+    mojo::ReportBadMessage("ACDH_GET_STATUS");
+    std::move(callback).Run(
+        blink::mojom::AppCacheStatus::APPCACHE_STATUS_UNCACHED);
+    return;
+  }
+
+  host->GetStatusWithCallback(std::move(callback));
+}
+
+void AppCacheBackendImpl::StartUpdate(int32_t host_id,
+                                      StartUpdateCallback callback) {
+  AppCacheHost* host = GetHost(host_id);
+  if (!host) {
+    mojo::ReportBadMessage("ACDH_START_UPDATE");
+    std::move(callback).Run(false);
+    return;
+  }
+
+  host->StartUpdateWithCallback(std::move(callback));
+}
+
+void AppCacheBackendImpl::SwapCache(int32_t host_id,
+                                    SwapCacheCallback callback) {
+  AppCacheHost* host = GetHost(host_id);
+  if (!host) {
+    mojo::ReportBadMessage("ACDH_SWAP_CACHE");
+    std::move(callback).Run(false);
+    return;
+  }
+
+  host->SwapCacheWithCallback(std::move(callback));
+}
+
+void AppCacheBackendImpl::GetResourceList(int32_t host_id,
+                                          GetResourceListCallback callback) {
+  std::vector<blink::mojom::AppCacheResourceInfo> params;
+  std::vector<blink::mojom::AppCacheResourceInfoPtr> out;
+
+  AppCacheHost* host = GetHost(host_id);
+  if (host)
+    host->GetResourceList(&params);
+
+  // Box up params for output.
+  out.reserve(params.size());
+  for (auto& p : params) {
+    out.emplace_back(base::in_place, std::move(p));
+  }
+  std::move(callback).Run(std::move(out));
 }
 
 void AppCacheBackendImpl::RegisterPrecreatedHost(
@@ -137,4 +166,9 @@
   hosts_[host->host_id()] = std::move(host);
 }
 
+void AppCacheBackendImpl::RegisterHostForTesting(int32_t id) {
+  hosts_[id] =
+      std::make_unique<AppCacheHost>(id, process_id(), frontend_, service_);
+}
+
 }  // namespace content
diff --git a/content/browser/appcache/appcache_backend_impl.h b/content/browser/appcache/appcache_backend_impl.h
index 30eb806..b0c1157 100644
--- a/content/browser/appcache/appcache_backend_impl.h
+++ b/content/browser/appcache/appcache_backend_impl.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include "content/browser/appcache/appcache_frontend_proxy.h"
 #include "content/browser/appcache/appcache_host.h"
 #include "content/common/content_export.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
@@ -15,44 +16,32 @@
 
 class AppCacheServiceImpl;
 
-class CONTENT_EXPORT AppCacheBackendImpl {
+class CONTENT_EXPORT AppCacheBackendImpl
+    : public blink::mojom::AppCacheBackend {
  public:
-  AppCacheBackendImpl();
-  ~AppCacheBackendImpl();
-
-  void Initialize(AppCacheServiceImpl* service,
-                  AppCacheFrontend* frontend,
-                  int process_id);
+  AppCacheBackendImpl(AppCacheServiceImpl* service, int process_id);
+  ~AppCacheBackendImpl() override;
 
   int process_id() const { return process_id_; }
 
-  // Methods to support the AppCacheBackend interface. A false return
-  // value indicates an invalid host_id and that no action was taken
-  // by the backend impl.
-  bool RegisterHost(int host_id);
-  bool UnregisterHost(int host_id);
-  bool SetSpawningHostId(int host_id, int spawning_host_id);
-  bool SelectCache(int host_id,
+  // blink::mojom::AppCacheBackend
+  void RegisterHost(int32_t host_id) override;
+  void UnregisterHost(int32_t host_id) override;
+  void SetSpawningHostId(int32_t host_id, int spawning_host_id) override;
+  void SelectCache(int32_t host_id,
                    const GURL& document_url,
-                   const int64_t cache_document_was_loaded_from,
-                   const GURL& manifest_url);
-  void GetResourceList(
-      int host_id,
-      std::vector<blink::mojom::AppCacheResourceInfo>* resource_infos);
-  bool SelectCacheForSharedWorker(int host_id, int64_t appcache_id);
-  bool MarkAsForeignEntry(int host_id,
+                   int64_t cache_document_was_loaded_from,
+                   const GURL& opt_manifest_url) override;
+  void SelectCacheForSharedWorker(int32_t host_id,
+                                  int64_t appcache_id) override;
+  void MarkAsForeignEntry(int32_t host_id,
                           const GURL& document_url,
-                          int64_t cache_document_was_loaded_from);
-
-  // The xxxWithCallback functions take ownership of the callback iff the host
-  // is found (and the return value is true). If the result is false, the
-  // callback might still be available to the caller of these methods.
-  // TODO(mek): Just pass callbacks unconditionally. That is possible if the
-  // caller is changed to call BindingSet::ReportBadMessage rather than calling
-  // the global mojo::ReportBadMessage.
-  bool GetStatusWithCallback(int host_id, GetStatusCallback* callback);
-  bool StartUpdateWithCallback(int host_id, StartUpdateCallback* callback);
-  bool SwapCacheWithCallback(int host_id, SwapCacheCallback* callback);
+                          int64_t cache_document_was_loaded_from) override;
+  void GetStatus(int32_t host_id, GetStatusCallback callback) override;
+  void StartUpdate(int32_t host_id, StartUpdateCallback callback) override;
+  void SwapCache(int32_t host_id, SwapCacheCallback callback) override;
+  void GetResourceList(int32_t host_id,
+                       GetResourceListCallback callback) override;
 
   // Returns a pointer to a registered host. The backend retains ownership.
   AppCacheHost* GetHost(int host_id) {
@@ -68,9 +57,18 @@
   // this function and ignore registrations for this host id from the renderer.
   void RegisterPrecreatedHost(std::unique_ptr<AppCacheHost> host);
 
+  void RegisterHostForTesting(int32_t host_id);
+
+  void set_frontend_for_testing(blink::mojom::AppCacheFrontend* frontend) {
+    frontend_ = frontend;
+  }
+
  private:
+  // Raw pointer is safe because instances of this class are owned by
+  // |service_|.
   AppCacheServiceImpl* service_;
-  AppCacheFrontend* frontend_;
+  AppCacheFrontendProxy frontend_proxy_;
+  blink::mojom::AppCacheFrontend* frontend_;
   int process_id_;
   HostMap hosts_;
 
diff --git a/content/browser/appcache/appcache_disk_cache.cc b/content/browser/appcache/appcache_disk_cache.cc
index 242bf8c..d38e9ed5 100644
--- a/content/browser/appcache/appcache_disk_cache.cc
+++ b/content/browser/appcache/appcache_disk_cache.cc
@@ -119,7 +119,7 @@
     scoped_refptr<ActiveCall> active_call(
         new ActiveCall(owner, entry, std::move(callback)));
     net::Error return_value = owner->disk_cache()->CreateEntry(
-        base::Int64ToString(key), net::HIGHEST, &active_call->entry_ptr_,
+        base::NumberToString(key), net::HIGHEST, &active_call->entry_ptr_,
         base::BindOnce(&ActiveCall::OnAsyncCompletion, active_call));
     return active_call->HandleImmediateReturnValue(return_value);
   }
@@ -131,7 +131,7 @@
     scoped_refptr<ActiveCall> active_call(
         new ActiveCall(owner, entry, std::move(callback)));
     net::Error return_value = owner->disk_cache()->OpenEntry(
-        base::Int64ToString(key), net::HIGHEST, &active_call->entry_ptr_,
+        base::NumberToString(key), net::HIGHEST, &active_call->entry_ptr_,
         base::BindOnce(&ActiveCall::OnAsyncCompletion, active_call));
     return active_call->HandleImmediateReturnValue(return_value);
   }
@@ -142,7 +142,7 @@
     scoped_refptr<ActiveCall> active_call(
         new ActiveCall(owner, nullptr, std::move(callback)));
     net::Error return_value = owner->disk_cache()->DoomEntry(
-        base::Int64ToString(key), net::HIGHEST,
+        base::NumberToString(key), net::HIGHEST,
         base::BindOnce(&ActiveCall::OnAsyncCompletion, active_call));
     return active_call->HandleImmediateReturnValue(return_value);
   }
diff --git a/content/browser/appcache/appcache_dispatcher_host.cc b/content/browser/appcache/appcache_dispatcher_host.cc
deleted file mode 100644
index 2cc32a04..0000000
--- a/content/browser/appcache/appcache_dispatcher_host.cc
+++ /dev/null
@@ -1,150 +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 "content/browser/appcache/appcache_dispatcher_host.h"
-
-#include <map>
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "content/browser/appcache/appcache_navigation_handle_core.h"
-#include "content/browser/appcache/chrome_appcache_service.h"
-#include "content/browser/bad_message.h"
-#include "content/public/browser/render_process_host.h"
-#include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
-#include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
-
-namespace content {
-
-AppCacheDispatcherHost::AppCacheDispatcherHost(
-    ChromeAppCacheService* appcache_service,
-    int process_id)
-    : frontend_proxy_(process_id) {
-  DCHECK(appcache_service);
-  backend_impl_.Initialize(appcache_service, &frontend_proxy_, process_id);
-}
-
-AppCacheDispatcherHost::~AppCacheDispatcherHost() = default;
-
-// static
-void AppCacheDispatcherHost::Create(
-    ChromeAppCacheService* appcache_service,
-    int process_id,
-    blink::mojom::AppCacheBackendRequest request) {
-  // The process_id is the id of the RenderProcessHost, which can be reused for
-  // a new renderer process if the previous renderer process was shutdown.
-  // It can take some time after shutdown for the pipe error to propagate
-  // and unregister the previous backend. Since the AppCacheService assumes
-  // that there is one backend per process_id, we need to ensure that the
-  // previous backend is unregistered by eagerly unbinding the pipe.
-  appcache_service->Unbind(process_id);
-
-  appcache_service->Bind(
-      std::make_unique<AppCacheDispatcherHost>(appcache_service, process_id),
-      std::move(request), process_id);
-}
-
-void AppCacheDispatcherHost::RegisterHost(int32_t host_id) {
-  // The AppCacheHost could have been precreated in which case we want to
-  // register it with the backend here.
-  std::unique_ptr<content::AppCacheHost> host =
-      AppCacheNavigationHandleCore::GetPrecreatedHost(host_id);
-  if (host.get()) {
-    backend_impl_.RegisterPrecreatedHost(std::move(host));
-    return;
-  }
-
-  if (!backend_impl_.RegisterHost(host_id)) {
-    mojo::ReportBadMessage("ACDH_REGISTER");
-  }
-}
-
-void AppCacheDispatcherHost::UnregisterHost(int32_t host_id) {
-  if (!backend_impl_.UnregisterHost(host_id)) {
-    mojo::ReportBadMessage("ACDH_UNREGISTER");
-  }
-}
-
-void AppCacheDispatcherHost::SetSpawningHostId(int32_t host_id,
-                                               int spawning_host_id) {
-  if (!backend_impl_.SetSpawningHostId(host_id, spawning_host_id))
-    mojo::ReportBadMessage("ACDH_SET_SPAWNING");
-}
-
-void AppCacheDispatcherHost::SelectCache(int32_t host_id,
-                                         const GURL& document_url,
-                                         int64_t cache_document_was_loaded_from,
-                                         const GURL& opt_manifest_url) {
-  if (!backend_impl_.SelectCache(host_id, document_url,
-                                 cache_document_was_loaded_from,
-                                 opt_manifest_url)) {
-    mojo::ReportBadMessage("ACDH_SELECT_CACHE");
-  }
-}
-
-void AppCacheDispatcherHost::SelectCacheForSharedWorker(int32_t host_id,
-                                                        int64_t appcache_id) {
-  if (!backend_impl_.SelectCacheForSharedWorker(host_id, appcache_id))
-    mojo::ReportBadMessage("ACDH_SELECT_CACHE_FOR_SHARED_WORKER");
-}
-
-void AppCacheDispatcherHost::MarkAsForeignEntry(
-    int32_t host_id,
-    const GURL& document_url,
-    int64_t cache_document_was_loaded_from) {
-  if (!backend_impl_.MarkAsForeignEntry(host_id, document_url,
-                                        cache_document_was_loaded_from)) {
-    mojo::ReportBadMessage("ACDH_MARK_AS_FOREIGN_ENTRY");
-  }
-}
-
-void AppCacheDispatcherHost::GetResourceList(int32_t host_id,
-                                             GetResourceListCallback callback) {
-  std::vector<blink::mojom::AppCacheResourceInfo> params;
-  std::vector<blink::mojom::AppCacheResourceInfoPtr> out;
-  backend_impl_.GetResourceList(host_id, &params);
-
-  // Box up params for output.
-  out.reserve(params.size());
-  for (auto& p : params) {
-    out.emplace_back(base::in_place, std::move(p));
-  }
-  std::move(callback).Run(std::move(out));
-}
-
-void AppCacheDispatcherHost::GetStatus(int32_t host_id,
-                                       GetStatusCallback callback) {
-  if (backend_impl_.GetStatusWithCallback(host_id, &callback)) {
-    return;
-  } else {
-    mojo::ReportBadMessage("ACDH_GET_STATUS");
-  }
-  if (callback) {
-    std::move(callback).Run(
-        blink::mojom::AppCacheStatus::APPCACHE_STATUS_UNCACHED);
-  }
-}
-
-void AppCacheDispatcherHost::StartUpdate(int32_t host_id,
-                                         StartUpdateCallback callback) {
-  if (backend_impl_.StartUpdateWithCallback(host_id, &callback)) {
-    return;
-  } else {
-    mojo::ReportBadMessage("ACDH_START_UPDATE");
-  }
-  if (callback)
-    std::move(callback).Run(false);
-}
-
-void AppCacheDispatcherHost::SwapCache(int32_t host_id,
-                                       SwapCacheCallback callback) {
-  if (backend_impl_.SwapCacheWithCallback(host_id, &callback)) {
-    return;
-  } else {
-    mojo::ReportBadMessage("ACDH_SWAP_CACHE");
-  }
-  if (callback)
-    std::move(callback).Run(false);
-}
-
-}  // namespace content
diff --git a/content/browser/appcache/appcache_dispatcher_host.h b/content/browser/appcache/appcache_dispatcher_host.h
deleted file mode 100644
index d292814..0000000
--- a/content/browser/appcache/appcache_dispatcher_host.h
+++ /dev/null
@@ -1,66 +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.
-
-#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_DISPATCHER_HOST_H_
-#define CONTENT_BROWSER_APPCACHE_APPCACHE_DISPATCHER_HOST_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/process/process.h"
-#include "content/browser/appcache/appcache_backend_impl.h"
-#include "content/browser/appcache/appcache_frontend_proxy.h"
-#include "content/public/browser/browser_message_filter.h"
-#include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
-
-namespace content {
-class ChromeAppCacheService;
-
-// Handles appcache related messages sent to the main browser process from
-// its child processes. There is a distinct host for each child process.
-// Messages are handled on the IO thread. The RenderProcessHostImpl creates
-// an instance and delegates calls to it.
-class AppCacheDispatcherHost : public blink::mojom::AppCacheBackend {
- public:
-  AppCacheDispatcherHost(ChromeAppCacheService* appcache_service,
-                         int process_id);
-  ~AppCacheDispatcherHost() override;
-
-  static void Create(ChromeAppCacheService* appcache_service,
-                     int process_id,
-                     blink::mojom::AppCacheBackendRequest request);
-
- private:
-  // blink::mojom::AppCacheBackend
-  void RegisterHost(int32_t host_id) override;
-  void UnregisterHost(int32_t host_id) override;
-  void SetSpawningHostId(int32_t host_id, int spawning_host_id) override;
-  void SelectCache(int32_t host_id,
-                   const GURL& document_url,
-                   int64_t cache_document_was_loaded_from,
-                   const GURL& opt_manifest_url) override;
-  void SelectCacheForSharedWorker(int32_t host_id,
-                                  int64_t appcache_id) override;
-  void MarkAsForeignEntry(int32_t host_id,
-                          const GURL& document_url,
-                          int64_t cache_document_was_loaded_from) override;
-  void GetStatus(int32_t host_id, GetStatusCallback callback) override;
-  void StartUpdate(int32_t host_id, StartUpdateCallback callback) override;
-  void SwapCache(int32_t host_id, SwapCacheCallback callback) override;
-  void GetResourceList(int32_t host_id,
-                       GetResourceListCallback callback) override;
-
-  AppCacheFrontendProxy frontend_proxy_;
-  AppCacheBackendImpl backend_impl_;
-
-  DISALLOW_COPY_AND_ASSIGN(AppCacheDispatcherHost);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_APPCACHE_APPCACHE_DISPATCHER_HOST_H_
diff --git a/content/browser/appcache/appcache_frontend.h b/content/browser/appcache/appcache_frontend.h
deleted file mode 100644
index ec7837d2..0000000
--- a/content/browser/appcache/appcache_frontend.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_BROWSER_APPCACHE_APPCACHE_FRONTEND_H_
-#define CONTENT_BROWSER_APPCACHE_APPCACHE_FRONTEND_H_
-
-#include <string>
-#include <vector>
-
-#include "base/files/file_path.h"
-#include "content/common/content_export.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
-#include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
-#include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
-#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
-
-class GURL;
-
-namespace content {
-
-// Interface used by backend (browser-process) to talk to frontend (renderer).
-class CONTENT_EXPORT AppCacheFrontend {
- public:
-  virtual ~AppCacheFrontend() = default;
-
-  virtual void OnCacheSelected(int host_id,
-                               const blink::mojom::AppCacheInfo& info) = 0;
-  virtual void OnStatusChanged(const std::vector<int>& host_ids,
-                               blink::mojom::AppCacheStatus status) = 0;
-  virtual void OnEventRaised(const std::vector<int>& host_ids,
-                             blink::mojom::AppCacheEventID event_id) = 0;
-  virtual void OnProgressEventRaised(const std::vector<int>& host_ids,
-                                     const GURL& url,
-                                     int num_total,
-                                     int num_complete) = 0;
-  virtual void OnErrorEventRaised(
-      const std::vector<int>& host_ids,
-      const blink::mojom::AppCacheErrorDetails& details) = 0;
-  virtual void OnContentBlocked(int host_id, const GURL& manifest_url) = 0;
-  virtual void OnLogMessage(int host_id,
-                            blink::mojom::ConsoleMessageLevel log_level,
-                            const std::string& message) = 0;
-  // In the network service world, we pass the URLLoaderFactory instance to be
-  // used to issue subresource requeste in the |loader_factory_pipe_handle|
-  // parameter.
-  virtual void OnSetSubresourceFactory(
-      int host_id,
-      network::mojom::URLLoaderFactoryPtr url_loader_factory) = 0;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_BROWSER_APPCACHE_APPCACHE_FRONTEND_H_
\ No newline at end of file
diff --git a/content/browser/appcache/appcache_frontend_proxy.cc b/content/browser/appcache/appcache_frontend_proxy.cc
index 6c3eafed9..e46c5fe5 100644
--- a/content/browser/appcache/appcache_frontend_proxy.cc
+++ b/content/browser/appcache/appcache_frontend_proxy.cc
@@ -40,54 +40,53 @@
   return app_cache_renderer_ptr_.get();
 }
 
-void AppCacheFrontendProxy::OnCacheSelected(
-    int host_id,
-    const blink::mojom::AppCacheInfo& info) {
-  // TODO(crbug:611938) Get rid of the need to Clone().
-  GetAppCacheFrontend()->CacheSelected(host_id, info.Clone());
+void AppCacheFrontendProxy::CacheSelected(int32_t host_id,
+                                          blink::mojom::AppCacheInfoPtr info) {
+  GetAppCacheFrontend()->CacheSelected(host_id, std::move(info));
 }
 
-void AppCacheFrontendProxy::OnStatusChanged(
-    const std::vector<int>& host_ids,
-    blink::mojom::AppCacheStatus status) {
+void AppCacheFrontendProxy::StatusChanged(const std::vector<int32_t>& host_ids,
+                                          blink::mojom::AppCacheStatus status) {
   GetAppCacheFrontend()->StatusChanged(host_ids, status);
 }
 
-void AppCacheFrontendProxy::OnEventRaised(
-    const std::vector<int>& host_ids,
+void AppCacheFrontendProxy::EventRaised(
+    const std::vector<int32_t>& host_ids,
     blink::mojom::AppCacheEventID event_id) {
   DCHECK_NE(blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT,
             event_id);  // See OnProgressEventRaised.
   GetAppCacheFrontend()->EventRaised(host_ids, event_id);
 }
 
-void AppCacheFrontendProxy::OnProgressEventRaised(
-    const std::vector<int>& host_ids,
-    const GURL& url, int num_total, int num_complete) {
+void AppCacheFrontendProxy::ProgressEventRaised(
+    const std::vector<int32_t>& host_ids,
+    const GURL& url,
+    int32_t num_total,
+    int32_t num_complete) {
   GetAppCacheFrontend()->ProgressEventRaised(host_ids, url, num_total,
                                              num_complete);
 }
 
-void AppCacheFrontendProxy::OnErrorEventRaised(
-    const std::vector<int>& host_ids,
-    const blink::mojom::AppCacheErrorDetails& details) {
-  GetAppCacheFrontend()->ErrorEventRaised(host_ids, details.Clone());
+void AppCacheFrontendProxy::ErrorEventRaised(
+    const std::vector<int32_t>& host_ids,
+    blink::mojom::AppCacheErrorDetailsPtr details) {
+  GetAppCacheFrontend()->ErrorEventRaised(host_ids, std::move(details));
 }
 
-void AppCacheFrontendProxy::OnLogMessage(
-    int host_id,
+void AppCacheFrontendProxy::LogMessage(
+    int32_t host_id,
     blink::mojom::ConsoleMessageLevel log_level,
     const std::string& message) {
   GetAppCacheFrontend()->LogMessage(host_id, log_level, message);
 }
 
-void AppCacheFrontendProxy::OnContentBlocked(int host_id,
-                                             const GURL& manifest_url) {
+void AppCacheFrontendProxy::ContentBlocked(int32_t host_id,
+                                           const GURL& manifest_url) {
   GetAppCacheFrontend()->ContentBlocked(host_id, manifest_url);
 }
 
-void AppCacheFrontendProxy::OnSetSubresourceFactory(
-    int host_id,
+void AppCacheFrontendProxy::SetSubresourceFactory(
+    int32_t host_id,
     network::mojom::URLLoaderFactoryPtr url_loader_factory) {
   GetAppCacheFrontend()->SetSubresourceFactory(host_id,
                                                std::move(url_loader_factory));
diff --git a/content/browser/appcache/appcache_frontend_proxy.h b/content/browser/appcache/appcache_frontend_proxy.h
index 2690f28..b2871636 100644
--- a/content/browser/appcache/appcache_frontend_proxy.h
+++ b/content/browser/appcache/appcache_frontend_proxy.h
@@ -8,7 +8,6 @@
 #include <string>
 #include <vector>
 
-#include "content/browser/appcache/appcache_frontend.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
@@ -16,31 +15,30 @@
 namespace content {
 
 // Sends appcache related messages to a child process.
-class AppCacheFrontendProxy : public AppCacheFrontend {
+class AppCacheFrontendProxy : public blink::mojom::AppCacheFrontend {
  public:
   explicit AppCacheFrontendProxy(int process_id);
   ~AppCacheFrontendProxy() override;
 
   // AppCacheFrontend methods
-  void OnCacheSelected(int host_id,
-                       const blink::mojom::AppCacheInfo& info) override;
-  void OnStatusChanged(const std::vector<int>& host_ids,
-                       blink::mojom::AppCacheStatus status) override;
-  void OnEventRaised(const std::vector<int>& host_ids,
-                     blink::mojom::AppCacheEventID event_id) override;
-  void OnProgressEventRaised(const std::vector<int>& host_ids,
-                             const GURL& url,
-                             int num_total,
-                             int num_complete) override;
-  void OnErrorEventRaised(
-      const std::vector<int>& host_ids,
-      const blink::mojom::AppCacheErrorDetails& details) override;
-  void OnLogMessage(int host_id,
-                    blink::mojom::ConsoleMessageLevel log_level,
-                    const std::string& message) override;
-  void OnContentBlocked(int host_id, const GURL& manifest_url) override;
-  void OnSetSubresourceFactory(
-      int host_id,
+  void CacheSelected(int32_t host_id,
+                     blink::mojom::AppCacheInfoPtr info) override;
+  void StatusChanged(const std::vector<int32_t>& host_ids,
+                     blink::mojom::AppCacheStatus status) override;
+  void EventRaised(const std::vector<int32_t>& host_ids,
+                   blink::mojom::AppCacheEventID event_id) override;
+  void ProgressEventRaised(const std::vector<int32_t>& host_ids,
+                           const GURL& url,
+                           int32_t num_total,
+                           int32_t num_complete) override;
+  void ErrorEventRaised(const std::vector<int32_t>& host_ids,
+                        blink::mojom::AppCacheErrorDetailsPtr details) override;
+  void LogMessage(int32_t host_id,
+                  blink::mojom::ConsoleMessageLevel log_level,
+                  const std::string& message) override;
+  void ContentBlocked(int32_t host_id, const GURL& manifest_url) override;
+  void SetSubresourceFactory(
+      int32_t host_id,
       network::mojom::URLLoaderFactoryPtr url_loader_factory) override;
 
  private:
diff --git a/content/browser/appcache/appcache_fuzzer.cc b/content/browser/appcache/appcache_fuzzer.cc
index 2e902e5..ce12023 100644
--- a/content/browser/appcache/appcache_fuzzer.cc
+++ b/content/browser/appcache/appcache_fuzzer.cc
@@ -10,7 +10,6 @@
 #include "base/task/post_task.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/test/test_timeouts.h"
-#include "content/browser/appcache/appcache_dispatcher_host.h"
 #include "content/browser/appcache/appcache_fuzzer.pb.h"
 #include "content/browser/appcache/chrome_appcache_service.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -149,8 +148,8 @@
         std::make_unique<mojo::internal::MessageDispatchContext>(&message);
 
   blink::mojom::AppCacheBackendPtr host;
-  AppCacheDispatcherHost::Create(SingletonEnv().appcache_service.get(),
-                                 /*process_id=*/1, mojo::MakeRequest(&host));
+  SingletonEnv().appcache_service->CreateBackend(/*process_id=*/1,
+                                                 mojo::MakeRequest(&host));
 
   for (const fuzzing::proto::Command& command : session.commands()) {
     switch (command.command_case()) {
diff --git a/content/browser/appcache/appcache_group_unittest.cc b/content/browser/appcache/appcache_group_unittest.cc
index 2dd834e..03cb46bd 100644
--- a/content/browser/appcache/appcache_group_unittest.cc
+++ b/content/browser/appcache/appcache_group_unittest.cc
@@ -8,7 +8,6 @@
 
 #include "base/test/scoped_task_environment.h"
 #include "content/browser/appcache/appcache.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "content/browser/appcache/appcache_group.h"
 #include "content/browser/appcache/appcache_host.h"
 #include "content/browser/appcache/appcache_update_job.h"
@@ -20,46 +19,46 @@
 
 namespace {
 
-class TestAppCacheFrontend : public content::AppCacheFrontend {
+class TestAppCacheFrontend : public blink::mojom::AppCacheFrontend {
  public:
   TestAppCacheFrontend()
       : last_host_id_(-1),
         last_cache_id_(-1),
         last_status_(blink::mojom::AppCacheStatus::APPCACHE_STATUS_OBSOLETE) {}
 
-  void OnCacheSelected(int host_id,
-                       const blink::mojom::AppCacheInfo& info) override {
+  void CacheSelected(int32_t host_id,
+                     blink::mojom::AppCacheInfoPtr info) override {
     last_host_id_ = host_id;
-    last_cache_id_ = info.cache_id;
-    last_status_ = info.status;
+    last_cache_id_ = info->cache_id;
+    last_status_ = info->status;
   }
 
-  void OnStatusChanged(const std::vector<int>& host_ids,
-                       blink::mojom::AppCacheStatus status) override {}
+  void StatusChanged(const std::vector<int32_t>& host_ids,
+                     blink::mojom::AppCacheStatus status) override {}
 
-  void OnEventRaised(const std::vector<int>& host_ids,
-                     blink::mojom::AppCacheEventID event_id) override {}
+  void EventRaised(const std::vector<int32_t>& host_ids,
+                   blink::mojom::AppCacheEventID event_id) override {}
 
-  void OnErrorEventRaised(
-      const std::vector<int>& host_ids,
-      const blink::mojom::AppCacheErrorDetails& details) override {}
+  void ErrorEventRaised(
+      const std::vector<int32_t>& host_ids,
+      blink::mojom::AppCacheErrorDetailsPtr details) override {}
 
-  void OnProgressEventRaised(const std::vector<int>& host_ids,
-                             const GURL& url,
-                             int num_total,
-                             int num_complete) override {}
+  void ProgressEventRaised(const std::vector<int32_t>& host_ids,
+                           const GURL& url,
+                           int32_t num_total,
+                           int32_t num_complete) override {}
 
-  void OnLogMessage(int host_id,
-                    blink::mojom::ConsoleMessageLevel log_level,
-                    const std::string& message) override {}
+  void LogMessage(int32_t host_id,
+                  blink::mojom::ConsoleMessageLevel log_level,
+                  const std::string& message) override {}
 
-  void OnContentBlocked(int host_id, const GURL& manifest_url) override {}
+  void ContentBlocked(int32_t host_id, const GURL& manifest_url) override {}
 
-  void OnSetSubresourceFactory(
-      int host_id,
+  void SetSubresourceFactory(
+      int32_t host_id,
       network::mojom::URLLoaderFactoryPtr url_loader_factory) override {}
 
-  int last_host_id_;
+  int32_t last_host_id_;
   int64_t last_cache_id_;
   blink::mojom::AppCacheStatus last_status_;
 };
@@ -88,7 +87,7 @@
 class TestAppCacheHost : public AppCacheHost {
  public:
   TestAppCacheHost(int host_id,
-                   AppCacheFrontend* frontend,
+                   blink::mojom::AppCacheFrontend* frontend,
                    AppCacheServiceImpl* service)
       : AppCacheHost(host_id, /* process_id = */ 456, frontend, service),
         update_completed_(false) {}
diff --git a/content/browser/appcache/appcache_host.cc b/content/browser/appcache/appcache_host.cc
index 51c066a..1d52936 100644
--- a/content/browser/appcache/appcache_host.cc
+++ b/content/browser/appcache/appcache_host.cc
@@ -4,6 +4,7 @@
 
 #include "content/browser/appcache/appcache_host.h"
 
+#include <utility>
 #include <vector>
 
 #include "base/logging.h"
@@ -12,7 +13,6 @@
 #include "base/strings/stringprintf.h"
 #include "content/browser/appcache/appcache.h"
 #include "content/browser/appcache/appcache_backend_impl.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "content/browser/appcache/appcache_policy.h"
 #include "content/browser/appcache/appcache_request.h"
 #include "content/browser/appcache/appcache_request_handler.h"
@@ -30,28 +30,28 @@
 
 namespace {
 
-blink::mojom::AppCacheInfo CreateCacheInfo(
+blink::mojom::AppCacheInfoPtr CreateCacheInfo(
     const AppCache* cache,
     const GURL& manifest_url,
     blink::mojom::AppCacheStatus status) {
-  blink::mojom::AppCacheInfo info;
-  info.manifest_url = manifest_url;
-  info.status = status;
+  auto info = blink::mojom::AppCacheInfo::New();
+  info->manifest_url = manifest_url;
+  info->status = status;
 
   if (!cache)
     return info;
 
-  info.cache_id = cache->cache_id();
+  info->cache_id = cache->cache_id();
 
   if (!cache->is_complete())
     return info;
 
   DCHECK(cache->owning_group());
-  info.is_complete = true;
-  info.group_id = cache->owning_group()->group_id();
-  info.last_update_time = cache->update_time();
-  info.creation_time = cache->owning_group()->creation_time();
-  info.size = cache->cache_size();
+  info->is_complete = true;
+  info->group_id = cache->owning_group()->group_id();
+  info->last_update_time = cache->update_time();
+  info->creation_time = cache->owning_group()->creation_time();
+  info->size = cache->cache_size();
   return info;
 }
 
@@ -59,7 +59,7 @@
 
 AppCacheHost::AppCacheHost(int host_id,
                            int process_id,
-                           AppCacheFrontend* frontend,
+                           blink::mojom::AppCacheFrontend* frontend,
                            AppCacheServiceImpl* service)
     : host_id_(host_id),
       process_id_(process_id),
@@ -113,11 +113,13 @@
   observers_.RemoveObserver(observer);
 }
 
-bool AppCacheHost::SelectCache(const GURL& document_url,
+void AppCacheHost::SelectCache(const GURL& document_url,
                                const int64_t cache_document_was_loaded_from,
                                const GURL& manifest_url) {
-  if (was_select_cache_called_)
-    return false;
+  if (was_select_cache_called_) {
+    mojo::ReportBadMessage("ACH_SELECT_CACHE");
+    return;
+  }
 
   DCHECK(pending_start_update_callback_.is_null() &&
          pending_swap_cache_callback_.is_null() &&
@@ -127,7 +129,7 @@
   was_select_cache_called_ = true;
   if (!is_cache_selection_enabled_) {
     FinishCacheSelection(nullptr, nullptr);
-    return true;
+    return;
   }
 
   origin_in_use_ = url::Origin::Create(document_url);
@@ -135,8 +137,7 @@
     service()->quota_manager_proxy()->NotifyOriginInUse(origin_in_use_);
 
   if (main_resource_blocked_)
-    frontend_->OnContentBlocked(host_id_,
-                                blocked_manifest_url_);
+    frontend_->ContentBlocked(host_id_, blocked_manifest_url_);
 
   // 6.9.6 The application cache selection algorithm.
   // The algorithm is started here and continues in FinishCacheSelection,
@@ -147,7 +148,7 @@
 
   if (cache_document_was_loaded_from != blink::mojom::kAppCacheNoCacheId) {
     LoadSelectedCache(cache_document_was_loaded_from);
-    return true;
+    return;
   }
 
   if (!manifest_url.is_empty() &&
@@ -167,16 +168,16 @@
         !policy->CanCreateAppCache(manifest_url, first_party_url_)) {
       FinishCacheSelection(nullptr, nullptr);
       std::vector<int> host_ids(1, host_id_);
-      frontend_->OnEventRaised(
+      frontend_->EventRaised(
           host_ids, blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT);
-      frontend_->OnErrorEventRaised(
+      frontend_->ErrorEventRaised(
           host_ids,
-          blink::mojom::AppCacheErrorDetails(
+          blink::mojom::AppCacheErrorDetails::New(
               "Cache creation was blocked by the content policy",
               blink::mojom::AppCacheErrorReason::APPCACHE_POLICY_ERROR, GURL(),
               0, false /*is_cross_origin*/));
-      frontend_->OnContentBlocked(host_id_, manifest_url);
-      return true;
+      frontend_->ContentBlocked(host_id_, manifest_url);
+      return;
     }
     // Note: The client detects if the document was not loaded using HTTP GET
     // and invokes SelectCache without a manifest url, so that detection step
@@ -184,17 +185,18 @@
     set_preferred_manifest_url(manifest_url);
     new_master_entry_url_ = document_url;
     LoadOrCreateGroup(manifest_url);
-    return true;
+    return;
   }
   // TODO(michaeln): If there was a manifest URL, the user agent may report
   // to the user that it was ignored, to aid in application development.
   FinishCacheSelection(nullptr, nullptr);
-  return true;
 }
 
-bool AppCacheHost::SelectCacheForSharedWorker(int64_t appcache_id) {
-  if (was_select_cache_called_)
-    return false;
+void AppCacheHost::SelectCacheForSharedWorker(int64_t appcache_id) {
+  if (was_select_cache_called_) {
+    mojo::ReportBadMessage("ACH_SELECT_CACHE_FOR_SHARED_WORKER");
+    return;
+  }
 
   DCHECK(pending_start_update_callback_.is_null() &&
          pending_swap_cache_callback_.is_null() &&
@@ -204,41 +206,41 @@
   was_select_cache_called_ = true;
   if (appcache_id != blink::mojom::kAppCacheNoCacheId) {
     LoadSelectedCache(appcache_id);
-    return true;
+    return;
   }
   FinishCacheSelection(nullptr, nullptr);
-  return true;
 }
 
 // TODO(michaeln): change method name to MarkEntryAsForeign for consistency
-bool AppCacheHost::MarkAsForeignEntry(const GURL& document_url,
+void AppCacheHost::MarkAsForeignEntry(const GURL& document_url,
                                       int64_t cache_document_was_loaded_from) {
-  if (was_select_cache_called_)
-    return false;
+  if (was_select_cache_called_) {
+    mojo::ReportBadMessage("ACH_MARK_AS_FOREIGN_ENTRY");
+    return;
+  }
 
   // The document url is not the resource url in the fallback case.
   storage()->MarkEntryAsForeign(
       main_resource_was_namespace_entry_ ? namespace_entry_url_ : document_url,
       cache_document_was_loaded_from);
   SelectCache(document_url, blink::mojom::kAppCacheNoCacheId, GURL());
-  return true;
 }
 
-bool AppCacheHost::GetStatusWithCallback(GetStatusCallback callback) {
+void AppCacheHost::GetStatusWithCallback(GetStatusCallback callback) {
   if (!pending_start_update_callback_.is_null() ||
       !pending_swap_cache_callback_.is_null() ||
       !pending_get_status_callback_.is_null()) {
+    mojo::ReportBadMessage("ACH_GET_STATUS");
     std::move(callback).Run(
         blink::mojom::AppCacheStatus::APPCACHE_STATUS_UNCACHED);
-    return false;
+    return;
   }
 
   pending_get_status_callback_ = std::move(callback);
   if (is_selection_pending())
-    return true;
+    return;
 
   DoPendingGetStatus();
-  return true;
 }
 
 void AppCacheHost::DoPendingGetStatus() {
@@ -247,20 +249,20 @@
   std::move(pending_get_status_callback_).Run(GetStatus());
 }
 
-bool AppCacheHost::StartUpdateWithCallback(StartUpdateCallback callback) {
+void AppCacheHost::StartUpdateWithCallback(StartUpdateCallback callback) {
   if (!pending_start_update_callback_.is_null() ||
       !pending_swap_cache_callback_.is_null() ||
       !pending_get_status_callback_.is_null()) {
+    mojo::ReportBadMessage("ACH_START_UPDATE");
     std::move(callback).Run(false);
-    return false;
+    return;
   }
 
   pending_start_update_callback_ = std::move(callback);
   if (is_selection_pending())
-    return true;
+    return;
 
   DoPendingStartUpdate();
-  return true;
 }
 
 void AppCacheHost::DoPendingStartUpdate() {
@@ -279,21 +281,21 @@
   std::move(pending_start_update_callback_).Run(success);
 }
 
-bool AppCacheHost::SwapCacheWithCallback(SwapCacheCallback callback) {
+void AppCacheHost::SwapCacheWithCallback(SwapCacheCallback callback) {
   if (!pending_start_update_callback_.is_null() ||
       !pending_swap_cache_callback_.is_null() ||
       !pending_get_status_callback_.is_null()) {
+    mojo::ReportBadMessage("ACH_SWAP_CACHE");
     std::move(callback).Run(false);
-    return false;
+    return;
   }
 
   pending_swap_cache_callback_ = std::move(callback);
 
   if (is_selection_pending())
-    return true;
+    return;
 
   DoPendingSwapCache();
-  return true;
 }
 
 void AppCacheHost::DoPendingSwapCache() {
@@ -435,7 +437,7 @@
     AppCacheGroup* owning_group = cache->owning_group();
     const char* kFormatString =
         "Document was loaded from Application Cache with manifest %s";
-    frontend_->OnLogMessage(
+    frontend_->LogMessage(
         host_id_, blink::mojom::ConsoleMessageLevel::kInfo,
         base::StringPrintf(kFormatString,
                            owning_group->manifest_url().spec().c_str()));
@@ -456,7 +458,7 @@
     const char* kFormatString = group->HasCache() ?
         "Adding master entry to Application Cache with manifest %s" :
         "Creating Application Cache with manifest %s";
-    frontend_->OnLogMessage(
+    frontend_->LogMessage(
         host_id_, blink::mojom::ConsoleMessageLevel::kInfo,
         base::StringPrintf(kFormatString,
                            group->manifest_url().spec().c_str()));
@@ -509,14 +511,14 @@
 
   if (associated_cache_info_pending_ && associated_cache_.get() &&
       associated_cache_->is_complete()) {
-    blink::mojom::AppCacheInfo info = CreateCacheInfo(
+    blink::mojom::AppCacheInfoPtr info = CreateCacheInfo(
         associated_cache_.get(), preferred_manifest_url_, GetStatus());
     associated_cache_info_pending_ = false;
     // In the network service world, we need to pass the URLLoaderFactory
     // instance to the renderer which it can use to request subresources.
     // This ensures that they can be served out of the AppCache.
     MaybePassSubresourceFactory();
-    frontend_->OnCacheSelected(host_id_, info);
+    frontend_->CacheSelected(host_id_, std::move(info));
   }
 }
 
@@ -578,7 +580,7 @@
   AppCacheSubresourceURLFactory::CreateURLLoaderFactory(GetWeakPtr(),
                                                         &factory_ptr);
 
-  frontend_->OnSetSubresourceFactory(host_id(), std::move(factory_ptr));
+  frontend_->SetSubresourceFactory(host_id(), std::move(factory_ptr));
 }
 
 void AppCacheHost::SetAppCacheSubresourceFactory(
@@ -615,7 +617,7 @@
   if (cache)
     cache->AssociateHost(this);
 
-  blink::mojom::AppCacheInfo info =
+  blink::mojom::AppCacheInfoPtr info =
       CreateCacheInfo(cache, manifest_url, GetStatus());
   // In the network service world, we need to pass the URLLoaderFactory
   // instance to the renderer which it can use to request subresources.
@@ -623,7 +625,7 @@
   if (cache && cache->is_complete())
     MaybePassSubresourceFactory();
 
-  frontend_->OnCacheSelected(host_id_, info);
+  frontend_->CacheSelected(host_id_, std::move(info));
 }
 
 }  // namespace content
diff --git a/content/browser/appcache/appcache_host.h b/content/browser/appcache/appcache_host.h
index 315ab55..41556ac 100644
--- a/content/browser/appcache/appcache_host.h
+++ b/content/browser/appcache/appcache_host.h
@@ -28,6 +28,12 @@
 #include "url/gurl.h"
 #include "url/origin.h"
 
+namespace blink {
+namespace mojom {
+class AppCacheFrontend;
+}  // namespace mojom
+}  // namespace blink
+
 namespace net {
 class URLRequest;
 }  // namespace net
@@ -40,7 +46,6 @@
 FORWARD_DECLARE_TEST(AppCacheHostTest, SetSwappableCache);
 FORWARD_DECLARE_TEST(AppCacheTest, CleanupUnusedCache);
 class AppCache;
-class AppCacheFrontend;
 class AppCacheGroupTest;
 class AppCacheRequest;
 class AppCacheRequestHandler;
@@ -77,7 +82,7 @@
 
   AppCacheHost(int host_id,
                int process_id,
-               AppCacheFrontend* frontend,
+               blink::mojom::AppCacheFrontend* frontend,
                AppCacheServiceImpl* service);
   ~AppCacheHost() override;
 
@@ -87,18 +92,16 @@
   void RemoveObserver(Observer* observer);
 
   // Support for cache selection and scriptable method calls. These methods
-  // return false if their preconditions have been broken.
-  // TODO(mek): WARN_UNUSED_RESULT to make sure we're actually checking the
-  // return values.
-  bool SelectCache(const GURL& document_url,
+  // call mojo::ReportBadMessage if their preconditions have been broken.
+  void SelectCache(const GURL& document_url,
                    const int64_t cache_document_was_loaded_from,
                    const GURL& manifest_url);
-  bool SelectCacheForSharedWorker(int64_t appcache_id);
-  bool MarkAsForeignEntry(const GURL& document_url,
+  void SelectCacheForSharedWorker(int64_t appcache_id);
+  void MarkAsForeignEntry(const GURL& document_url,
                           int64_t cache_document_was_loaded_from);
-  bool GetStatusWithCallback(GetStatusCallback callback);
-  bool StartUpdateWithCallback(StartUpdateCallback callback);
-  bool SwapCacheWithCallback(SwapCacheCallback callback);
+  void GetStatusWithCallback(GetStatusCallback callback);
+  void StartUpdateWithCallback(StartUpdateCallback callback);
+  void SwapCacheWithCallback(SwapCacheCallback callback);
 
   // Called prior to the main resource load. When the system contains multiple
   // candidates for a main resource load, the appcache preferred by the host
@@ -182,13 +185,15 @@
 
   AppCacheServiceImpl* service() const { return service_; }
   AppCacheStorage* storage() const { return storage_; }
-  AppCacheFrontend* frontend() const { return frontend_; }
+  blink::mojom::AppCacheFrontend* frontend() const { return frontend_; }
 
   // PlzNavigate:
   // The AppCacheHost instance is created with a dummy AppCacheFrontend
   // pointer when the navigation starts. We need to switch it to the
   // actual frontend when the navigation commits.
-  void set_frontend(AppCacheFrontend* frontend) { frontend_ = frontend; }
+  void set_frontend(blink::mojom::AppCacheFrontend* frontend) {
+    frontend_ = frontend;
+  }
 
   AppCache* associated_cache() const { return associated_cache_.get(); }
 
@@ -323,7 +328,7 @@
   GURL new_master_entry_url_;
 
   // The frontend proxy to deliver notifications to the child process.
-  AppCacheFrontend* frontend_;
+  blink::mojom::AppCacheFrontend* frontend_;
 
   // Our central service object.
   AppCacheServiceImpl* service_;
diff --git a/content/browser/appcache/appcache_host_unittest.cc b/content/browser/appcache/appcache_host_unittest.cc
index fc4b29a2..09d066f 100644
--- a/content/browser/appcache/appcache_host_unittest.cc
+++ b/content/browser/appcache/appcache_host_unittest.cc
@@ -7,16 +7,18 @@
 #include <stdint.h>
 
 #include <memory>
+#include <string>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/test/scoped_task_environment.h"
 #include "content/browser/appcache/appcache.h"
 #include "content/browser/appcache/appcache_backend_impl.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "content/browser/appcache/appcache_group.h"
 #include "content/browser/appcache/mock_appcache_policy.h"
 #include "content/browser/appcache/mock_appcache_service.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/test_support/test_utils.h"
 #include "net/url_request/url_request.h"
 #include "storage/browser/quota/quota_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -41,7 +43,7 @@
         &AppCacheHostTest::GetStatusCallback, base::Unretained(this));
   }
 
-  class MockFrontend : public AppCacheFrontend {
+  class MockFrontend : public blink::mojom::AppCacheFrontend {
    public:
     MockFrontend()
         : last_host_id_(-222),
@@ -53,49 +55,49 @@
               blink::mojom::AppCacheEventID::APPCACHE_OBSOLETE_EVENT),
           content_blocked_(false) {}
 
-    void OnCacheSelected(int host_id,
-                         const blink::mojom::AppCacheInfo& info) override {
+    void CacheSelected(int32_t host_id,
+                       blink::mojom::AppCacheInfoPtr info) override {
       last_host_id_ = host_id;
-      last_cache_id_ = info.cache_id;
-      last_status_ = info.status;
+      last_cache_id_ = info->cache_id;
+      last_status_ = info->status;
     }
 
-    void OnStatusChanged(const std::vector<int>& host_ids,
-                         blink::mojom::AppCacheStatus status) override {
+    void StatusChanged(const std::vector<int32_t>& host_ids,
+                       blink::mojom::AppCacheStatus status) override {
       last_status_changed_ = status;
     }
 
-    void OnEventRaised(const std::vector<int>& host_ids,
-                       blink::mojom::AppCacheEventID event_id) override {
+    void EventRaised(const std::vector<int32_t>& host_ids,
+                     blink::mojom::AppCacheEventID event_id) override {
       last_event_id_ = event_id;
     }
 
-    void OnErrorEventRaised(
-        const std::vector<int>& host_ids,
-        const blink::mojom::AppCacheErrorDetails& details) override {
+    void ErrorEventRaised(
+        const std::vector<int32_t>& host_ids,
+        blink::mojom::AppCacheErrorDetailsPtr details) override {
       last_event_id_ = blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT;
     }
 
-    void OnProgressEventRaised(const std::vector<int>& host_ids,
-                               const GURL& url,
-                               int num_total,
-                               int num_complete) override {
+    void ProgressEventRaised(const std::vector<int32_t>& host_ids,
+                             const GURL& url,
+                             int32_t num_total,
+                             int32_t num_complete) override {
       last_event_id_ = blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT;
     }
 
-    void OnLogMessage(int host_id,
-                      blink::mojom::ConsoleMessageLevel log_level,
-                      const std::string& message) override {}
+    void LogMessage(int32_t host_id,
+                    blink::mojom::ConsoleMessageLevel log_level,
+                    const std::string& message) override {}
 
-    void OnContentBlocked(int host_id, const GURL& manifest_url) override {
+    void ContentBlocked(int32_t host_id, const GURL& manifest_url) override {
       content_blocked_ = true;
     }
 
-    void OnSetSubresourceFactory(
-        int host_id,
+    void SetSubresourceFactory(
+        int32_t host_id,
         network::mojom::URLLoaderFactoryPtr url_loader_factory) override {}
 
-    int last_host_id_;
+    int32_t last_host_id_;
     int64_t last_cache_id_;
     blink::mojom::AppCacheStatus last_status_;
     blink::mojom::AppCacheStatus last_status_changed_;
@@ -550,20 +552,46 @@
 }
 
 TEST_F(AppCacheHostTest, SelectCacheTwice) {
-  AppCacheHost host(kHostIdForTest, kProcessIdForTest, &mock_frontend_,
-                    &service_);
   const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin());
 
-  EXPECT_TRUE(host.SelectCache(kDocAndOriginUrl,
-                               blink::mojom::kAppCacheNoCacheId, GURL()));
+  AppCacheBackendImpl backend(&service_, kProcessIdForTest);
+  backend.set_frontend_for_testing(&mock_frontend_);
+  backend.RegisterHostForTesting(kHostIdForTest);
+
+  blink::mojom::AppCacheBackendPtr backend_ptr;
+  mojo::Binding<blink::mojom::AppCacheBackend> backend_binding(
+      &backend, mojo::MakeRequest(&backend_ptr));
+
+  {
+    mojo::test::BadMessageObserver bad_message_observer;
+    backend_ptr->SelectCache(kHostIdForTest, kDocAndOriginUrl,
+                             blink::mojom::kAppCacheNoCacheId, GURL());
+
+    base::RunLoop().RunUntilIdle();
+    EXPECT_FALSE(bad_message_observer.got_bad_message());
+  }
 
   // Select methods should bail if cache has already been selected.
-  EXPECT_FALSE(host.SelectCache(kDocAndOriginUrl,
-                                blink::mojom::kAppCacheNoCacheId, GURL()));
-  EXPECT_FALSE(
-      host.SelectCacheForSharedWorker(blink::mojom::kAppCacheNoCacheId));
-  EXPECT_FALSE(host.MarkAsForeignEntry(kDocAndOriginUrl,
-                                       blink::mojom::kAppCacheNoCacheId));
+  {
+    mojo::test::BadMessageObserver bad_message_observer;
+    backend_ptr->SelectCache(kHostIdForTest, kDocAndOriginUrl,
+                             blink::mojom::kAppCacheNoCacheId, GURL());
+    EXPECT_EQ("ACH_SELECT_CACHE", bad_message_observer.WaitForBadMessage());
+  }
+  {
+    mojo::test::BadMessageObserver bad_message_observer;
+    backend_ptr->SelectCacheForSharedWorker(kHostIdForTest,
+                                            blink::mojom::kAppCacheNoCacheId);
+    EXPECT_EQ("ACH_SELECT_CACHE_FOR_SHARED_WORKER",
+              bad_message_observer.WaitForBadMessage());
+  }
+  {
+    mojo::test::BadMessageObserver bad_message_observer;
+    backend_ptr->MarkAsForeignEntry(kHostIdForTest, kDocAndOriginUrl,
+                                    blink::mojom::kAppCacheNoCacheId);
+    EXPECT_EQ("ACH_MARK_AS_FOREIGN_ENTRY",
+              bad_message_observer.WaitForBadMessage());
+  }
 }
 
 }  // namespace content
diff --git a/content/browser/appcache/appcache_internals_ui.cc b/content/browser/appcache/appcache_internals_ui.cc
index f03f5472..d63844fe 100644
--- a/content/browser/appcache/appcache_internals_ui.cc
+++ b/content/browser/appcache/appcache_internals_ui.cc
@@ -67,9 +67,9 @@
       new base::DictionaryValue());
   dict_value->SetString("manifestURL", response_enquiry.manifest_url);
   dict_value->SetString("groupId",
-                        base::Int64ToString(response_enquiry.group_id));
+                        base::NumberToString(response_enquiry.group_id));
   dict_value->SetString("responseId",
-                        base::Int64ToString(response_enquiry.response_id));
+                        base::NumberToString(response_enquiry.response_id));
   return dict_value;
 }
 
@@ -86,7 +86,8 @@
   dict_value->SetString(
       "size",
       base::UTF16ToUTF8(base::FormatBytesUnlocalized(appcache_info.size)));
-  dict_value->SetString("groupId", base::Int64ToString(appcache_info.group_id));
+  dict_value->SetString("groupId",
+                        base::NumberToString(appcache_info.group_id));
 
   return dict_value;
 }
@@ -120,7 +121,8 @@
   dict->SetString(
       "size",
       base::UTF16ToUTF8(base::FormatBytesUnlocalized(resource_info.size)));
-  dict->SetString("responseId", base::Int64ToString(resource_info.response_id));
+  dict->SetString("responseId",
+                  base::NumberToString(resource_info.response_id));
   dict->SetBoolean("isExplicit", resource_info.is_explicit);
   dict->SetBoolean("isManifest", resource_info.is_manifest);
   dict->SetBoolean("isMaster", resource_info.is_master);
diff --git a/content/browser/appcache/appcache_navigation_handle_core.cc b/content/browser/appcache/appcache_navigation_handle_core.cc
index d33107ae..8ff99a62 100644
--- a/content/browser/appcache/appcache_navigation_handle_core.cc
+++ b/content/browser/appcache/appcache_navigation_handle_core.cc
@@ -87,27 +87,27 @@
   precreated_host_->SetProcessId(process_id);
 }
 
-void AppCacheNavigationHandleCore::OnCacheSelected(
+void AppCacheNavigationHandleCore::CacheSelected(
     int host_id,
-    const blink::mojom::AppCacheInfo& info) {
+    blink::mojom::AppCacheInfoPtr info) {
   DCHECK(false);
 }
 
-void AppCacheNavigationHandleCore::OnStatusChanged(
+void AppCacheNavigationHandleCore::StatusChanged(
     const std::vector<int>& host_ids,
     blink::mojom::AppCacheStatus status) {
   // Should never be called.
   DCHECK(false);
 }
 
-void AppCacheNavigationHandleCore::OnEventRaised(
+void AppCacheNavigationHandleCore::EventRaised(
     const std::vector<int>& host_ids,
     blink::mojom::AppCacheEventID event_id) {
   // Should never be called.
   DCHECK(false);
 }
 
-void AppCacheNavigationHandleCore::OnProgressEventRaised(
+void AppCacheNavigationHandleCore::ProgressEventRaised(
     const std::vector<int>& host_ids,
     const GURL& url,
     int num_total,
@@ -116,14 +116,14 @@
   DCHECK(false);
 }
 
-void AppCacheNavigationHandleCore::OnErrorEventRaised(
+void AppCacheNavigationHandleCore::ErrorEventRaised(
     const std::vector<int>& host_ids,
-    const blink::mojom::AppCacheErrorDetails& details) {
+    blink::mojom::AppCacheErrorDetailsPtr details) {
   // Should never be called.
   DCHECK(false);
 }
 
-void AppCacheNavigationHandleCore::OnLogMessage(
+void AppCacheNavigationHandleCore::LogMessage(
     int host_id,
     blink::mojom::ConsoleMessageLevel log_level,
     const std::string& message) {
@@ -131,13 +131,13 @@
   DCHECK(false);
 }
 
-void AppCacheNavigationHandleCore::OnContentBlocked(int host_id,
-                                                    const GURL& manifest_url) {
+void AppCacheNavigationHandleCore::ContentBlocked(int host_id,
+                                                  const GURL& manifest_url) {
   // Should never be called.
   DCHECK(false);
 }
 
-void AppCacheNavigationHandleCore::OnSetSubresourceFactory(
+void AppCacheNavigationHandleCore::SetSubresourceFactory(
     int host_id,
     network::mojom::URLLoaderFactoryPtr url_loader_factory) {
   // Should never be called.
diff --git a/content/browser/appcache/appcache_navigation_handle_core.h b/content/browser/appcache/appcache_navigation_handle_core.h
index 6d06d06..aaf4523 100644
--- a/content/browser/appcache/appcache_navigation_handle_core.h
+++ b/content/browser/appcache/appcache_navigation_handle_core.h
@@ -6,11 +6,12 @@
 #define CONTENT_BROWSER_APPCACHE_APPCACHE_NAVIGATION_HANDLE_CORE_H_
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/weak_ptr.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
@@ -29,7 +30,7 @@
 // pendant of AppCacheNavigationHandle. See the
 // AppCacheNavigationHandle header for more details about the lifetime of
 // both classes.
-class AppCacheNavigationHandleCore : public AppCacheFrontend {
+class AppCacheNavigationHandleCore : public blink::mojom::AppCacheFrontend {
  public:
   AppCacheNavigationHandleCore(ChromeAppCacheService* appcache_service,
                                int appcache_host_id,
@@ -58,25 +59,24 @@
   // AppCacheFrontend methods
   // We don't expect calls on the AppCacheFrontend methods while the
   // AppCacheHost is not registered with the AppCacheBackend.
-  void OnCacheSelected(int host_id,
-                       const blink::mojom::AppCacheInfo& info) override;
-  void OnStatusChanged(const std::vector<int>& host_ids,
-                       blink::mojom::AppCacheStatus status) override;
-  void OnEventRaised(const std::vector<int>& host_ids,
-                     blink::mojom::AppCacheEventID event_id) override;
-  void OnProgressEventRaised(const std::vector<int>& host_ids,
-                             const GURL& url,
-                             int num_total,
-                             int num_complete) override;
-  void OnErrorEventRaised(
-      const std::vector<int>& host_ids,
-      const blink::mojom::AppCacheErrorDetails& details) override;
-  void OnLogMessage(int host_id,
-                    blink::mojom::ConsoleMessageLevel log_level,
-                    const std::string& message) override;
-  void OnContentBlocked(int host_id, const GURL& manifest_url) override;
-  void OnSetSubresourceFactory(
-      int host_id,
+  void CacheSelected(int32_t host_id,
+                     blink::mojom::AppCacheInfoPtr info) override;
+  void StatusChanged(const std::vector<int32_t>& host_ids,
+                     blink::mojom::AppCacheStatus status) override;
+  void EventRaised(const std::vector<int32_t>& host_ids,
+                   blink::mojom::AppCacheEventID event_id) override;
+  void ProgressEventRaised(const std::vector<int32_t>& host_ids,
+                           const GURL& url,
+                           int32_t num_total,
+                           int32_t num_complete) override;
+  void ErrorEventRaised(const std::vector<int32_t>& host_ids,
+                        blink::mojom::AppCacheErrorDetailsPtr details) override;
+  void LogMessage(int32_t host_id,
+                  blink::mojom::ConsoleMessageLevel log_level,
+                  const std::string& message) override;
+  void ContentBlocked(int32_t host_id, const GURL& manifest_url) override;
+  void SetSubresourceFactory(
+      int32_t host_id,
       network::mojom::URLLoaderFactoryPtr url_loader_factory) override;
 
  private:
diff --git a/content/browser/appcache/appcache_request_handler.cc b/content/browser/appcache/appcache_request_handler.cc
index b5c8cff..ac249855 100644
--- a/content/browser/appcache/appcache_request_handler.cc
+++ b/content/browser/appcache/appcache_request_handler.cc
@@ -400,7 +400,7 @@
       host_->NotifyMainResourceBlocked(manifest_url);
     } else {
       DCHECK_EQ(resource_type_, RESOURCE_TYPE_SHARED_WORKER);
-      host_->frontend()->OnContentBlocked(host_->host_id(), manifest_url);
+      host_->frontend()->ContentBlocked(host_->host_id(), manifest_url);
     }
     DeliverNetworkResponse();
     return;
diff --git a/content/browser/appcache/appcache_request_handler_unittest.cc b/content/browser/appcache/appcache_request_handler_unittest.cc
index 63489b1..4f4e1272 100644
--- a/content/browser/appcache/appcache_request_handler_unittest.cc
+++ b/content/browser/appcache/appcache_request_handler_unittest.cc
@@ -27,7 +27,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/appcache/appcache.h"
 #include "content/browser/appcache/appcache_backend_impl.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "content/browser/appcache/appcache_job.h"
 #include "content/browser/appcache/appcache_url_loader_job.h"
 #include "content/browser/appcache/appcache_url_loader_request.h"
@@ -72,34 +71,34 @@
 class AppCacheRequestHandlerTest
     : public testing::TestWithParam<RequestHandlerType> {
  public:
-  class MockFrontend : public AppCacheFrontend {
+  class MockFrontend : public blink::mojom::AppCacheFrontend {
    public:
-    void OnCacheSelected(int host_id,
-                         const blink::mojom::AppCacheInfo& info) override {}
+    void CacheSelected(int32_t host_id,
+                       blink::mojom::AppCacheInfoPtr info) override {}
 
-    void OnStatusChanged(const std::vector<int>& host_ids,
-                         blink::mojom::AppCacheStatus status) override {}
+    void StatusChanged(const std::vector<int32_t>& host_ids,
+                       blink::mojom::AppCacheStatus status) override {}
 
-    void OnEventRaised(const std::vector<int>& host_ids,
-                       blink::mojom::AppCacheEventID event_id) override {}
+    void EventRaised(const std::vector<int32_t>& host_ids,
+                     blink::mojom::AppCacheEventID event_id) override {}
 
-    void OnErrorEventRaised(
-        const std::vector<int>& host_ids,
-        const blink::mojom::AppCacheErrorDetails& details) override {}
+    void ErrorEventRaised(
+        const std::vector<int32_t>& host_ids,
+        blink::mojom::AppCacheErrorDetailsPtr details) override {}
 
-    void OnProgressEventRaised(const std::vector<int>& host_ids,
-                               const GURL& url,
-                               int num_total,
-                               int num_complete) override {}
+    void ProgressEventRaised(const std::vector<int32_t>& host_ids,
+                             const GURL& url,
+                             int32_t num_total,
+                             int32_t num_complete) override {}
 
-    void OnLogMessage(int host_id,
-                      blink::mojom::ConsoleMessageLevel log_level,
-                      const std::string& message) override {}
+    void LogMessage(int32_t host_id,
+                    blink::mojom::ConsoleMessageLevel log_level,
+                    const std::string& message) override {}
 
-    void OnContentBlocked(int host_id, const GURL& manifest_url) override {}
+    void ContentBlocked(int32_t host_id, const GURL& manifest_url) override {}
 
-    void OnSetSubresourceFactory(
-        int host_id,
+    void SetSubresourceFactory(
+        int32_t host_id,
         network::mojom::URLLoaderFactoryPtr url_loader_factory) override {}
   };
 
@@ -255,9 +254,9 @@
     mock_policy_.reset(new MockAppCachePolicy);
     mock_service_->set_appcache_policy(mock_policy_.get());
     mock_frontend_.reset(new MockFrontend);
-    backend_impl_.reset(new AppCacheBackendImpl);
-    backend_impl_->Initialize(mock_service_.get(), mock_frontend_.get(),
-                              kMockProcessId);
+    backend_impl_ = std::make_unique<AppCacheBackendImpl>(mock_service_.get(),
+                                                          kMockProcessId);
+    backend_impl_->set_frontend_for_testing(mock_frontend_.get());
     const int kHostId = 1;
     backend_impl_->RegisterHost(kHostId);
     host_ = backend_impl_->GetHost(kHostId);
diff --git a/content/browser/appcache/appcache_storage_impl_unittest.cc b/content/browser/appcache/appcache_storage_impl_unittest.cc
index f472a55..464addc 100644
--- a/content/browser/appcache/appcache_storage_impl_unittest.cc
+++ b/content/browser/appcache/appcache_storage_impl_unittest.cc
@@ -30,7 +30,6 @@
 #include "content/browser/appcache/appcache_backend_impl.h"
 #include "content/browser/appcache/appcache_database.h"
 #include "content/browser/appcache/appcache_entry.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "content/browser/appcache/appcache_group.h"
 #include "content/browser/appcache/appcache_host.h"
 #include "content/browser/appcache/appcache_interceptor.h"
@@ -1542,31 +1541,31 @@
     AppCacheStorageImplTest* test_;
   };
 
-  class MockAppCacheFrontend : public AppCacheFrontend {
+  class MockAppCacheFrontend : public blink::mojom::AppCacheFrontend {
    public:
     MockAppCacheFrontend() : error_event_was_raised_(false) {}
 
-    void OnCacheSelected(int host_id,
-                         const blink::mojom::AppCacheInfo& info) override {}
-    void OnStatusChanged(const std::vector<int>& host_ids,
-                         blink::mojom::AppCacheStatus status) override {}
-    void OnEventRaised(const std::vector<int>& host_ids,
-                       blink::mojom::AppCacheEventID event_id) override {}
-    void OnProgressEventRaised(const std::vector<int>& host_ids,
-                               const GURL& url,
-                               int num_total,
-                               int num_complete) override {}
-    void OnErrorEventRaised(
-        const std::vector<int>& host_ids,
-        const blink::mojom::AppCacheErrorDetails& details) override {
+    void CacheSelected(int32_t host_id,
+                       blink::mojom::AppCacheInfoPtr info) override {}
+    void StatusChanged(const std::vector<int32_t>& host_ids,
+                       blink::mojom::AppCacheStatus status) override {}
+    void EventRaised(const std::vector<int32_t>& host_ids,
+                     blink::mojom::AppCacheEventID event_id) override {}
+    void ProgressEventRaised(const std::vector<int32_t>& host_ids,
+                             const GURL& url,
+                             int32_t num_total,
+                             int32_t num_complete) override {}
+    void ErrorEventRaised(
+        const std::vector<int32_t>& host_ids,
+        blink::mojom::AppCacheErrorDetailsPtr details) override {
       error_event_was_raised_ = true;
     }
-    void OnLogMessage(int host_id,
-                      blink::mojom::ConsoleMessageLevel log_level,
-                      const std::string& message) override {}
-    void OnContentBlocked(int host_id, const GURL& manifest_url) override {}
-    void OnSetSubresourceFactory(
-        int host_id,
+    void LogMessage(int32_t host_id,
+                    blink::mojom::ConsoleMessageLevel log_level,
+                    const std::string& message) override {}
+    void ContentBlocked(int32_t host_id, const GURL& manifest_url) override {}
+    void SetSubresourceFactory(
+        int32_t host_id,
         network::mojom::URLLoaderFactoryPtr url_loader_factory) override {}
 
     bool error_event_was_raised_;
@@ -1682,8 +1681,9 @@
 
   void Continue_Reinitialize(ReinitTestCase test_case) {
     const int kMockProcessId = 1;
-    backend_.reset(new AppCacheBackendImpl);
-    backend_->Initialize(service_.get(), &frontend_, kMockProcessId);
+    backend_ =
+        std::make_unique<AppCacheBackendImpl>(service_.get(), kMockProcessId);
+    backend_->set_frontend_for_testing(&frontend_);
 
     if (test_case == CORRUPT_SQL_ON_INSTALL) {
       // Break the db file
diff --git a/content/browser/appcache/appcache_unittest.cc b/content/browser/appcache/appcache_unittest.cc
index 8292beb..069048da7 100644
--- a/content/browser/appcache/appcache_unittest.cc
+++ b/content/browser/appcache/appcache_unittest.cc
@@ -9,7 +9,6 @@
 
 #include "base/test/scoped_task_environment.h"
 #include "content/browser/appcache/appcache.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "content/browser/appcache/appcache_host.h"
 #include "content/browser/appcache/mock_appcache_service.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,27 +20,27 @@
 
 namespace {
 
-class MockAppCacheFrontend : public AppCacheFrontend {
+class MockAppCacheFrontend : public blink::mojom::AppCacheFrontend {
  public:
-  void OnCacheSelected(int host_id,
-                       const blink::mojom::AppCacheInfo& info) override {}
-  void OnStatusChanged(const std::vector<int>& host_ids,
-                       blink::mojom::AppCacheStatus status) override {}
-  void OnEventRaised(const std::vector<int>& host_ids,
-                     blink::mojom::AppCacheEventID event_id) override {}
-  void OnProgressEventRaised(const std::vector<int>& host_ids,
-                             const GURL& url,
-                             int num_total,
-                             int num_complete) override {}
-  void OnErrorEventRaised(
-      const std::vector<int>& host_ids,
-      const blink::mojom::AppCacheErrorDetails& details) override {}
-  void OnLogMessage(int host_id,
-                    blink::mojom::ConsoleMessageLevel log_level,
-                    const std::string& message) override {}
-  void OnContentBlocked(int host_id, const GURL& manifest_url) override {}
-  void OnSetSubresourceFactory(
-      int host_id,
+  void CacheSelected(int32_t host_id,
+                     blink::mojom::AppCacheInfoPtr info) override {}
+  void StatusChanged(const std::vector<int32_t>& host_ids,
+                     blink::mojom::AppCacheStatus status) override {}
+  void EventRaised(const std::vector<int32_t>& host_ids,
+                   blink::mojom::AppCacheEventID event_id) override {}
+  void ProgressEventRaised(const std::vector<int32_t>& host_ids,
+                           const GURL& url,
+                           int32_t num_total,
+                           int32_t num_complete) override {}
+  void ErrorEventRaised(
+      const std::vector<int32_t>& host_ids,
+      blink::mojom::AppCacheErrorDetailsPtr details) override {}
+  void LogMessage(int32_t host_id,
+                  blink::mojom::ConsoleMessageLevel log_level,
+                  const std::string& message) override {}
+  void ContentBlocked(int32_t host_id, const GURL& manifest_url) override {}
+  void SetSubresourceFactory(
+      int32_t host_id,
       network::mojom::URLLoaderFactoryPtr url_loader_factory) override {}
 };
 
diff --git a/content/browser/appcache/appcache_update_job.cc b/content/browser/appcache/appcache_update_job.cc
index d85ad67c..2e76caa 100644
--- a/content/browser/appcache/appcache_update_job.cc
+++ b/content/browser/appcache/appcache_update_job.cc
@@ -12,7 +12,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "content/browser/appcache/appcache_group.h"
 #include "content/browser/appcache/appcache_histograms.h"
 #include "content/browser/appcache/appcache_update_url_fetcher.h"
@@ -101,6 +100,7 @@
 // so that only one notification is sent for all hosts using the same frontend.
 class HostNotifier {
  public:
+  using AppCacheFrontend = blink::mojom::AppCacheFrontend;
   using HostIds = std::vector<int>;
   using NotifyHostMap = std::map<AppCacheFrontend*, HostIds>;
 
@@ -119,7 +119,7 @@
   void SendNotifications(blink::mojom::AppCacheEventID event_id) {
     for (auto& pair : hosts_to_notify_) {
       AppCacheFrontend* frontend = pair.first;
-      frontend->OnEventRaised(pair.second, event_id);
+      frontend->EventRaised(pair.second, event_id);
     }
   }
 
@@ -128,8 +128,7 @@
                                  int num_complete) {
     for (const auto& pair : hosts_to_notify_) {
       AppCacheFrontend* frontend = pair.first;
-      frontend->OnProgressEventRaised(pair.second, url, num_total,
-                                      num_complete);
+      frontend->ProgressEventRaised(pair.second, url, num_total, num_complete);
     }
   }
 
@@ -138,7 +137,7 @@
     DCHECK(!details.message.empty());
     for (const auto& pair : hosts_to_notify_) {
       AppCacheFrontend* frontend = pair.first;
-      frontend->OnErrorEventRaised(pair.second, details);
+      frontend->ErrorEventRaised(pair.second, details.Clone());
     }
   }
 
@@ -146,8 +145,8 @@
     for (const auto& pair : hosts_to_notify_) {
       AppCacheFrontend* frontend = pair.first;
       for (const auto& id : pair.second)
-        frontend->OnLogMessage(id, blink::mojom::ConsoleMessageLevel::kWarning,
-                               message);
+        frontend->LogMessage(id, blink::mojom::ConsoleMessageLevel::kWarning,
+                             message);
     }
   }
 
@@ -830,7 +829,7 @@
     AppCacheHost* host,
     blink::mojom::AppCacheEventID event_id) {
   std::vector<int> ids(1, host->host_id());
-  host->frontend()->OnEventRaised(ids, event_id);
+  host->frontend()->EventRaised(ids, event_id);
 }
 
 void AppCacheUpdateJob::NotifyAllAssociatedHosts(
diff --git a/content/browser/appcache/appcache_update_job_unittest.cc b/content/browser/appcache/appcache_update_job_unittest.cc
index f48f609..33fa7af 100644
--- a/content/browser/appcache/appcache_update_job_unittest.cc
+++ b/content/browser/appcache/appcache_update_job_unittest.cc
@@ -22,7 +22,6 @@
 #include "base/task/post_task.h"
 #include "base/test/scoped_task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "content/browser/appcache/appcache_group.h"
 #include "content/browser/appcache/appcache_host.h"
 #include "content/browser/appcache/appcache_response.h"
@@ -244,7 +243,7 @@
          lhs.target_url == rhs.target_url;
 }
 
-class MockFrontend : public AppCacheFrontend {
+class MockFrontend : public blink::mojom::AppCacheFrontend {
  public:
   MockFrontend()
       : ignore_progress_events_(false),
@@ -255,14 +254,14 @@
             blink::mojom::AppCacheEventID::APPCACHE_CHECKING_EVENT),
         update_(nullptr) {}
 
-  void OnCacheSelected(int host_id,
-                       const blink::mojom::AppCacheInfo& info) override {}
+  void CacheSelected(int32_t host_id,
+                     blink::mojom::AppCacheInfoPtr info) override {}
 
-  void OnStatusChanged(const std::vector<int>& host_ids,
-                       blink::mojom::AppCacheStatus status) override {}
+  void StatusChanged(const std::vector<int32_t>& host_ids,
+                     blink::mojom::AppCacheStatus status) override {}
 
-  void OnEventRaised(const std::vector<int>& host_ids,
-                     blink::mojom::AppCacheEventID event_id) override {
+  void EventRaised(const std::vector<int32_t>& host_ids,
+                   blink::mojom::AppCacheEventID event_id) override {
     raised_events_.push_back(RaisedEvent(host_ids, event_id));
 
     // Trigger additional updates if requested.
@@ -275,21 +274,20 @@
     }
   }
 
-  void OnErrorEventRaised(
-      const std::vector<int>& host_ids,
-      const blink::mojom::AppCacheErrorDetails& details) override {
-    error_message_ = details.message;
-    OnEventRaised(host_ids,
-                  blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
+  void ErrorEventRaised(
+      const std::vector<int32_t>& host_ids,
+      blink::mojom::AppCacheErrorDetailsPtr details) override {
+    error_message_ = details->message;
+    EventRaised(host_ids, blink::mojom::AppCacheEventID::APPCACHE_ERROR_EVENT);
   }
 
-  void OnProgressEventRaised(const std::vector<int>& host_ids,
-                             const GURL& url,
-                             int num_total,
-                             int num_complete) override {
+  void ProgressEventRaised(const std::vector<int32_t>& host_ids,
+                           const GURL& url,
+                           int32_t num_total,
+                           int32_t num_complete) override {
     if (!ignore_progress_events_)
-      OnEventRaised(host_ids,
-                    blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
+      EventRaised(host_ids,
+                  blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
 
     if (verify_progress_events_) {
       EXPECT_GE(num_total, num_complete);
@@ -315,17 +313,17 @@
     }
   }
 
-  void OnLogMessage(int host_id,
-                    blink::mojom::ConsoleMessageLevel log_level,
-                    const std::string& message) override {}
+  void LogMessage(int32_t host_id,
+                  blink::mojom::ConsoleMessageLevel log_level,
+                  const std::string& message) override {}
 
-  void OnContentBlocked(int host_id, const GURL& manifest_url) override {}
+  void ContentBlocked(int32_t host_id, const GURL& manifest_url) override {}
 
-  void OnSetSubresourceFactory(
-      int host_id,
+  void SetSubresourceFactory(
+      int32_t host_id,
       network::mojom::URLLoaderFactoryPtr url_loader_factory) override {}
 
-  void AddExpectedEvent(const std::vector<int>& host_ids,
+  void AddExpectedEvent(const std::vector<int32_t>& host_ids,
                         blink::mojom::AppCacheEventID event_id) {
     DCHECK(!ignore_progress_events_ ||
            event_id != blink::mojom::AppCacheEventID::APPCACHE_PROGRESS_EVENT);
@@ -356,7 +354,7 @@
     update_hosts_.push_back(host);
   }
 
-  using HostIds = std::vector<int>;
+  using HostIds = std::vector<int32_t>;
   using RaisedEvent = std::pair<HostIds, blink::mojom::AppCacheEventID>;
   using RaisedEvents = std::vector<RaisedEvent>;
   RaisedEvents raised_events_;
@@ -3591,7 +3589,8 @@
     return cache;
   }
 
-  AppCacheHost* MakeHost(int host_id, AppCacheFrontend* frontend) {
+  AppCacheHost* MakeHost(int host_id,
+                         blink::mojom::AppCacheFrontend* frontend) {
     constexpr int kProcessIdForTests = 123;
     hosts_.push_back(std::make_unique<AppCacheHost>(host_id, kProcessIdForTests,
                                                     frontend, service_.get()));
diff --git a/content/browser/appcache/appcache_url_request_job.cc b/content/browser/appcache/appcache_url_request_job.cc
index 17e635c2..158ef9e 100644
--- a/content/browser/appcache/appcache_url_request_job.cc
+++ b/content/browser/appcache/appcache_url_request_job.cc
@@ -16,7 +16,6 @@
 #include "base/strings/stringprintf.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/browser/appcache/appcache.h"
-#include "content/browser/appcache/appcache_frontend.h"
 #include "content/browser/appcache/appcache_group.h"
 #include "content/browser/appcache/appcache_histograms.h"
 #include "content/browser/appcache/appcache_host.h"
@@ -171,7 +170,7 @@
 
 void AppCacheURLRequestJob::BeginErrorDelivery(const char* message) {
   if (host_)
-    host_->frontend()->OnLogMessage(
+    host_->frontend()->LogMessage(
         host_->host_id(), blink::mojom::ConsoleMessageLevel::kError, message);
   delivery_type_ = DeliveryType::kError;
   storage_ = nullptr;
diff --git a/content/browser/appcache/chrome_appcache_service.cc b/content/browser/appcache/chrome_appcache_service.cc
index 225f0a1..790e72d 100644
--- a/content/browser/appcache/chrome_appcache_service.cc
+++ b/content/browser/appcache/chrome_appcache_service.cc
@@ -44,6 +44,21 @@
   set_special_storage_policy(special_storage_policy.get());
 }
 
+void ChromeAppCacheService::CreateBackend(
+    int process_id,
+    blink::mojom::AppCacheBackendRequest request) {
+  // The process_id is the id of the RenderProcessHost, which can be reused for
+  // a new renderer process if the previous renderer process was shutdown.
+  // It can take some time after shutdown for the pipe error to propagate
+  // and unregister the previous backend. Since the AppCacheService assumes
+  // that there is one backend per process_id, we need to ensure that the
+  // previous backend is unregistered by eagerly unbinding the pipe.
+  Unbind(process_id);
+
+  Bind(std::make_unique<AppCacheBackendImpl>(this, process_id),
+       std::move(request), process_id);
+}
+
 void ChromeAppCacheService::Bind(
     std::unique_ptr<blink::mojom::AppCacheBackend> backend,
     blink::mojom::AppCacheBackendRequest request,
diff --git a/content/browser/appcache/chrome_appcache_service.h b/content/browser/appcache/chrome_appcache_service.h
index e4f1c19..7d0ecea 100644
--- a/content/browser/appcache/chrome_appcache_service.h
+++ b/content/browser/appcache/chrome_appcache_service.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_APPCACHE_CHROME_APPCACHE_SERVICE_H_
 #define CONTENT_BROWSER_APPCACHE_CHROME_APPCACHE_SERVICE_H_
 
+#include <memory>
+
 #include "base/compiler_specific.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -56,15 +58,8 @@
       scoped_refptr<net::URLRequestContextGetter> request_context_getter,
       scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy);
 
-  void Bind(std::unique_ptr<blink::mojom::AppCacheBackend> backend,
-            blink::mojom::AppCacheBackendRequest request,
-            int process_id);
-  // Unbinds the pipe corresponding to the given process_id. Unbinding
-  // unregisters and destroys the existing backend for that process_id.
-  // The function must be called before a new backend is created for the given
-  // process_id to ensure that there is at most one backend per process_id.
-  // The function does nothing if no pipe was bound.
-  void Unbind(int process_id);
+  void CreateBackend(int process_id,
+                     blink::mojom::AppCacheBackendRequest request);
 
   void Shutdown();
 
@@ -86,6 +81,16 @@
                                           ChromeAppCacheServiceDeleter>;
   friend struct ChromeAppCacheServiceDeleter;
 
+  void Bind(std::unique_ptr<blink::mojom::AppCacheBackend> backend,
+            blink::mojom::AppCacheBackendRequest request,
+            int process_id);
+  // Unbinds the pipe corresponding to the given process_id. Unbinding
+  // unregisters and destroys the existing backend for that process_id.
+  // The function must be called before a new backend is created for the given
+  // process_id to ensure that there is at most one backend per process_id.
+  // The function does nothing if no pipe was bound.
+  void Unbind(int process_id);
+
   void DeleteOnCorrectThread() const;
 
   ResourceContext* resource_context_;
diff --git a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
index 2b15ead..ace376a 100644
--- a/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager_unittest.cc
@@ -973,7 +973,7 @@
   for (int i = 0; i < 2; i++) {
     // First Service Worker.
     BackgroundFetchRegistrationId registration_id1(
-        swid1, origin(), kExampleDeveloperId + base::IntToString(i),
+        swid1, origin(), kExampleDeveloperId + base::NumberToString(i),
         base::GenerateGUID());
     CreateRegistration(
         registration_id1, std::vector<blink::mojom::FetchAPIRequestPtr>(),
@@ -982,7 +982,7 @@
 
     // Second service Worker.
     BackgroundFetchRegistrationId registration_id2(
-        swid2, origin(), kExampleDeveloperId + base::IntToString(i),
+        swid2, origin(), kExampleDeveloperId + base::NumberToString(i),
         base::GenerateGUID());
     CreateRegistration(
         registration_id2, std::vector<blink::mojom::FetchAPIRequestPtr>(),
diff --git a/content/browser/background_fetch/background_fetch_test_base.cc b/content/browser/background_fetch/background_fetch_test_base.cc
index 301e3bb..7bc6a7e 100644
--- a/content/browser/background_fetch/background_fetch_test_base.cc
+++ b/content/browser/background_fetch/background_fetch_test_base.cc
@@ -68,7 +68,7 @@
 }
 
 GURL GetScopeForId(const std::string& origin, int64_t id) {
-  return GURL(origin + base::IntToString(id));
+  return GURL(origin + base::NumberToString(id));
 }
 
 }  // namespace
diff --git a/content/browser/bad_message.cc b/content/browser/bad_message.cc
index e0a02d1..80136c4 100644
--- a/content/browser/bad_message.cc
+++ b/content/browser/bad_message.cc
@@ -26,7 +26,8 @@
 
   LOG(ERROR) << "Terminating renderer for bad IPC message, reason " << reason;
   base::UmaHistogramSparse("Stability.BadMessageTerminated.Content", reason);
-  base::debug::SetCrashKeyString(bad_message_reason, base::IntToString(reason));
+  base::debug::SetCrashKeyString(bad_message_reason,
+                                 base::NumberToString(reason));
 }
 
 void ReceivedBadMessageOnUIThread(int render_process_id,
diff --git a/content/browser/browsing_instance.cc b/content/browser/browsing_instance.cc
index 8aca938..1404997 100644
--- a/content/browser/browsing_instance.cc
+++ b/content/browser/browsing_instance.cc
@@ -23,10 +23,25 @@
     : browser_context_(browser_context),
       isolation_context_(
           BrowsingInstanceId::FromUnsafeValue(next_browsing_instance_id_++)),
-      active_contents_count_(0u) {
+      active_contents_count_(0u),
+      default_process_(nullptr) {
   DCHECK(browser_context);
 }
 
+void BrowsingInstance::RenderProcessHostDestroyed(RenderProcessHost* host) {
+  DCHECK_EQ(default_process_, host);
+  // Only clear the default process if the RenderProcessHost object goes away,
+  // not if the renderer process goes away while the RenderProcessHost remains.
+  default_process_->RemoveObserver(this);
+  default_process_ = nullptr;
+}
+
+void BrowsingInstance::SetDefaultProcess(RenderProcessHost* default_process) {
+  DCHECK(!default_process_);
+  default_process_ = default_process;
+  default_process_->AddObserver(this);
+}
+
 bool BrowsingInstance::HasSiteInstance(const GURL& url) {
   std::string site =
       SiteInstanceImpl::GetSiteForURL(browser_context_, isolation_context_, url)
@@ -96,6 +111,8 @@
   // us are gone.
   DCHECK(site_instance_map_.empty());
   DCHECK_EQ(0u, active_contents_count_);
+  if (default_process_)
+    default_process_->RemoveObserver(this);
 }
 
 }  // namespace content
diff --git a/content/browser/browsing_instance.h b/content/browser/browsing_instance.h
index d439dfbb..474039c 100644
--- a/content/browser/browsing_instance.h
+++ b/content/browser/browsing_instance.h
@@ -18,10 +18,12 @@
 #include "content/browser/isolation_context.h"
 #include "content/common/content_export.h"
 #include "content/public/browser/browser_context.h"
+#include "content/public/browser/render_process_host_observer.h"
 
 class GURL;
 
 namespace content {
+class RenderProcessHost;
 class SiteInstanceImpl;
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -63,7 +65,8 @@
 //
 ///////////////////////////////////////////////////////////////////////////////
 class CONTENT_EXPORT BrowsingInstance final
-    : public base::RefCounted<BrowsingInstance> {
+    : public base::RefCounted<BrowsingInstance>,
+      public RenderProcessHostObserver {
  private:
   friend class base::RefCounted<BrowsingInstance>;
   friend class SiteInstanceImpl;
@@ -81,7 +84,10 @@
   // Create a new BrowsingInstance.
   explicit BrowsingInstance(BrowserContext* context);
 
-  ~BrowsingInstance();
+  ~BrowsingInstance() final;
+
+  // RenderProcessHostObserver implementation.
+  void RenderProcessHostDestroyed(RenderProcessHost* host) final;
 
   // Get the browser context to which this BrowsingInstance belongs.
   BrowserContext* browser_context() const { return browser_context_; }
@@ -118,6 +124,11 @@
     active_contents_count_--;
   }
 
+  // Stores the process that should be used if a SiteInstance doesn't need
+  // a dedicated process.
+  void SetDefaultProcess(RenderProcessHost* default_process);
+  RenderProcessHost* default_process() const { return default_process_; }
+
   // Map of site to SiteInstance, to ensure we only have one SiteInstance per
   // site.
   typedef std::unordered_map<std::string, SiteInstanceImpl*> SiteInstanceMap;
@@ -145,6 +156,10 @@
   // Number of WebContentses currently using this BrowsingInstance.
   size_t active_contents_count_;
 
+  // The process to use for any SiteInstance in this BrowsingInstance that
+  // doesn't require a dedicated process.
+  RenderProcessHost* default_process_;
+
   DISALLOW_COPY_AND_ASSIGN(BrowsingInstance);
 };
 
diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc
index d2351328..7347cd4e 100644
--- a/content/browser/child_process_security_policy_impl.cc
+++ b/content/browser/child_process_security_policy_impl.cc
@@ -1122,7 +1122,9 @@
   // If |filesystem_url.origin()| is not committable in this process, then this
   // page should not be able to place content in that origin via the filesystem
   // API either.
-  if (!CanCommitURL(child_id, filesystem_url.origin())) {
+  // TODO(lukasza): Audit whether CanAccessDataForOrigin can be used directly
+  // here.
+  if (!CanCommitURL(child_id, filesystem_url.origin().GetURL())) {
     UMA_HISTOGRAM_BOOLEAN("FileSystem.OriginFailedCanCommitURL", true);
     return false;
   }
diff --git a/content/browser/child_process_security_policy_unittest.cc b/content/browser/child_process_security_policy_unittest.cc
index 9d2a65b..99c96260 100644
--- a/content/browser/child_process_security_policy_unittest.cc
+++ b/content/browser/child_process_security_policy_unittest.cc
@@ -720,7 +720,8 @@
   base::FilePath file(TEST_PATH("/dir/testfile"));
   file = file.NormalizePathSeparators();
   storage::FileSystemURL url = storage::FileSystemURL::CreateForTest(
-      GURL("http://foo/"), storage::kFileSystemTypeTest, file);
+      url::Origin::Create(GURL("http://foo/")), storage::kFileSystemTypeTest,
+      file);
 
   // Test initially having no permissions.
   CheckHasNoFileSystemFilePermission(p, file, url);
diff --git a/content/browser/cookie_store/cookie_store_manager_unittest.cc b/content/browser/cookie_store/cookie_store_manager_unittest.cc
index feea1705..587cda44 100644
--- a/content/browser/cookie_store/cookie_store_manager_unittest.cc
+++ b/content/browser/cookie_store/cookie_store_manager_unittest.cc
@@ -118,7 +118,7 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
diff --git a/content/browser/database_browsertest.cc b/content/browser/database_browsertest.cc
index 3516029..688dc19 100644
--- a/content/browser/database_browsertest.cc
+++ b/content/browser/database_browsertest.cc
@@ -45,13 +45,13 @@
   void UpdateRecord(Shell* shell, int index, const std::string& data) {
     RunScriptAndCheckResult(
         shell,
-        "updateRecord(" + base::IntToString(index) + ", '" + data + "')",
+        "updateRecord(" + base::NumberToString(index) + ", '" + data + "')",
         "done");
   }
 
   void DeleteRecord(Shell* shell, int index) {
     RunScriptAndCheckResult(
-        shell, "deleteRecord(" + base::IntToString(index) + ")", "done");
+        shell, "deleteRecord(" + base::NumberToString(index) + ")", "done");
   }
 
   void CompareRecords(Shell* shell, const std::string& expected) {
@@ -123,7 +123,7 @@
 
   std::string expected;
   for (int i = 0; i < 10; ++i) {
-    std::string item = base::IntToString(i);
+    std::string item = base::NumberToString(i);
     InsertRecord(shell(), item);
     if (!expected.empty())
       expected += ", ";
@@ -133,7 +133,7 @@
 
   expected.clear();
   for (int i = 0; i < 10; ++i) {
-    std::string item = base::IntToString(i * i);
+    std::string item = base::NumberToString(i * i);
     UpdateRecord(shell(), i, item);
     if (!expected.empty())
       expected += ", ";
diff --git a/content/browser/devtools/devtools_io_context.cc b/content/browser/devtools/devtools_io_context.cc
index f7c4a2b..c45fdfc7 100644
--- a/content/browser/devtools/devtools_io_context.cc
+++ b/content/browser/devtools/devtools_io_context.cc
@@ -21,7 +21,7 @@
 
 std::string DevToolsIOContext::Stream::Register(DevToolsIOContext* context) {
   static unsigned s_last_stream_handle = 0;
-  const std::string handle = base::UintToString(++s_last_stream_handle);
+  const std::string handle = base::NumberToString(++s_last_stream_handle);
   Register(context, handle);
   return handle;
 }
diff --git a/content/browser/devtools/protocol/emulation_handler.cc b/content/browser/devtools/protocol/emulation_handler.cc
index 8df358c..18b6e54 100644
--- a/content/browser/devtools/protocol/emulation_handler.cc
+++ b/content/browser/devtools/protocol/emulation_handler.cc
@@ -183,7 +183,7 @@
       screen_height.fromMaybe(0) > max_size) {
     return Response::InvalidParams(
         "Screen width and height values must be positive, not greater than " +
-        base::IntToString(max_size));
+        base::NumberToString(max_size));
   }
 
   if (position_x.fromMaybe(0) < 0 || position_y.fromMaybe(0) < 0 ||
@@ -195,7 +195,7 @@
   if (width < 0 || height < 0 || width > max_size || height > max_size) {
     return Response::InvalidParams(
         "Width and height values must be positive, not greater than " +
-        base::IntToString(max_size));
+        base::NumberToString(max_size));
   }
 
   if (device_scale_factor < 0)
@@ -219,7 +219,7 @@
     if (orientationAngle < 0 || orientationAngle >= max_orientation_angle) {
       return Response::InvalidParams(
           "Screen orientation angle must be non-negative, less than " +
-          base::IntToString(max_orientation_angle));
+          base::NumberToString(max_orientation_angle));
     }
   }
 
diff --git a/content/browser/devtools/protocol/memory_handler.cc b/content/browser/devtools/protocol/memory_handler.cc
index 2eb1f78..5b02174 100644
--- a/content/browser/devtools/protocol/memory_handler.cc
+++ b/content/browser/devtools/protocol/memory_handler.cc
@@ -14,7 +14,6 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/common/bind_interface_helpers.h"
 #include "content/public/common/child_process_host.h"
-#include "content/public/common/content_features.h"
 
 namespace content {
 namespace protocol {
@@ -79,12 +78,6 @@
 
 Response MemoryHandler::SetPressureNotificationsSuppressed(
     bool suppressed) {
-  if (base::FeatureList::IsEnabled(features::kMemoryCoordinator)) {
-    return Response::Error(
-        "Cannot enable/disable notifications when memory coordinator is "
-        "enabled");
-  }
-
   base::MemoryPressureListener::SetNotificationsSuppressed(suppressed);
   return Response::OK();
 }
diff --git a/content/browser/devtools/protocol/service_worker_handler.cc b/content/browser/devtools/protocol/service_worker_handler.cc
index 19b4801..3dc663a 100644
--- a/content/browser/devtools/protocol/service_worker_handler.cc
+++ b/content/browser/devtools/protocol/service_worker_handler.cc
@@ -364,7 +364,7 @@
   for (const auto& registration : registrations) {
     result->addItem(Registration::Create()
                         .SetRegistrationId(
-                            base::Int64ToString(registration.registration_id))
+                            base::NumberToString(registration.registration_id))
                         .SetScopeURL(registration.scope.spec())
                         .SetIsDeleted(registration.delete_flag ==
                                       ServiceWorkerRegistrationInfo::IS_DELETED)
@@ -404,20 +404,18 @@
     for (auto& c : client_set)
       clients->addItem(c);
 
-    std::unique_ptr<Version> version_value = Version::Create()
-        .SetVersionId(base::Int64ToString(version.version_id))
-        .SetRegistrationId(
-            base::Int64ToString(version.registration_id))
-        .SetScriptURL(version.script_url.spec())
-        .SetRunningStatus(
-            GetVersionRunningStatusString(version.running_status))
-        .SetStatus(GetVersionStatusString(version.status))
-        .SetScriptLastModified(
-            version.script_last_modified.ToDoubleT())
-        .SetScriptResponseTime(
-            version.script_response_time.ToDoubleT())
-        .SetControlledClients(std::move(clients))
-        .Build();
+    std::unique_ptr<Version> version_value =
+        Version::Create()
+            .SetVersionId(base::NumberToString(version.version_id))
+            .SetRegistrationId(base::NumberToString(version.registration_id))
+            .SetScriptURL(version.script_url.spec())
+            .SetRunningStatus(
+                GetVersionRunningStatusString(version.running_status))
+            .SetStatus(GetVersionStatusString(version.status))
+            .SetScriptLastModified(version.script_last_modified.ToDoubleT())
+            .SetScriptResponseTime(version.script_response_time.ToDoubleT())
+            .SetControlledClients(std::move(clients))
+            .Build();
     scoped_refptr<DevToolsAgentHostImpl> host(
         ServiceWorkerDevToolsManager::GetInstance()
             ->GetDevToolsAgentHostForWorker(
@@ -437,8 +435,8 @@
   frontend_->WorkerErrorReported(
       ServiceWorker::ServiceWorkerErrorMessage::Create()
           .SetErrorMessage(base::UTF16ToUTF8(info.error_message))
-          .SetRegistrationId(base::Int64ToString(registration_id))
-          .SetVersionId(base::Int64ToString(version_id))
+          .SetRegistrationId(base::NumberToString(registration_id))
+          .SetVersionId(base::NumberToString(version_id))
           .SetSourceURL(info.source_url.spec())
           .SetLineNumber(info.line_number)
           .SetColumnNumber(info.column_number)
diff --git a/content/browser/devtools/protocol_string.h b/content/browser/devtools/protocol_string.h
index 23144ca..83cf975 100644
--- a/content/browser/devtools/protocol_string.h
+++ b/content/browser/devtools/protocol_string.h
@@ -45,9 +45,7 @@
   static String substring(const String& s, unsigned pos, unsigned len) {
     return s.substr(pos, len);
   }
-  static String fromInteger(int number) {
-    return base::IntToString(number);
-  }
+  static String fromInteger(int number) { return base::NumberToString(number); }
   static String fromDouble(double number) {
     String s = base::NumberToString(number);
     if (!s.empty() && s[0] == '.')
diff --git a/content/browser/dom_storage/local_storage_context_mojo.cc b/content/browser/dom_storage/local_storage_context_mojo.cc
index 4915e0ac..5067dec 100644
--- a/content/browser/dom_storage/local_storage_context_mojo.cc
+++ b/content/browser/dom_storage/local_storage_context_mojo.cc
@@ -230,7 +230,7 @@
       item->type = leveldb::mojom::BatchOperationType::PUT_KEY;
       item->key = leveldb::StdStringToUint8Vector(kVersionKey);
       item->value = leveldb::StdStringToUint8Vector(
-          base::Int64ToString(kCurrentLocalStorageSchemaVersion));
+          base::NumberToString(kCurrentLocalStorageSchemaVersion));
       operations.push_back(std::move(item));
       context_->database_initialized_ = true;
     }
diff --git a/content/browser/dom_storage/session_storage_database.cc b/content/browser/dom_storage/session_storage_database.cc
index 4c660ab..69c678a9 100644
--- a/content/browser/dom_storage/session_storage_database.cc
+++ b/content/browser/dom_storage/session_storage_database.cc
@@ -680,7 +680,7 @@
     if (!ConsistencyCheck(conversion_ok))
       return false;
   }
-  batch->Put(next_map_id_key, base::Int64ToString(++next_map_id));
+  batch->Put(next_map_id_key, base::NumberToString(++next_map_id));
   std::string namespace_key =
       NamespaceKey(namespace_id, origin.GetURL().spec());
   batch->Put(namespace_key, *map_id);
@@ -762,7 +762,7 @@
   int64_t old_ref_count;
   if (!GetMapRefCount(map_id, &old_ref_count))
     return false;
-  batch->Put(MapRefCountKey(map_id), base::Int64ToString(++old_ref_count));
+  batch->Put(MapRefCountKey(map_id), base::NumberToString(++old_ref_count));
   return true;
 }
 
@@ -777,7 +777,7 @@
     return false;
   ref_count -= decrease;
   if (ref_count > 0) {
-    batch->Put(MapRefCountKey(map_id), base::Int64ToString(ref_count));
+    batch->Put(MapRefCountKey(map_id), base::NumberToString(ref_count));
   } else {
     // Clear all keys in the map.
     if (!ClearMap(map_id, batch))
diff --git a/content/browser/dom_storage/storage_area_impl_unittest.cc b/content/browser/dom_storage/storage_area_impl_unittest.cc
index 7357675..fdf84c9 100644
--- a/content/browser/dom_storage/storage_area_impl_unittest.cc
+++ b/content/browser/dom_storage/storage_area_impl_unittest.cc
@@ -1134,7 +1134,7 @@
 
 namespace {
 std::string GetNewPrefix(int* i) {
-  std::string prefix = "prefix-" + base::Int64ToString(*i) + "-";
+  std::string prefix = "prefix-" + base::NumberToString(*i) + "-";
   (*i)++;
   return prefix;
 }
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index b0670742..c19a8e1 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -966,7 +966,7 @@
     std::map<std::string, std::string> params = {
         {download::kMinSliceSizeFinchKey, "1"},
         {download::kParallelRequestCountFinchKey,
-         base::IntToString(kTestRequestCount)},
+         base::NumberToString(kTestRequestCount)},
         {download::kParallelRequestDelayFinchKey, "0"},
         {download::kParallelRequestRemainingTimeFinchKey, "0"}};
     scoped_feature_list_.InitAndEnableFeatureWithParameters(
diff --git a/content/browser/download/download_manager_impl.cc b/content/browser/download/download_manager_impl.cc
index 941eb49..2d45e9e 100644
--- a/content/browser/download/download_manager_impl.cc
+++ b/content/browser/download/download_manager_impl.cc
@@ -1298,6 +1298,15 @@
   }
 
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+    // Ideally everywhere a blob: URL is downloaded a URLLoaderFactory for that
+    // blob URL is also passed, but since that isn't always the case, create
+    // a new factory if we don't have one already.
+    if (!blob_url_loader_factory && params->url().SchemeIsBlob()) {
+      blob_url_loader_factory =
+          ChromeBlobStorageContext::URLLoaderFactoryForUrl(browser_context_,
+                                                           params->url());
+    }
+
     auto* rfh = RenderFrameHost::FromID(params->render_process_host_id(),
                                         params->render_frame_host_routing_id());
     bool content_initiated = params->content_initiated();
diff --git a/content/browser/download/drag_download_util.cc b/content/browser/download/drag_download_util.cc
index a91ec488..ab829be 100644
--- a/content/browser/download/drag_download_util.cc
+++ b/content/browser/download/drag_download_util.cc
@@ -70,9 +70,9 @@
     } else {
 #if defined(OS_WIN)
       base::string16 suffix =
-          base::ASCIIToUTF16("-") + base::IntToString16(seq);
+          base::ASCIIToUTF16("-") + base::NumberToString16(seq);
 #else
-      std::string suffix = std::string("-") + base::IntToString(seq);
+      std::string suffix = std::string("-") + base::NumberToString(seq);
 #endif
       new_file_path = file_path->InsertBeforeExtension(suffix);
     }
diff --git a/content/browser/fileapi/browser_file_system_helper.cc b/content/browser/fileapi/browser_file_system_helper.cc
index 6c26a49..e278fff 100644
--- a/content/browser/fileapi/browser_file_system_helper.cc
+++ b/content/browser/fileapi/browser_file_system_helper.cc
@@ -262,10 +262,10 @@
 
     // Note: We are using the origin URL provided by the sender here. It may be
     // different from the receiver's.
-    file_system_file.url =
-        GURL(storage::GetIsolatedFileSystemRootURIString(
-                 file_system_url.origin(), filesystem_id, std::string())
-                 .append(register_name));
+    file_system_file.url = GURL(
+        storage::GetIsolatedFileSystemRootURIString(
+            file_system_url.origin().GetURL(), filesystem_id, std::string())
+            .append(register_name));
     file_system_file.filesystem_id = filesystem_id;
   }
 }
diff --git a/content/browser/fileapi/browser_file_system_helper_unittest.cc b/content/browser/fileapi/browser_file_system_helper_unittest.cc
index 9c12c5a..97e4162 100644
--- a/content/browser/fileapi/browser_file_system_helper_unittest.cc
+++ b/content/browser/fileapi/browser_file_system_helper_unittest.cc
@@ -57,7 +57,7 @@
       external_mount_points->CreateExternalFileSystemURL(kSensitiveOrigin,
                                                          kMountName, kTestPath);
   EXPECT_TRUE(original_file.is_valid());
-  EXPECT_EQ(kSensitiveOrigin, original_file.origin());
+  EXPECT_EQ(kSensitiveOrigin, original_file.origin().GetURL());
 
   // Prepare fake FileSystemContext to use in the test.
   scoped_refptr<base::SingleThreadTaskRunner> io_task_runner(
diff --git a/content/browser/fileapi/file_system_url_loader_factory.cc b/content/browser/fileapi/file_system_url_loader_factory.cc
index e579b60..198cd82 100644
--- a/content/browser/fileapi/file_system_url_loader_factory.cc
+++ b/content/browser/fileapi/file_system_url_loader_factory.cc
@@ -297,7 +297,7 @@
     const DirectoryEntry& entry = entries_[index];
     const FileSystemURL entry_url =
         params_.file_system_context->CreateCrackedFileSystemURL(
-            url_.origin(), url_.type(),
+            url_.origin().GetURL(), url_.type(),
             url_.path().Append(base::FilePath(entry.name)));
     DCHECK(entry_url.is_valid());
     params_.file_system_context->operation_runner()->GetMetadata(
diff --git a/content/browser/frame_host/frame_tree_unittest.cc b/content/browser/frame_host/frame_tree_unittest.cc
index 20c5bb3..bb01795 100644
--- a/content/browser/frame_host/frame_tree_unittest.cc
+++ b/content/browser/frame_host/frame_tree_unittest.cc
@@ -33,7 +33,7 @@
 // Appends a description of the structure of the frame tree to |result|.
 void AppendTreeNodeState(FrameTreeNode* node, std::string* result) {
   result->append(
-      base::Int64ToString(node->current_frame_host()->GetRoutingID()));
+      base::NumberToString(node->current_frame_host()->GetRoutingID()));
   if (!node->current_frame_host()->IsRenderFrameLive())
     result->append("*");  // Asterisk next to dead frames.
 
@@ -139,7 +139,8 @@
          frame_tree->NodesExceptSubtree(subtree_to_skip)) {
       if (!result.empty())
         result += " ";
-      result += base::Int64ToString(node->current_frame_host()->GetRoutingID());
+      result +=
+          base::NumberToString(node->current_frame_host()->GetRoutingID());
     }
     return result;
   }
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 32cb4633..ad2231d4 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -2774,14 +2774,25 @@
   }
 
   std::unique_ptr<NavigationEntryImpl> entry;
+  // extra_headers in params are \n separated; navigation entries want \r\n.
+  std::string extra_headers_crlf;
+  base::ReplaceChars(params.extra_headers, "\n", "\r\n", &extra_headers_crlf);
 
   // For subframes, create a pending entry with a corresponding frame entry.
   if (!node->IsMainFrame()) {
-    DCHECK(GetLastCommittedEntry());
-
-    // Create an identical NavigationEntry with a new FrameNavigationEntry for
-    // the target subframe.
-    entry = GetLastCommittedEntry()->Clone();
+    if (GetLastCommittedEntry()) {
+      // Create an identical NavigationEntry with a new FrameNavigationEntry for
+      // the target subframe.
+      entry = GetLastCommittedEntry()->Clone();
+    } else {
+      // If there's no last committed entry, create an entry for about:blank
+      // with a subframe entry for our destination.
+      // TODO(creis): Ensure this case can't exist in https://crbug.com/524208.
+      entry = NavigationEntryImpl::FromNavigationEntry(CreateNavigationEntry(
+          GURL(url::kAboutBlankURL), params.referrer, params.transition_type,
+          params.is_renderer_initiated, extra_headers_crlf, browser_context_,
+          blob_url_loader_factory));
+    }
 
     entry->AddOrUpdateFrameEntry(
         node, -1, -1, nullptr,
@@ -2790,10 +2801,6 @@
         PageState(), "GET", -1, blob_url_loader_factory);
   } else {
     // Otherwise, create a pending entry for the main frame.
-
-    // extra_headers in params are \n separated; navigation entries want \r\n.
-    std::string extra_headers_crlf;
-    base::ReplaceChars(params.extra_headers, "\n", "\r\n", &extra_headers_crlf);
     entry = NavigationEntryImpl::FromNavigationEntry(CreateNavigationEntry(
         params.url, params.referrer, params.transition_type,
         params.is_renderer_initiated, extra_headers_crlf, browser_context_,
diff --git a/content/browser/frame_host/navigation_handle_impl.cc b/content/browser/frame_host/navigation_handle_impl.cc
index 99421a6..48b56d9 100644
--- a/content/browser/frame_host/navigation_handle_impl.cc
+++ b/content/browser/frame_host/navigation_handle_impl.cc
@@ -133,47 +133,25 @@
 
 NavigationHandleImpl::NavigationHandleImpl(
     NavigationRequest* navigation_request,
-    const GURL& url,
-    const base::Optional<url::Origin>& initiator_origin,
     const std::vector<GURL>& redirect_chain,
-    bool is_renderer_initiated,
     bool is_same_document,
-    base::TimeTicks navigation_start,
     int pending_nav_entry_id,
-    bool started_from_context_menu,
-    bool is_form_submission,
     std::unique_ptr<NavigationUIData> navigation_ui_data,
-    const std::string& method,
     net::HttpRequestHeaders request_headers,
-    scoped_refptr<network::ResourceRequestBody> resource_request_body,
     const Referrer& sanitized_referrer,
-    bool has_user_gesture,
-    ui::PageTransition transition,
-    bool is_external_protocol,
-    const std::string& href_translate,
-    base::TimeTicks input_start)
+    bool is_external_protocol)
     : navigation_request_(navigation_request),
-      url_(url),
-      initiator_origin_(initiator_origin),
-      has_user_gesture_(has_user_gesture),
-      transition_(transition),
       is_external_protocol_(is_external_protocol),
       net_error_code_(net::OK),
       render_frame_host_(nullptr),
-      is_renderer_initiated_(is_renderer_initiated),
       is_same_document_(is_same_document),
       was_redirected_(false),
       did_replace_entry_(false),
       should_update_history_(false),
       subframe_entry_committed_(false),
       connection_info_(net::HttpResponseInfo::CONNECTION_INFO_UNKNOWN),
-      href_translate_(href_translate),
-      original_url_(url),
-      method_(method),
       request_headers_(std::move(request_headers)),
       state_(INITIAL),
-      navigation_start_(navigation_start),
-      input_start_(input_start),
       pending_nav_entry_id_(pending_nav_entry_id),
       navigation_ui_data_(std::move(navigation_ui_data)),
       navigation_id_(CreateUniqueHandleID()),
@@ -182,21 +160,19 @@
       restore_type_(RestoreType::NONE),
       navigation_type_(NAVIGATION_TYPE_UNKNOWN),
       expected_render_process_host_id_(ChildProcessHost::kInvalidUniqueID),
-      is_form_submission_(is_form_submission),
-      should_replace_current_entry_(false),
       is_download_(false),
       is_stream_(false),
       is_signed_exchange_inner_response_(false),
       was_cached_(false),
-      started_from_context_menu_(started_from_context_menu),
       is_same_process_(true),
       throttle_runner_(this, this),
       weak_factory_(this) {
+  const GURL& url = navigation_request_->common_params().url;
   TRACE_EVENT_ASYNC_BEGIN2("navigation", "NavigationHandle", this,
                            "frame_tree_node",
                            frame_tree_node()->frame_tree_node_id(), "url",
-                           url_.possibly_invalid_spec());
-  DCHECK(!navigation_start.is_null());
+                           url.possibly_invalid_spec());
+  DCHECK(!navigation_request_->common_params().navigation_start.is_null());
   DCHECK(!IsRendererDebugURL(url));
 
   starting_site_instance_ =
@@ -204,19 +180,13 @@
 
   site_url_ = SiteInstanceImpl::GetSiteForURL(
       starting_site_instance_->GetBrowserContext(),
-      starting_site_instance_->GetIsolationContext(), url_);
+      starting_site_instance_->GetIsolationContext(), url);
   if (redirect_chain_.empty())
     redirect_chain_.push_back(url);
 
-  if (method != "POST")
-    DCHECK(!resource_request_body);
-
-  // Update the navigation parameters.
-  if (method_ == "POST")
-    resource_request_body_ = resource_request_body;
-
   // Mirrors the logic in RenderFrameImpl::SendDidCommitProvisionalLoad.
-  if (transition_ & ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
+  if (navigation_request_->common_params().transition &
+      ui::PAGE_TRANSITION_CLIENT_REDIRECT) {
     // If the page contained a client redirect (meta refresh,
     // document.location), set the referrer appropriately.
     sanitized_referrer_ =
@@ -245,7 +215,6 @@
     if (nav_entry) {
       reload_type_ = nav_entry->reload_type();
       restore_type_ = nav_entry->restore_type();
-      base_url_for_data_url_ = nav_entry->GetBaseURLForDataURL();
     }
   }
 
@@ -254,7 +223,8 @@
   if (IsInMainFrame()) {
     TRACE_EVENT_ASYNC_BEGIN_WITH_TIMESTAMP1(
         "navigation", "Navigation StartToCommit", this,
-        navigation_start, "Initial URL", url_.spec());
+        navigation_request_->common_params().navigation_start, "Initial URL",
+        url.spec());
   }
 
   if (is_same_document_) {
@@ -279,8 +249,9 @@
 
   if (IsInMainFrame()) {
     TRACE_EVENT_ASYNC_END2("navigation", "Navigation StartToCommit", this,
-                           "URL", url_.spec(), "Net Error Code",
-                           net_error_code_);
+                           "URL",
+                           navigation_request_->common_params().url.spec(),
+                           "Net Error Code", net_error_code_);
   }
   TRACE_EVENT_ASYNC_END0("navigation", "NavigationHandle", this);
 }
@@ -294,7 +265,7 @@
 }
 
 const GURL& NavigationHandleImpl::GetURL() {
-  return url_;
+  return navigation_request_->common_params().url;
 }
 
 SiteInstanceImpl* NavigationHandleImpl::GetStartingSiteInstance() {
@@ -313,7 +284,7 @@
 }
 
 bool NavigationHandleImpl::IsRendererInitiated() {
-  return is_renderer_initiated_;
+  return !navigation_request_->browser_initiated();
 }
 
 bool NavigationHandleImpl::WasServerRedirect() {
@@ -336,20 +307,20 @@
 }
 
 base::TimeTicks NavigationHandleImpl::NavigationStart() {
-  return navigation_start_;
+  return navigation_request_->common_params().navigation_start;
 }
 
 base::TimeTicks NavigationHandleImpl::NavigationInputStart() {
-  return input_start_;
+  return navigation_request_->common_params().input_start;
 }
 
 bool NavigationHandleImpl::IsPost() {
-  return method_ == "POST";
+  return navigation_request_->common_params().method == "POST";
 }
 
 const scoped_refptr<network::ResourceRequestBody>&
 NavigationHandleImpl::GetResourceRequestBody() {
-  return resource_request_body_;
+  return navigation_request_->common_params().post_data;
 }
 
 const Referrer& NavigationHandleImpl::GetReferrer() {
@@ -357,11 +328,11 @@
 }
 
 bool NavigationHandleImpl::HasUserGesture() {
-  return has_user_gesture_;
+  return navigation_request_->common_params().has_user_gesture;
 }
 
 ui::PageTransition NavigationHandleImpl::GetPageTransition() {
-  return transition_;
+  return navigation_request_->common_params().transition;
 }
 
 const NavigationUIData* NavigationHandleImpl::GetNavigationUIData() {
@@ -488,24 +459,20 @@
   throttle_runner_.AddThrottle(std::move(navigation_throttle));
 }
 
-void NavigationHandleImpl::CallResumeForTesting() {
-  throttle_runner_.CallResumeForTesting();
-}
-
 bool NavigationHandleImpl::IsDeferredForTesting() {
   return throttle_runner_.GetDeferringThrottle() != nullptr;
 }
 
 bool NavigationHandleImpl::WasStartedFromContextMenu() const {
-  return started_from_context_menu_;
+  return navigation_request_->common_params().started_from_context_menu;
 }
 
 const GURL& NavigationHandleImpl::GetSearchableFormURL() {
-  return searchable_form_url_;
+  return navigation_request_->begin_params()->searchable_form_url;
 }
 
 const std::string& NavigationHandleImpl::GetSearchableFormEncoding() {
-  return searchable_form_encoding_;
+  return navigation_request_->begin_params()->searchable_form_encoding;
 }
 
 ReloadType NavigationHandleImpl::GetReloadType() {
@@ -517,7 +484,7 @@
 }
 
 const GURL& NavigationHandleImpl::GetBaseURLForDataURL() {
-  return base_url_for_data_url_;
+  return navigation_request_->common_params().base_url_for_data_url;
 }
 
 NavigationData* NavigationHandleImpl::GetNavigationData() {
@@ -543,15 +510,19 @@
 }
 
 bool NavigationHandleImpl::IsFormSubmission() {
-  return is_form_submission_;
+  return navigation_request_->begin_params()->is_form_submission;
 }
 
 const std::string& NavigationHandleImpl::GetHrefTranslate() {
-  return href_translate_;
+  return navigation_request_->common_params().href_translate;
+}
+
+void NavigationHandleImpl::CallResumeForTesting() {
+  throttle_runner_.CallResumeForTesting();
 }
 
 const base::Optional<url::Origin>& NavigationHandleImpl::GetInitiatorOrigin() {
-  return initiator_origin_;
+  return navigation_request_->common_params().initiator_origin;
 }
 
 bool NavigationHandleImpl::IsSignedExchangeInnerResponse() {
@@ -615,46 +586,36 @@
 }
 
 void NavigationHandleImpl::UpdateStateFollowingRedirect(
-    const GURL& new_url,
-    const std::string& new_method,
     const GURL& new_referrer_url,
     bool new_is_external_protocol,
     scoped_refptr<net::HttpResponseHeaders> response_headers,
     net::HttpResponseInfo::ConnectionInfo connection_info,
     ThrottleChecksFinishedCallback callback) {
-  // |new_url| is not expected to be a "renderer debug" url. It should be
+  // The navigation should not redirect to a "renderer debug" url. It should be
   // blocked in NavigationRequest::OnRequestRedirected or in
-  // ResourceLoader::OnReceivedRedirect. If it is not the case,
-  // DidFinishNavigation will not be called. It could confuse some
-  // WebContentsObserver because DidStartNavigation was called.
+  // ResourceLoader::OnReceivedRedirect.
+  // Note: the call to GetURL below returns the post-redirect URL.
   // See https://crbug.com/728398.
-  CHECK(!IsRendererDebugURL(new_url));
+  CHECK(!IsRendererDebugURL(GetURL()));
 
   // Update the navigation parameters.
-  url_ = new_url;
-  method_ = new_method;
-
-  if (!(transition_ & ui::PAGE_TRANSITION_CLIENT_REDIRECT)) {
+  if (!(GetPageTransition() & ui::PAGE_TRANSITION_CLIENT_REDIRECT)) {
     sanitized_referrer_.url = new_referrer_url;
     sanitized_referrer_ =
-        Referrer::SanitizeForRequest(url_, sanitized_referrer_);
+        Referrer::SanitizeForRequest(GetURL(), sanitized_referrer_);
   }
 
   is_external_protocol_ = new_is_external_protocol;
   response_headers_ = response_headers;
   connection_info_ = connection_info;
   was_redirected_ = true;
-  redirect_chain_.push_back(new_url);
-  if (new_method != "POST")
-    resource_request_body_ = nullptr;
+  redirect_chain_.push_back(GetURL());
 
   state_ = PROCESSING_WILL_REDIRECT_REQUEST;
   complete_callback_ = std::move(callback);
 }
 
 void NavigationHandleImpl::WillRedirectRequest(
-    const GURL& new_url,
-    const std::string& new_method,
     const GURL& new_referrer_url,
     bool new_is_external_protocol,
     scoped_refptr<net::HttpResponseHeaders> response_headers,
@@ -663,10 +624,10 @@
     ThrottleChecksFinishedCallback callback) {
   TRACE_EVENT_ASYNC_STEP_INTO1("navigation", "NavigationHandle", this,
                                "WillRedirectRequest", "url",
-                               new_url.possibly_invalid_spec());
-  UpdateStateFollowingRedirect(new_url, new_method, new_referrer_url,
-                               new_is_external_protocol, response_headers,
-                               connection_info, std::move(callback));
+                               GetURL().possibly_invalid_spec());
+  UpdateStateFollowingRedirect(new_referrer_url, new_is_external_protocol,
+                               response_headers, connection_info,
+                               std::move(callback));
   UpdateSiteURL(post_redirect_process);
 
   if (IsSelfReferentialURL()) {
@@ -709,7 +670,6 @@
     const net::HostPortPair& socket_address,
     const net::SSLInfo& ssl_info,
     const GlobalRequestID& request_id,
-    bool should_replace_current_entry,
     bool is_download,
     bool is_stream,
     bool is_signed_exchange_inner_response,
@@ -723,7 +683,6 @@
   response_headers_ = response_headers;
   connection_info_ = connection_info;
   request_id_ = request_id;
-  should_replace_current_entry_ = should_replace_current_entry;
   is_download_ = is_download;
   is_stream_ = is_stream;
   is_signed_exchange_inner_response_ = is_signed_exchange_inner_response;
@@ -772,30 +731,36 @@
     is_same_process_ =
         render_frame_host_->GetProcess()->GetID() ==
         frame_tree_node()->current_frame_host()->GetProcess()->GetID();
-    LogIsSameProcess(transition_, is_same_process_);
+    LogIsSameProcess(GetPageTransition(), is_same_process_);
 
     // Don't log process-priority-specific UMAs for TimeToReadyToCommit2 metric
     // (which shouldn't be influenced by renderer priority).
     constexpr base::Optional<bool> kIsBackground = base::nullopt;
 
-    base::TimeDelta delta = ready_to_commit_time_ - navigation_start_;
-    LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2", transition_,
+    base::TimeDelta delta =
+        ready_to_commit_time_ -
+        navigation_request_->common_params().navigation_start;
+    LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2", GetPageTransition(),
                                     kIsBackground, delta);
 
     if (IsInMainFrame()) {
       LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2.MainFrame",
-                                      transition_, kIsBackground, delta);
+                                      GetPageTransition(), kIsBackground,
+                                      delta);
     } else {
       LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2.Subframe",
-                                      transition_, kIsBackground, delta);
+                                      GetPageTransition(), kIsBackground,
+                                      delta);
     }
 
     if (is_same_process_) {
       LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2.SameProcess",
-                                      transition_, kIsBackground, delta);
+                                      GetPageTransition(), kIsBackground,
+                                      delta);
     } else {
       LOG_NAVIGATION_TIMING_HISTOGRAM("TimeToReadyToCommit2.CrossProcess",
-                                      transition_, kIsBackground, delta);
+                                      GetPageTransition(), kIsBackground,
+                                      delta);
     }
   }
 
@@ -814,12 +779,9 @@
     RenderFrameHostImpl* render_frame_host) {
   DCHECK(!render_frame_host_ || render_frame_host_ == render_frame_host);
   DCHECK_EQ(frame_tree_node(), render_frame_host->frame_tree_node());
-  CHECK_EQ(url_, params.url);
+  CHECK_EQ(GetURL(), params.url);
 
   did_replace_entry_ = did_replace_entry;
-  method_ = params.method;
-  has_user_gesture_ = (params.gesture == NavigationGestureUser);
-  transition_ = params.transition;
   should_update_history_ = params.should_update_history;
   render_frame_host_ = render_frame_host;
   previous_url_ = previous_url;
@@ -845,7 +807,8 @@
   // another document without error.
   if (!IsSameDocument() && !IsErrorPage()) {
     base::TimeTicks now = base::TimeTicks::Now();
-    base::TimeDelta delta = now - navigation_start_;
+    base::TimeDelta delta =
+        now - navigation_request_->common_params().navigation_start;
     ui::PageTransition transition = GetPageTransition();
     base::Optional<bool> is_background =
         render_frame_host->GetProcess()->IsProcessBackgrounded();
@@ -881,8 +844,8 @@
     }
 
     if (!ready_to_commit_time_.is_null()) {
-      LOG_NAVIGATION_TIMING_HISTOGRAM("ReadyToCommitUntilCommit2", transition_,
-                                      is_background,
+      LOG_NAVIGATION_TIMING_HISTOGRAM("ReadyToCommitUntilCommit2",
+                                      GetPageTransition(), is_background,
                                       now - ready_to_commit_time_);
     }
   }
@@ -1064,17 +1027,17 @@
   // about: URLs should be exempted since they are reserved for other purposes
   // and cannot be the source of infinite recursion. See
   // https://crbug.com/341858 .
-  if (url_.SchemeIs("about"))
+  if (GetURL().SchemeIs("about"))
     return false;
 
   // Browser-triggered navigations should be exempted.
-  if (!is_renderer_initiated_)
+  if (navigation_request_->browser_initiated())
     return false;
 
   // Some sites rely on constructing frame hierarchies where frames are loaded
   // via POSTs with the same URLs, so exempt POST requests.  See
   // https://crbug.com/710008.
-  if (method_ == "POST")
+  if (navigation_request_->common_params().method == "POST")
     return false;
 
   // We allow one level of self-reference because some sites depend on that,
@@ -1082,7 +1045,7 @@
   bool found_self_reference = false;
   for (const FrameTreeNode* node = frame_tree_node()->parent(); node;
        node = node->parent()) {
-    if (node->current_url().EqualsIgnoringRef(url_)) {
+    if (node->current_url().EqualsIgnoringRef(GetURL())) {
       if (found_self_reference)
         return true;
       found_self_reference = true;
@@ -1097,7 +1060,7 @@
   // be correct for cross-BrowsingInstance redirects.
   GURL new_site_url = SiteInstanceImpl::GetSiteForURL(
       frame_tree_node()->navigator()->GetController()->GetBrowserContext(),
-      starting_site_instance_->GetIsolationContext(), url_);
+      starting_site_instance_->GetIsolationContext(), GetURL());
   int post_redirect_process_id = post_redirect_process
                                      ? post_redirect_process->GetID()
                                      : ChildProcessHost::kInvalidUniqueID;
diff --git a/content/browser/frame_host/navigation_handle_impl.h b/content/browser/frame_host/navigation_handle_impl.h
index f88f1fc..02a4c83 100644
--- a/content/browser/frame_host/navigation_handle_impl.h
+++ b/content/browser/frame_host/navigation_handle_impl.h
@@ -134,10 +134,10 @@
   const std::string& GetHrefTranslate() override;
   const base::Optional<url::Origin>& GetInitiatorOrigin() override;
 
-  const std::string& origin_policy() const { return origin_policy_; }
-  void set_origin_policy(const std::string& origin_policy) {
-    origin_policy_ = origin_policy;
-  }
+  // Returns the NavigationRequest which owns this NavigationHandle.
+  NavigationRequest* navigation_request() { return navigation_request_; }
+
+  const std::string& GetOriginPolicy() const;
 
   // Resume and CancelDeferredNavigation must only be called by the
   // NavigationThrottle that is currently deferring the navigation.
@@ -217,8 +217,6 @@
   // Updates the state of the navigation handle after encountering a server
   // redirect.
   void UpdateStateFollowingRedirect(
-      const GURL& new_url,
-      const std::string& new_method,
       const GURL& new_referrer_url,
       bool new_is_external_protocol,
       scoped_refptr<net::HttpResponseHeaders> response_headers,
@@ -235,8 +233,6 @@
   // null if there is no live process that can be used. In that case, a suitable
   // renderer process will be created at commit time.
   void WillRedirectRequest(
-      const GURL& new_url,
-      const std::string& new_method,
       const GURL& new_referrer_url,
       bool new_is_external_protocol,
       scoped_refptr<net::HttpResponseHeaders> response_headers,
@@ -267,7 +263,6 @@
       const net::HostPortPair& socket_address,
       const net::SSLInfo& ssl_info,
       const GlobalRequestID& request_id,
-      bool should_replace_current_entry,
       bool is_download,
       bool is_stream,
       bool is_signed_exchange_inner_response,
@@ -312,11 +307,6 @@
 
   const GURL& base_url() { return base_url_; }
 
-  void set_searchable_form_url(const GURL& url) { searchable_form_url_ = url; }
-  void set_searchable_form_encoding(const std::string& encoding) {
-    searchable_form_encoding_ = encoding;
-  }
-
   NavigationType navigation_type() {
     DCHECK_GE(state_, DID_COMMIT);
     return navigation_type_;
@@ -382,27 +372,14 @@
   // start with |url|. Otherwise |redirect_chain| is used as the starting point.
   // |navigation_start| comes from the CommonNavigationParams associated with
   // this navigation.
-  NavigationHandleImpl(
-      NavigationRequest* navigation_request,
-      const GURL& url,
-      const base::Optional<url::Origin>& initiator_origin,
-      const std::vector<GURL>& redirect_chain,
-      bool is_renderer_initiated,
-      bool is_same_document,
-      base::TimeTicks navigation_start,
-      int pending_nav_entry_id,
-      bool started_from_context_menu,
-      bool is_form_submission,
-      std::unique_ptr<NavigationUIData> navigation_ui_data,
-      const std::string& method,
-      net::HttpRequestHeaders request_headers,
-      scoped_refptr<network::ResourceRequestBody> resource_request_body,
-      const Referrer& sanitized_referrer,
-      bool has_user_gesture,
-      ui::PageTransition transition,
-      bool is_external_protocol,
-      const std::string& href_translate,
-      base::TimeTicks input_start);
+  NavigationHandleImpl(NavigationRequest* navigation_request,
+                       const std::vector<GURL>& redirect_chain,
+                       bool is_same_document,
+                       int pending_nav_entry_id,
+                       std::unique_ptr<NavigationUIData> navigation_ui_data,
+                       net::HttpRequestHeaders request_headers,
+                       const Referrer& sanitized_referrer,
+                       bool is_external_protocol);
 
   // NavigationThrottleRunner::Delegate:
   void OnNavigationEventProcessed(
@@ -454,16 +431,11 @@
   NavigationRequest* navigation_request_;
 
   // See NavigationHandle for a description of those member variables.
-  GURL url_;
   scoped_refptr<SiteInstanceImpl> starting_site_instance_;
-  base::Optional<url::Origin> initiator_origin_;
   Referrer sanitized_referrer_;
-  bool has_user_gesture_;
-  ui::PageTransition transition_;
   bool is_external_protocol_;
   net::Error net_error_code_;
   RenderFrameHostImpl* render_frame_host_;
-  const bool is_renderer_initiated_;
   const bool is_same_document_;
   bool was_redirected_;
   bool did_replace_entry_;
@@ -472,18 +444,10 @@
   scoped_refptr<net::HttpResponseHeaders> response_headers_;
   net::HttpResponseInfo::ConnectionInfo connection_info_;
   net::SSLInfo ssl_info_;
-  std::string href_translate_;
-
-  // The original url of the navigation. This may differ from |url_| if the
-  // navigation encounters redirects.
-  const GURL original_url_;
 
   // The site URL of this navigation, as obtained from SiteInstance::GetSiteURL.
   GURL site_url_;
 
-  // The HTTP method used for the navigation.
-  std::string method_;
-
   // The headers used for the request.
   net::HttpRequestHeaders request_headers_;
 
@@ -494,11 +458,6 @@
   std::vector<std::string> removed_request_headers_;
   net::HttpRequestHeaders modified_request_headers_;
 
-  // The POST body associated with this navigation.  This will be null for GET
-  // and/or other non-POST requests (or if a response to a POST request was a
-  // redirect that changed the method to GET - for example 302).
-  scoped_refptr<network::ResourceRequestBody> resource_request_body_;
-
   // The state the navigation is in.
   State state_;
 
@@ -508,14 +467,6 @@
   // The index of the next throttle to check.
   size_t next_index_;
 
-  // The time this navigation started.
-  const base::TimeTicks navigation_start_;
-
-  // The time the input event that lead to this navigation started.
-  // Currently available only if the navigation was initiated by
-  // the user clicking a link in the renderer.
-  const base::TimeTicks input_start_;
-
   // The time this naviagtion was ready to commit.
   base::TimeTicks ready_to_commit_time_;
 
@@ -570,12 +521,8 @@
   // Stores the restore type, or NONE it it's not a restore.
   RestoreType restore_type_;
 
-  GURL searchable_form_url_;
-  std::string searchable_form_encoding_;
-
   GURL previous_url_;
   GURL base_url_;
-  GURL base_url_for_data_url_;
   net::HostPortPair socket_address_;
   NavigationType navigation_type_;
 
@@ -583,15 +530,6 @@
   // in it.
   int expected_render_process_host_id_;
 
-  // The origin policy that applies to this navigation. Empty if none applies.
-  std::string origin_policy_;
-
-  // Whether or not the navigation results from the submission of a form.
-  bool is_form_submission_;
-
-  // Whether the current NavigationEntry should be replaced upon commit.
-  bool should_replace_current_entry_;
-
   // Whether the navigation ended up being a download or a stream.
   bool is_download_;
   bool is_stream_;
@@ -605,9 +543,6 @@
   // Which proxy server was used for this navigation, if any.
   net::ProxyServer proxy_server_;
 
-  // False by default unless the navigation started within a context menu.
-  bool started_from_context_menu_;
-
   // Set in ReadyToCommitNavigation.
   bool is_same_process_;
 
diff --git a/content/browser/frame_host/navigation_handle_impl_unittest.cc b/content/browser/frame_host/navigation_handle_impl_unittest.cc
index 3071098..d6226569 100644
--- a/content/browser/frame_host/navigation_handle_impl_unittest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_unittest.cc
@@ -109,7 +109,7 @@
     // It's safe to use base::Unretained since the NavigationHandle is owned by
     // the NavigationHandleImplTest.
     test_handle_->WillRedirectRequest(
-        GURL(), "GET", GURL(), false, scoped_refptr<net::HttpResponseHeaders>(),
+        GURL(), false, scoped_refptr<net::HttpResponseHeaders>(),
         net::HttpResponseInfo::CONNECTION_INFO_HTTP1_1, nullptr,
         base::Bind(&NavigationHandleImplTest::UpdateThrottleCheckResult,
                    base::Unretained(this)));
@@ -149,7 +149,7 @@
     test_handle_->WillProcessResponse(
         main_test_rfh(), scoped_refptr<net::HttpResponseHeaders>(),
         net::HttpResponseInfo::CONNECTION_INFO_QUIC_35, net::HostPortPair(),
-        net::SSLInfo(), GlobalRequestID(), false, false, false, false, false,
+        net::SSLInfo(), GlobalRequestID(), false, false, false, false,
         base::Bind(&NavigationHandleImplTest::UpdateThrottleCheckResult,
                    base::Unretained(this)));
   }
@@ -237,23 +237,13 @@
         main_test_rfh()->frame_tree_node(), CommonNavigationParams(),
         CommitNavigationParams(), false /* browser-initiated */, std::string(),
         *frame_entry, nullptr, nullptr, nullptr);
-    test_handle_ =
-        base::WrapUnique<NavigationHandleImpl>(new NavigationHandleImpl(
-            request_.get(), GURL(), base::nullopt, std::vector<GURL>(),
-            true,   // is_renderer_initiated
-            false,  // is_same_document
-            base::TimeTicks::Now(), 0,
-            false,                  // started_from_context_menu
-            false,                  // is_form_submission
-            nullptr,                // navigation_ui_data
-            "GET", net::HttpRequestHeaders(),
-            nullptr,  // resource_request_body
-            Referrer(),
-            false,  // has_user_gesture
-            ui::PAGE_TRANSITION_LINK,
-            false,  // is_external_protocol
-            std::string(),        // href_translate
-            base::TimeTicks()));  // input_start
+    test_handle_ = base::WrapUnique<NavigationHandleImpl>(
+        new NavigationHandleImpl(request_.get(), std::vector<GURL>(),
+                                 false,  // is_same_document
+                                 0,
+                                 nullptr,  // navigation_ui_data
+                                 net::HttpRequestHeaders(), Referrer(),
+                                 false));  // is_external_protocol
   }
 
  private:
diff --git a/content/browser/frame_host/navigation_request.cc b/content/browser/frame_host/navigation_request.cc
index f5ffc8df..e5cacf8 100644
--- a/content/browser/frame_host/navigation_request.cc
+++ b/content/browser/frame_host/navigation_request.cc
@@ -494,6 +494,7 @@
       weak_factory_(this) {
   DCHECK(!browser_initiated || (entry != nullptr && frame_entry != nullptr));
   DCHECK(!IsRendererDebugURL(common_params_.url));
+  DCHECK(common_params_.method == "POST" || !common_params_.post_data);
   TRACE_EVENT_ASYNC_BEGIN2("navigation", "NavigationRequest", this,
                            "frame_tree_node",
                            frame_tree_node_->frame_tree_node_id(), "url",
@@ -772,19 +773,13 @@
 
   std::unique_ptr<NavigationHandleImpl> navigation_handle =
       base::WrapUnique(new NavigationHandleImpl(
-          this, common_params_.url, common_params_.initiator_origin,
-          redirect_chain, !browser_initiated_,
+          this, redirect_chain,
           FrameMsg_Navigate_Type::IsSameDocument(
               common_params_.navigation_type),
-          common_params_.navigation_start, nav_entry_id_,
-          common_params_.started_from_context_menu,
-          begin_params_->is_form_submission, std::move(navigation_ui_data_),
-          common_params_.method, std::move(headers), common_params_.post_data,
+          nav_entry_id_, std::move(navigation_ui_data_), std::move(headers),
           Referrer::SanitizeForRequest(common_params_.url,
                                        common_params_.referrer),
-          common_params_.has_user_gesture, common_params_.transition,
-          is_external_protocol, common_params_.href_translate,
-          common_params_.input_start));
+          is_external_protocol));
 
   if (!frame_tree_node->navigation_request() && !is_for_commit) {
     // A callback could have cancelled this request synchronously in which case
@@ -793,13 +788,6 @@
   }
 
   navigation_handle_ = std::move(navigation_handle);
-
-  if (!begin_params_->searchable_form_url.is_empty()) {
-    navigation_handle_->set_searchable_form_url(
-        begin_params_->searchable_form_url);
-    navigation_handle_->set_searchable_form_encoding(
-        begin_params_->searchable_form_encoding);
-  }
 }
 
 std::unique_ptr<NavigationHandleImpl>
@@ -848,6 +836,10 @@
   return commit_navigation_client_.get();
 }
 
+void NavigationRequest::SetOriginPolicy(const std::string& policy) {
+  common_params_.origin_policy = policy;
+}
+
 void NavigationRequest::OnRequestRedirected(
     const net::RedirectInfo& redirect_info,
     const scoped_refptr<network::ResourceResponse>& response) {
@@ -877,11 +869,12 @@
     bool is_external_protocol =
         !GetContentClient()->browser()->IsHandledURL(common_params_.url);
     navigation_handle_->set_net_error_code(net::ERR_ABORTED);
+    common_params_.url = redirect_info.new_url;
+    common_params_.method = redirect_info.new_method;
     // Update the navigation handle to point to the new url to ensure
     // AwWebContents sees the new URL and thus passes that URL to onPageFinished
     // (rather than passing the old URL).
     navigation_handle_->UpdateStateFollowingRedirect(
-        redirect_info.new_url, redirect_info.new_method,
         GURL(redirect_info.new_referrer), is_external_protocol,
         response->head.headers, response->head.connection_info,
         base::Bind(&NavigationRequest::OnRedirectChecksComplete,
@@ -1013,8 +1006,7 @@
   bool is_external_protocol =
       !GetContentClient()->browser()->IsHandledURL(common_params_.url);
   navigation_handle_->WillRedirectRequest(
-      common_params_.url, common_params_.method, common_params_.referrer.url,
-      is_external_protocol, response->head.headers,
+      common_params_.referrer.url, is_external_protocol, response->head.headers,
       response->head.connection_info, expected_process,
       base::Bind(&NavigationRequest::OnRedirectChecksComplete,
                  base::Unretained(this)));
@@ -1244,8 +1236,8 @@
   navigation_handle_->WillProcessResponse(
       render_frame_host, response->head.headers.get(),
       response->head.connection_info, response->head.socket_address, ssl_info_,
-      request_id, common_params_.should_replace_current_entry, is_download_,
-      is_stream, response->head.is_signed_exchange_inner_response,
+      request_id, is_download_, is_stream,
+      response->head.is_signed_exchange_inner_response,
       response->head.was_fetched_via_cache,
       base::Bind(&NavigationRequest::OnWillProcessResponseChecksComplete,
                  base::Unretained(this)));
@@ -1796,11 +1788,6 @@
          navigation_handle_->IsSameDocument());
   DCHECK(!common_params_.url.SchemeIs(url::kJavaScriptScheme));
 
-  // Send the applicable origin policy (if any) along with the request.
-  // (The policy is fetched by a throttle and is thus available only now.)
-  DCHECK(common_params_.origin_policy.empty());
-  common_params_.origin_policy = navigation_handle_->origin_policy();
-
   // Retrieve the RenderFrameHost that needs to commit the navigation.
   RenderFrameHostImpl* render_frame_host =
       navigation_handle_->GetRenderFrameHost();
diff --git a/content/browser/frame_host/navigation_request.h b/content/browser/frame_host/navigation_request.h
index 0af28a81c..5dd4d9b 100644
--- a/content/browser/frame_host/navigation_request.h
+++ b/content/browser/frame_host/navigation_request.h
@@ -225,6 +225,12 @@
   // for commit. Only used with PerNavigationMojoInterface enabled.
   mojom::NavigationClient* GetCommitNavigationClient();
 
+  void SetOriginPolicy(const std::string& policy);
+
+  void set_transition(ui::PageTransition transition) {
+    common_params_.transition = transition;
+  }
+
  private:
   NavigationRequest(FrameTreeNode* frame_tree_node,
                     const CommonNavigationParams& common_params,
diff --git a/content/browser/frame_host/origin_policy_throttle.cc b/content/browser/frame_host/origin_policy_throttle.cc
index fce4c35..f4ccb8e 100644
--- a/content/browser/frame_host/origin_policy_throttle.cc
+++ b/content/browser/frame_host/origin_policy_throttle.cc
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 
 #include "content/browser/frame_host/origin_policy_throttle.h"
+#include "content/public/browser/origin_policy_commands.h"
 
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/feature_list.h"
 #include "base/no_destructor.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
+#include "content/browser/frame_host/navigation_request.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/navigation_handle.h"
@@ -27,6 +29,12 @@
 static const char* kDeletePolicy = "0";
 static const char* kWellKnown = "/.well-known/origin-policy/";
 
+// Marker for (temporarily) exempted origins.
+// TODO(vogelheim): Make sure this is outside the value space for policy
+//                  names. A name with a comma in it shouldn't be allowed, but
+//                  I don't think we presently check this anywhere.
+static const char* kExemptedOriginPolicy = "exception,";
+
 // Maximum policy size (implementation-defined limit in bytes).
 // (Limit copied from network::SimpleURLLoader::kMaxBoundedStringDownloadSize.)
 static const size_t kMaxPolicySize = 1024 * 1024;
@@ -34,6 +42,12 @@
 
 namespace content {
 
+// Implement the public "API" from
+// content/public/browser/origin_policy_commands.h:
+void OriginPolicyAddExceptionFor(const GURL& url) {
+  OriginPolicyThrottle::AddExceptionFor(url);
+}
+
 // static
 bool OriginPolicyThrottle::ShouldRequestOriginPolicy(
     const GURL& url,
@@ -52,8 +66,9 @@
   if (request_version) {
     const KnownVersionMap& versions = GetKnownVersions();
     const auto iter = versions.find(url::Origin::Create(url));
-    *request_version =
-        iter == versions.end() ? std::string(kDefaultPolicy) : iter->second;
+    bool has_version = iter != versions.end();
+    bool use_default = !has_version || iter->second == kExemptedOriginPolicy;
+    *request_version = use_default ? std::string(kDefaultPolicy) : iter->second;
   }
   return true;
 }
@@ -78,6 +93,11 @@
   return base::WrapUnique(new OriginPolicyThrottle(handle));
 }
 
+// static
+void OriginPolicyThrottle::AddExceptionFor(const GURL& url) {
+  GetKnownVersions()[url::Origin::Create(url)] = kExemptedOriginPolicy;
+}
+
 OriginPolicyThrottle::~OriginPolicyThrottle() {}
 
 NavigationThrottle::ThrottleCheckResult
@@ -130,6 +150,11 @@
     return NavigationThrottle::PROCEED;
   }
 
+  // Process policy exceptions.
+  if (iter != versions.end() && iter->second == kExemptedOriginPolicy) {
+    return NavigationThrottle::PROCEED;
+  }
+
   // No policy applies to this request?
   if (!header_found && iter == versions.end()) {
     return NavigationThrottle::PROCEED;
@@ -245,7 +270,8 @@
   //                  the policy content at this point.
 
   static_cast<NavigationHandleImpl*>(navigation_handle())
-      ->set_origin_policy(*policy_content);
+      ->navigation_request()
+      ->SetOriginPolicy(*policy_content);
   Resume();
 }
 
@@ -261,7 +287,7 @@
 void OriginPolicyThrottle::CancelNavigation(OriginPolicyErrorReason reason) {
   base::Optional<std::string> error_page =
       GetContentClient()->browser()->GetOriginPolicyErrorPage(
-          reason, GetRequestOrigin(), navigation_handle()->GetURL());
+          reason, navigation_handle());
   CancelDeferredNavigation(NavigationThrottle::ThrottleCheckResult(
       NavigationThrottle::CANCEL, net::ERR_BLOCKED_BY_CLIENT, error_page));
 }
diff --git a/content/browser/frame_host/origin_policy_throttle.h b/content/browser/frame_host/origin_policy_throttle.h
index f142dcf..5bf8d43 100644
--- a/content/browser/frame_host/origin_policy_throttle.h
+++ b/content/browser/frame_host/origin_policy_throttle.h
@@ -55,6 +55,12 @@
   static std::unique_ptr<NavigationThrottle> MaybeCreateThrottleFor(
       NavigationHandle* handle);
 
+  // Adds an exception for the given url, despite it serving a broken (or
+  // otherwise invalid) policy. This is meant to be called by the security
+  // interstitial.
+  // This will exempt the entire origin, rather than only the given URL.
+  static void AddExceptionFor(const GURL& url);
+
   ~OriginPolicyThrottle() override;
 
   ThrottleCheckResult WillStartRequest() override;
diff --git a/content/browser/frame_host/origin_policy_throttle_unittest.cc b/content/browser/frame_host/origin_policy_throttle_unittest.cc
index 3f698c2..f082c51a 100644
--- a/content/browser/frame_host/origin_policy_throttle_unittest.cc
+++ b/content/browser/frame_host/origin_policy_throttle_unittest.cc
@@ -139,7 +139,60 @@
 
   // At the end of the navigation, the navigation handle should have a copy
   // of the origin policy.
-  EXPECT_EQ(policy, nav_handle->origin_policy());
+  EXPECT_EQ(policy,
+            nav_handle->navigation_request()->common_params().origin_policy);
+}
+
+TEST_P(OriginPolicyThrottleTest, AddException) {
+  if (!enabled())
+    return;
+
+  GURL url("https://example.org/bla");
+  OriginPolicyThrottle::GetKnownVersionsForTesting()[url::Origin::Create(url)] =
+      "abcd";
+
+  std::string version;
+  OriginPolicyThrottle::ShouldRequestOriginPolicy(url, &version);
+  EXPECT_EQ(version, "abcd");
+
+  OriginPolicyThrottle::AddExceptionFor(url);
+  OriginPolicyThrottle::ShouldRequestOriginPolicy(url, &version);
+  EXPECT_EQ(version, "0");
+}
+
+TEST_P(OriginPolicyThrottleTest, AddExceptionEndToEnd) {
+  if (!enabled())
+    return;
+
+  OriginPolicyThrottle::AddExceptionFor(GURL("https://example.org/blubb"));
+
+  // Start the navigation.
+  auto navigation = NavigationSimulator::CreateBrowserInitiated(
+      GURL("https://example.org/bla"), web_contents());
+  navigation->SetAutoAdvance(false);
+  navigation->Start();
+  EXPECT_FALSE(navigation->IsDeferred());
+  EXPECT_EQ(NavigationThrottle::PROCEED,
+            navigation->GetLastThrottleCheckResult().action());
+
+  // Fake a response with a policy header.
+  const char* raw_headers = "HTTP/1.1 200 OK\nSec-Origin-Policy: policy-1\n\n";
+  scoped_refptr<net::HttpResponseHeaders> headers =
+      new net::HttpResponseHeaders(
+          net::HttpUtil::AssembleRawHeaders(raw_headers, strlen(raw_headers)));
+  NavigationHandleImpl* nav_handle =
+      static_cast<NavigationHandleImpl*>(navigation->GetNavigationHandle());
+  nav_handle->set_response_headers_for_testing(headers);
+  navigation->ReadyToCommit();
+
+  // Due to the exception, we expect the policy to not defer.
+  EXPECT_FALSE(navigation->IsDeferred());
+
+  // Also check that the header policy did not overwrite the exemption:
+  std::string version;
+  OriginPolicyThrottle::ShouldRequestOriginPolicy(
+      GURL("https://example.org/bla"), &version);
+  EXPECT_EQ(version, "0");
 }
 
 }  // 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 870875d..1ff0eae44 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -6157,6 +6157,12 @@
   DCHECK(committed_request);
   DCHECK(committed_request->navigation_handle());
 
+  // Update the page transition. For subframe navigations, the renderer process
+  // only gives the correct page transition at commit time.
+  // TODO(clamy): We should get the correct page transition when starting the
+  // request.
+  committed_request->set_transition(validated_params->transition);
+
   UpdateSiteURL(validated_params->url, validated_params->url_is_unreachable);
 
   accessibility_reset_count_ = 0;
diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc
index 2b608df..97874df 100644
--- a/content/browser/frame_host/render_frame_host_manager.cc
+++ b/content/browser/frame_host/render_frame_host_manager.cc
@@ -49,6 +49,7 @@
 #include "content/public/browser/render_widget_host_iterator.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/site_isolation_policy.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/referrer.h"
 #include "content/public/common/url_constants.h"
@@ -1393,16 +1394,23 @@
   // OOPIFs; see https://crbug.com/711006.
   //
   // TODO(alexmos): Remove this check after fixing https://crbug.com/787576.
-  if (!frame_tree_node_->IsMainFrame()) {
-    RenderFrameHostImpl* parent =
-        frame_tree_node_->parent()->current_frame_host();
-    bool dest_url_requires_dedicated_process =
-        SiteInstanceImpl::DoesSiteRequireDedicatedProcess(
-            browser_context, parent->GetSiteInstance()->GetIsolationContext(),
-            dest_url);
-    if (!parent->GetSiteInstance()->RequiresDedicatedProcess() &&
-        !dest_url_requires_dedicated_process) {
-      return SiteInstanceDescriptor(parent->GetSiteInstance());
+  //
+  // Also if kProcessSharingWithStrictSiteInstances is enabled, don't lump the
+  // subframe into the same SiteInstance as the parent. These separate
+  // SiteInstances can get assigned to the same process later.
+  if (!base::FeatureList::IsEnabled(
+          features::kProcessSharingWithStrictSiteInstances)) {
+    if (!frame_tree_node_->IsMainFrame()) {
+      RenderFrameHostImpl* parent =
+          frame_tree_node_->parent()->current_frame_host();
+      bool dest_url_requires_dedicated_process =
+          SiteInstanceImpl::DoesSiteRequireDedicatedProcess(
+              browser_context, parent->GetSiteInstance()->GetIsolationContext(),
+              dest_url);
+      if (!parent->GetSiteInstance()->RequiresDedicatedProcess() &&
+          !dest_url_requires_dedicated_process) {
+        return SiteInstanceDescriptor(parent->GetSiteInstance());
+      }
     }
   }
 
@@ -1484,6 +1492,14 @@
     return false;
   }
 
+  // Attempting a transfer with kProcessSharingWithStrictSiteInstances allows
+  // us to "swap" from the renderer's perspective and create the full OOPIF
+  // plumbing, even if this subframe is eventually assigned to the same process
+  // as its cross-site parent.
+  if (base::FeatureList::IsEnabled(
+          features::kProcessSharingWithStrictSiteInstances))
+    return true;
+
   // The sites differ. If either one requires a dedicated process,
   // then a transfer is needed.
   if (rfh->GetSiteInstance()->RequiresDedicatedProcess() ||
diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc
index c2d2c11a..3fa00d9 100644
--- a/content/browser/gpu/gpu_internals_ui.cc
+++ b/content/browser/gpu/gpu_internals_ui.cc
@@ -154,7 +154,7 @@
   auto basic_info = std::make_unique<base::ListValue>();
   basic_info->Append(NewDescriptionValuePair(
       "Initialization time",
-      base::Int64ToString(gpu_info.initialization_time.InMilliseconds())));
+      base::NumberToString(gpu_info.initialization_time.InMilliseconds())));
   basic_info->Append(NewDescriptionValuePair(
       "In-process GPU",
       std::make_unique<base::Value>(gpu_info.in_process_gpu)));
diff --git a/content/browser/indexed_db/indexed_db_database.cc b/content/browser/indexed_db/indexed_db_database.cc
index 145ffe72..e655c64 100644
--- a/content/browser/indexed_db/indexed_db_database.cc
+++ b/content/browser/indexed_db/indexed_db_database.cc
@@ -44,7 +44,7 @@
 #include "url/origin.h"
 
 using base::ASCIIToUTF16;
-using base::Int64ToString16;
+using base::NumberToString16;
 using blink::IndexedDBDatabaseMetadata;
 using blink::IndexedDBIndexKeys;
 using blink::IndexedDBIndexMetadata;
@@ -140,7 +140,7 @@
         } else {
           message =
               ASCIIToUTF16("Internal error opening database with version ") +
-              Int64ToString16(pending_->version);
+              NumberToString16(pending_->version);
         }
         pending_->callbacks->OnError(IndexedDBDatabaseError(
             blink::kWebIDBDatabaseExceptionUnknownError, message));
@@ -190,9 +190,9 @@
       pending_->callbacks->OnError(IndexedDBDatabaseError(
           blink::kWebIDBDatabaseExceptionVersionError,
           ASCIIToUTF16("The requested version (") +
-              Int64ToString16(pending_->version) +
+              NumberToString16(pending_->version) +
               ASCIIToUTF16(") is less than the existing version (") +
-              Int64ToString16(db_->metadata_.version) + ASCIIToUTF16(").")));
+              NumberToString16(db_->metadata_.version) + ASCIIToUTF16(").")));
       db_->RequestComplete(this);
       return;
     }
diff --git a/content/browser/isolated_origin_browsertest.cc b/content/browser/isolated_origin_browsertest.cc
index 0f2f828..d1bbeede 100644
--- a/content/browser/isolated_origin_browsertest.cc
+++ b/content/browser/isolated_origin_browsertest.cc
@@ -1898,4 +1898,238 @@
   EXPECT_EQ(bad_message::RPH_MOJO_PROCESS_ERROR, kill_waiter.Wait());
 }
 
+class IsolatedOriginTestWithStrictSiteInstances : public IsolatedOriginTest {
+ public:
+  IsolatedOriginTestWithStrictSiteInstances() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kProcessSharingWithStrictSiteInstances);
+  }
+  ~IsolatedOriginTestWithStrictSiteInstances() override {}
+
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    IsolatedOriginTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(switches::kDisableSiteIsolation);
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+
+  DISALLOW_COPY_AND_ASSIGN(IsolatedOriginTestWithStrictSiteInstances);
+};
+
+IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,
+                       NonIsolatedFramesCanShareDefaultProcess) {
+  GURL top_url(
+      embedded_test_server()->GetURL("/frame_tree/page_with_two_frames.html"));
+  ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(top_url)));
+  EXPECT_TRUE(NavigateToURL(shell(), top_url));
+
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+  FrameTreeNode* child1 = root->child_at(0);
+  FrameTreeNode* child2 = root->child_at(1);
+
+  GURL bar_url(embedded_test_server()->GetURL("www.bar.com", "/title3.html"));
+  ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(bar_url)));
+  {
+    TestFrameNavigationObserver observer(child1);
+    NavigationHandleObserver handle_observer(web_contents(), bar_url);
+    EXPECT_TRUE(
+        ExecuteScript(child1, "location.href = '" + bar_url.spec() + "';"));
+    observer.Wait();
+  }
+
+  GURL baz_url(embedded_test_server()->GetURL("www.baz.com", "/title3.html"));
+  ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(baz_url)));
+  {
+    TestFrameNavigationObserver observer(child2);
+    NavigationHandleObserver handle_observer(web_contents(), baz_url);
+    EXPECT_TRUE(
+        ExecuteScript(child2, "location.href = '" + baz_url.spec() + "';"));
+    observer.Wait();
+  }
+
+  // All 3 frames are from different sites, so each should have its own
+  // SiteInstance.
+  EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
+            child1->current_frame_host()->GetSiteInstance());
+  EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
+            child2->current_frame_host()->GetSiteInstance());
+  EXPECT_NE(child1->current_frame_host()->GetSiteInstance(),
+            child2->current_frame_host()->GetSiteInstance());
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   |--Site B ------- proxies for A C\n"
+      "   +--Site C ------- proxies for A B\n"
+      "Where A = http://127.0.0.1/\n"
+      "      B = http://bar.com/\n"
+      "      C = http://baz.com/",
+      FrameTreeVisualizer().DepictFrameTree(root));
+
+  // But none are isolated, so all should share the default process for their
+  // BrowsingInstance.
+  RenderProcessHost* host = root->current_frame_host()->GetProcess();
+  EXPECT_EQ(host, child1->current_frame_host()->GetProcess());
+  EXPECT_EQ(host, child2->current_frame_host()->GetProcess());
+  EXPECT_EQ(ChildProcessSecurityPolicyImpl::CheckOriginLockResult::NO_LOCK,
+            ChildProcessSecurityPolicyImpl::GetInstance()->CheckOriginLock(
+                host->GetID(), GURL()));
+}
+
+// Creates a non-isolated main frame with an isolated child and non-isolated
+// grandchild. With strict site isolation disabled and
+// kProcessSharingWithStrictSiteInstances enabled, the main frame and the
+// grandchild should be in the same process even though they have different
+// SiteInstances.
+IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,
+                       IsolatedChildWithNonIsolatedGrandchild) {
+  GURL top_url(
+      embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
+  ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(top_url)));
+  EXPECT_TRUE(NavigateToURL(shell(), top_url));
+
+  GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
+                                                   "/page_with_iframe.html"));
+  ASSERT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
+
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+  FrameTreeNode* child = root->child_at(0);
+
+  NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
+  EXPECT_EQ(child->current_url(), isolated_url);
+
+  // Verify that the child frame is an OOPIF with a different SiteInstance.
+  EXPECT_NE(web_contents()->GetSiteInstance(),
+            child->current_frame_host()->GetSiteInstance());
+  EXPECT_TRUE(child->current_frame_host()->IsCrossProcessSubframe());
+  EXPECT_EQ(GURL("http://isolated.foo.com/"),
+            child->current_frame_host()->GetSiteInstance()->GetSiteURL());
+
+  // Verify that the isolated frame's subframe (which starts out at a relative
+  // path) is kept in the isolated parent's SiteInstance.
+  FrameTreeNode* grandchild = child->child_at(0);
+  EXPECT_EQ(child->current_frame_host()->GetSiteInstance(),
+            grandchild->current_frame_host()->GetSiteInstance());
+
+  // Navigating the grandchild to www.bar.com should put it into the top
+  // frame's process, but not its SiteInstance.
+  GURL non_isolated_url(
+      embedded_test_server()->GetURL("www.bar.com", "/title3.html"));
+  ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(non_isolated_url)));
+  TestFrameNavigationObserver observer(grandchild);
+  EXPECT_TRUE(ExecuteScript(
+      grandchild, "location.href = '" + non_isolated_url.spec() + "';"));
+  observer.Wait();
+  EXPECT_EQ(non_isolated_url, grandchild->current_url());
+
+  EXPECT_NE(root->current_frame_host()->GetSiteInstance(),
+            grandchild->current_frame_host()->GetSiteInstance());
+  EXPECT_NE(child->current_frame_host()->GetSiteInstance(),
+            grandchild->current_frame_host()->GetSiteInstance());
+  EXPECT_EQ(root->current_frame_host()->GetProcess(),
+            grandchild->current_frame_host()->GetProcess());
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   +--Site B ------- proxies for A C\n"
+      "        +--Site C -- proxies for A B\n"
+      "Where A = http://foo.com/\n"
+      "      B = http://isolated.foo.com/\n"
+      "      C = http://bar.com/",
+      FrameTreeVisualizer().DepictFrameTree(root));
+}
+
+// Navigate a frame into and out of an isolated origin. This should not
+// confuse BrowsingInstance into holding onto a stale default_process_.
+IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,
+                       SubframeNavigatesOutofIsolationThenToIsolation) {
+  GURL isolated_url(embedded_test_server()->GetURL("isolated.foo.com",
+                                                   "/page_with_iframe.html"));
+  ASSERT_TRUE(IsIsolatedOrigin(url::Origin::Create(isolated_url)));
+  EXPECT_TRUE(NavigateToURL(shell(), isolated_url));
+
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+  FrameTreeNode* child = root->child_at(0);
+  EXPECT_EQ(web_contents()->GetSiteInstance(),
+            child->current_frame_host()->GetSiteInstance());
+  EXPECT_FALSE(child->current_frame_host()->IsCrossProcessSubframe());
+
+  GURL non_isolated_url(
+      embedded_test_server()->GetURL("www.foo.com", "/title3.html"));
+  ASSERT_FALSE(IsIsolatedOrigin(url::Origin::Create(non_isolated_url)));
+  NavigateIframeToURL(web_contents(), "test_iframe", non_isolated_url);
+  EXPECT_EQ(child->current_url(), non_isolated_url);
+
+  // Verify that the child frame is an OOPIF with a different SiteInstance.
+  EXPECT_NE(web_contents()->GetSiteInstance(),
+            child->current_frame_host()->GetSiteInstance());
+  EXPECT_NE(root->current_frame_host()->GetProcess(),
+            child->current_frame_host()->GetProcess());
+
+  // Navigating the child to the isolated origin again.
+  NavigateIframeToURL(web_contents(), "test_iframe", isolated_url);
+  EXPECT_EQ(child->current_url(), isolated_url);
+  EXPECT_EQ(web_contents()->GetSiteInstance(),
+            child->current_frame_host()->GetSiteInstance());
+
+  // And navigate out of the isolated origin one last time.
+  NavigateIframeToURL(web_contents(), "test_iframe", non_isolated_url);
+  EXPECT_EQ(child->current_url(), non_isolated_url);
+  EXPECT_NE(web_contents()->GetSiteInstance(),
+            child->current_frame_host()->GetSiteInstance());
+  EXPECT_NE(root->current_frame_host()->GetProcess(),
+            child->current_frame_host()->GetProcess());
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://isolated.foo.com/\n"
+      "      B = http://foo.com/",
+      FrameTreeVisualizer().DepictFrameTree(root));
+}
+
+// Ensure a popup and its opener can go in the same process, even though
+// they have different SiteInstances with kProcessSharingWithStrictSiteInstances
+// enabled.
+IN_PROC_BROWSER_TEST_F(IsolatedOriginTestWithStrictSiteInstances,
+                       NonIsolatedPopup) {
+  GURL foo_url(
+      embedded_test_server()->GetURL("www.foo.com", "/page_with_iframe.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), foo_url));
+  FrameTreeNode* root = web_contents()->GetFrameTree()->root();
+
+  // Open a blank popup.
+  ShellAddedObserver new_shell_observer;
+  EXPECT_TRUE(ExecuteScript(root, "window.w = window.open();"));
+  Shell* new_shell = new_shell_observer.GetShell();
+
+  // Have the opener navigate the popup to a non-isolated origin.
+  GURL isolated_url(
+      embedded_test_server()->GetURL("www.bar.com", "/title1.html"));
+  {
+    TestNavigationManager manager(new_shell->web_contents(), isolated_url);
+    EXPECT_TRUE(ExecuteScript(
+        root, "window.w.location.href = '" + isolated_url.spec() + "';"));
+    manager.WaitForNavigationFinished();
+  }
+
+  // The popup and the opener should not share a SiteInstance, but should
+  // end up in the same process.
+  EXPECT_NE(new_shell->web_contents()->GetMainFrame()->GetSiteInstance(),
+            root->current_frame_host()->GetSiteInstance());
+  EXPECT_EQ(root->current_frame_host()->GetProcess(),
+            new_shell->web_contents()->GetMainFrame()->GetProcess());
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site A ------- proxies for B\n"
+      "Where A = http://foo.com/\n"
+      "      B = http://bar.com/",
+      FrameTreeVisualizer().DepictFrameTree(root));
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "Where A = http://bar.com/\n"
+      "      B = http://foo.com/",
+      FrameTreeVisualizer().DepictFrameTree(
+          static_cast<WebContentsImpl*>(new_shell->web_contents())
+              ->GetFrameTree()
+              ->root()));
+}
+
 }  // namespace content
diff --git a/content/browser/loader/prefetch_url_loader.cc b/content/browser/loader/prefetch_url_loader.cc
index 98042ef..32bfe1c 100644
--- a/content/browser/loader/prefetch_url_loader.cc
+++ b/content/browser/loader/prefetch_url_loader.cc
@@ -102,25 +102,34 @@
     }
   }
 
+  DCHECK(loader_);
   loader_->FollowRedirect(removed_headers, modified_request_headers_for_accept,
                           base::nullopt);
 }
 
 void PrefetchURLLoader::ProceedWithResponse() {
-  loader_->ProceedWithResponse();
+  if (loader_)
+    loader_->ProceedWithResponse();
 }
 
 void PrefetchURLLoader::SetPriority(net::RequestPriority priority,
                                     int intra_priority_value) {
-  loader_->SetPriority(priority, intra_priority_value);
+  if (loader_)
+    loader_->SetPriority(priority, intra_priority_value);
 }
 
 void PrefetchURLLoader::PauseReadingBodyFromNet() {
-  loader_->PauseReadingBodyFromNet();
+  // TODO(kinuko): Propagate or handle the case where |loader_| is
+  // detached (for SignedExchanges), see OnReceiveResponse.
+  if (loader_)
+    loader_->PauseReadingBodyFromNet();
 }
 
 void PrefetchURLLoader::ResumeReadingBodyFromNet() {
-  loader_->ResumeReadingBodyFromNet();
+  // TODO(kinuko): Propagate or handle the case where |loader_| is
+  // detached (for SignedExchanges), see OnReceiveResponse.
+  if (loader_)
+    loader_->ResumeReadingBodyFromNet();
 }
 
 void PrefetchURLLoader::OnReceiveResponse(
diff --git a/content/browser/loader/prefetch_url_loader.h b/content/browser/loader/prefetch_url_loader.h
index 3055466..f1143e88 100644
--- a/content/browser/loader/prefetch_url_loader.h
+++ b/content/browser/loader/prefetch_url_loader.h
@@ -120,7 +120,6 @@
   std::unique_ptr<SignedExchangePrefetchHandler>
       signed_exchange_prefetch_handler_;
 
-
   scoped_refptr<SignedExchangePrefetchMetricRecorder>
       signed_exchange_prefetch_metric_recorder_;
 
diff --git a/content/browser/media/capture/desktop_capture_device_unittest.cc b/content/browser/media/capture/desktop_capture_device_unittest.cc
index 23948c7..3a596dc 100644
--- a/content/browser/media/capture/desktop_capture_device_unittest.cc
+++ b/content/browser/media/capture/desktop_capture_device_unittest.cc
@@ -669,7 +669,7 @@
 
   base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
       "--webrtc-max-cpu-consumption-percentage",
-      base::IntToString(max_cpu_consumption_percentage));
+      base::NumberToString(max_cpu_consumption_percentage));
 
   const double actual_framerate = CaptureFrames();
 
diff --git a/content/browser/media/encrypted_media_browsertest.cc b/content/browser/media/encrypted_media_browsertest.cc
index 6dddb58..1f1f059c 100644
--- a/content/browser/media/encrypted_media_browsertest.cc
+++ b/content/browser/media/encrypted_media_browsertest.cc
@@ -102,7 +102,7 @@
     query_params.emplace_back("keySystem", CurrentKeySystem());
     query_params.emplace_back(
         "configChangeType",
-        base::IntToString(static_cast<int>(config_change_type)));
+        base::NumberToString(static_cast<int>(config_change_type)));
     RunMediaTestPage("mse_config_change.html", query_params, media::kEnded,
                      true);
   }
diff --git a/content/browser/media/media_browsertest.cc b/content/browser/media/media_browsertest.cc
index 8c47e38..38404f1 100644
--- a/content/browser/media/media_browsertest.cc
+++ b/content/browser/media/media_browsertest.cc
@@ -135,9 +135,9 @@
 
   void RunVideoSizeTest(const char* media_file, int width, int height) {
     std::string expected;
-    expected += base::IntToString(width);
+    expected += base::NumberToString(width);
     expected += " ";
-    expected += base::IntToString(height);
+    expected += base::NumberToString(height);
     base::StringPairs query_params;
     query_params.emplace_back("video", media_file);
     RunMediaTestPage("player.html", query_params, expected, false);
diff --git a/content/browser/media/media_internals.cc b/content/browser/media/media_internals.cc
index 11bfd1e0..e88f2a0 100644
--- a/content/browser/media/media_internals.cc
+++ b/content/browser/media/media_internals.cc
@@ -79,7 +79,7 @@
   if (effects) {
     if (!ret.empty())
       ret += " | ";
-    ret += base::IntToString(effects);
+    ret += base::NumberToString(effects);
   }
 
   return ret;
diff --git a/content/browser/media/session/media_session_impl_browsertest.cc b/content/browser/media/session/media_session_impl_browsertest.cc
index 8ee483b2..6bf0783 100644
--- a/content/browser/media/session/media_session_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_impl_browsertest.cc
@@ -743,7 +743,7 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_TRUE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(true);
   }
 
   EXPECT_TRUE(IsControllable());
@@ -763,7 +763,7 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
   }
 
   EXPECT_FALSE(IsControllable());
@@ -787,7 +787,7 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
   }
 
   EXPECT_FALSE(IsControllable());
@@ -811,17 +811,15 @@
     SetPlaybackState(blink::mojom::MediaSessionPlaybackState::NONE);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
   }
 
   EXPECT_FALSE(IsControllable());
   EXPECT_TRUE(IsActive());
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
-IN_PROC_BROWSER_TEST_P(
-    MediaSessionImplParamBrowserTest,
-    DISABLED_ControlsShowForTransientAndPlaybackStatePaused) {
+IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
+                       ControlsShowForTransientAndPlaybackStatePaused) {
   EnsureMediaSessionService();
   auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(
       shell()->web_contents()->GetMainFrame());
@@ -837,17 +835,15 @@
     SetPlaybackState(blink::mojom::MediaSessionPlaybackState::PAUSED);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_TRUE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(true);
   }
 
   EXPECT_TRUE(IsControllable());
   EXPECT_TRUE(IsActive());
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
-IN_PROC_BROWSER_TEST_P(
-    MediaSessionImplParamBrowserTest,
-    DISABLED_ControlsShowForTransientAndPlaybackStatePlaying) {
+IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
+                       ControlsShowForTransientAndPlaybackStatePlaying) {
   EnsureMediaSessionService();
   auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>(
       shell()->web_contents()->GetMainFrame());
@@ -863,7 +859,7 @@
     SetPlaybackState(blink::mojom::MediaSessionPlaybackState::PLAYING);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_TRUE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(true);
   }
 
   EXPECT_TRUE(IsControllable());
@@ -883,8 +879,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -895,8 +891,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kInactive);
+    observer.WaitForControllable(false);
 
-    EXPECT_FALSE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -916,8 +912,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -929,8 +925,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -950,8 +946,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(false);
 
-    EXPECT_FALSE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -964,8 +960,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -985,8 +981,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -997,8 +993,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1011,8 +1007,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1033,8 +1029,7 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-
-    EXPECT_TRUE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(true);
   }
 
   RemovePlayer(player_observer.get(), 0);
@@ -1042,7 +1037,7 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_TRUE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(true);
   }
 
   EXPECT_TRUE(IsControllable());
@@ -1053,7 +1048,7 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kInactive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
   }
 
   EXPECT_FALSE(IsControllable());
@@ -1072,8 +1067,7 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-
-    EXPECT_TRUE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(true);
   }
 
   RemovePlayers(player_observer.get());
@@ -1081,7 +1075,7 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kInactive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
   }
 
   EXPECT_FALSE(IsControllable());
@@ -1100,8 +1094,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1111,8 +1105,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1125,8 +1119,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -1146,8 +1140,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1157,8 +1151,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -1178,8 +1172,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1191,8 +1185,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1212,8 +1206,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1222,10 +1216,9 @@
 
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
-
     observer.WaitForState(MediaSessionInfo::SessionState::kInactive);
+    observer.WaitForControllable(false);
 
-    EXPECT_FALSE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -1245,8 +1238,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1257,8 +1250,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kInactive);
+    observer.WaitForControllable(false);
 
-    EXPECT_FALSE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -1278,8 +1271,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1289,8 +1282,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -1304,8 +1297,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(false);
 
-    EXPECT_FALSE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1325,8 +1318,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1336,8 +1329,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -1350,8 +1343,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1371,8 +1364,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1382,8 +1375,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -1396,8 +1389,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1417,8 +1410,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1428,8 +1421,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -1449,8 +1442,8 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1460,8 +1453,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kSuspended);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPaused,
               observer.session_info()->playback_state);
   }
@@ -1471,8 +1464,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1485,8 +1478,8 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
+    observer.WaitForControllable(true);
 
-    EXPECT_TRUE(observer.session_info()->is_controllable);
     EXPECT_EQ(MediaPlaybackState::kPlaying,
               observer.session_info()->playback_state);
   }
@@ -1495,9 +1488,8 @@
   EXPECT_TRUE(IsActive());
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
 IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
-                       DISABLED_ControlsDontShowWhenOneShotIsPresent) {
+                       ControlsDontShowWhenOneShotIsPresent) {
   auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>();
 
   {
@@ -1507,7 +1499,7 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
 
     EXPECT_FALSE(IsControllable());
     EXPECT_TRUE(IsActive());
@@ -1519,7 +1511,7 @@
     StartNewPlayer(player_observer.get(), media::MediaContentType::Transient);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
 
     EXPECT_FALSE(IsControllable());
     EXPECT_TRUE(IsActive());
@@ -1531,17 +1523,15 @@
     StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent);
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
 
     EXPECT_FALSE(IsControllable());
     EXPECT_TRUE(IsActive());
   }
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
-IN_PROC_BROWSER_TEST_P(
-    MediaSessionImplParamBrowserTest,
-    DISABLED_ControlsHiddenAfterRemoveOneShotWithoutOtherPlayers) {
+IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
+                       ControlsHiddenAfterRemoveOneShotWithoutOtherPlayers) {
   auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>();
 
   {
@@ -1551,7 +1541,7 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
   }
 
   RemovePlayer(player_observer.get(), 0);
@@ -1559,17 +1549,15 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kInactive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
   }
 
   EXPECT_FALSE(IsControllable());
   EXPECT_FALSE(IsActive());
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
-IN_PROC_BROWSER_TEST_P(
-    MediaSessionImplParamBrowserTest,
-    DISABLED_ControlsShowAfterRemoveOneShotWithPersistentPresent) {
+IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
+                       ControlsShowAfterRemoveOneShotWithPersistentPresent) {
   auto player_observer = std::make_unique<MockMediaSessionPlayerObserver>();
 
   {
@@ -1581,7 +1569,7 @@
     ResolveAudioFocusSuccess();
 
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_FALSE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(false);
   }
 
   RemovePlayer(player_observer.get(), 0);
@@ -1589,7 +1577,7 @@
   {
     media_session::test::MockMediaSessionMojoObserver observer(*media_session_);
     observer.WaitForState(MediaSessionInfo::SessionState::kActive);
-    EXPECT_TRUE(observer.session_info()->is_controllable);
+    observer.WaitForControllable(true);
   }
 
   EXPECT_TRUE(IsControllable());
@@ -2187,13 +2175,11 @@
 
   media_session::MediaMetadata expected_metadata;
   expected_metadata.source_title = GetExpectedSourceTitle();
-  EXPECT_EQ(expected_metadata, observer.WaitForMetadata());
+  observer.WaitForExpectedMetadata(expected_metadata);
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
-IN_PROC_BROWSER_TEST_P(
-    MediaSessionImplParamBrowserTest,
-    DISABLED_AddingMojoObserverNotifiesCurrentInformation_WithInfo) {
+IN_PROC_BROWSER_TEST_P(MediaSessionImplParamBrowserTest,
+                       AddingMojoObserverNotifiesCurrentInformation_WithInfo) {
   // Set up the service and information.
   EnsureMediaSessionService();
 
@@ -2219,7 +2205,7 @@
     StartNewPlayer(player_observer.get(), media::MediaContentType::Persistent);
     ResolveAudioFocusSuccess();
 
-    EXPECT_EQ(expected_metadata, observer.WaitForNonEmptyMetadata());
+    observer.WaitForExpectedMetadata(expected_metadata);
   }
 }
 
diff --git a/content/browser/media/session/media_session_impl_service_routing_unittest.cc b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
index 683e4759..5e796af 100644
--- a/content/browser/media/session/media_session_impl_service_routing_unittest.cc
+++ b/content/browser/media/session/media_session_impl_service_routing_unittest.cc
@@ -80,9 +80,11 @@
         std::make_unique<content::TestServiceManagerContext>();
 
     contents()->GetMainFrame()->InitializeRenderFrameIfNeeded();
+    contents()->NavigateAndCommit(GURL("http://www.example.com"));
+
     main_frame_ = contents()->GetMainFrame();
     sub_frame_ = main_frame_->AppendChild("sub_frame");
-    empty_metadata_.source_title = GetExpectedSourceTitle();
+    empty_metadata_.source_title = base::ASCIIToUTF16("http://www.example.com");
   }
 
   void TearDown() override {
@@ -128,11 +130,6 @@
         ->RemovePlayer(players_[frame].get(), kPlayerId);
   }
 
-  base::string16 GetExpectedSourceTitle() {
-    return base::ASCIIToUTF16(
-        contents()->GetLastCommittedURL().GetOrigin().host());
-  }
-
   MockMediaSessionPlayerObserver* GetPlayerForFrame(
       TestRenderFrameHost* frame) {
     auto iter = players_.find(frame);
@@ -280,20 +277,17 @@
   services_[main_frame_]->SetMetadata(nullptr);
   services_[main_frame_]->EnableAction(MediaSessionAction::kPlay);
 
-  observer.WaitForActions();
-  EXPECT_TRUE(observer.actions().empty());
-
-  EXPECT_TRUE(observer.WaitForMetadata()->IsEmpty());
+  observer.WaitForEmptyActions();
+  observer.WaitForEmptyMetadata();
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
 TEST_F(MediaSessionImplServiceRoutingTest,
-       DISABLED_NotifyMetadataAndActionsChangeWhenControllable) {
+       NotifyMetadataAndActionsChangeWhenControllable) {
   media_session::MediaMetadata expected_metadata;
   expected_metadata.title = base::ASCIIToUTF16("title");
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
-  expected_metadata.source_title = GetExpectedSourceTitle();
+  expected_metadata.source_title = empty_metadata().source_title;
 
   CreateServiceForFrame(main_frame_);
   StartPlayerForFrame(main_frame_);
@@ -302,10 +296,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(
         *GetMediaSession());
 
-    observer.WaitForActions();
-
-    EXPECT_EQ(default_actions(), observer.actions_set());
-    EXPECT_EQ(empty_metadata(), observer.WaitForNonEmptyMetadata());
+    observer.WaitForExpectedActions(default_actions());
+    observer.WaitForExpectedMetadata(empty_metadata());
   }
 
   {
@@ -321,22 +313,19 @@
     services_[main_frame_]->SetMetadata(std::move(spec_metadata));
     services_[main_frame_]->EnableAction(MediaSessionAction::kSeekForward);
 
-    observer.WaitForActions();
-
-    EXPECT_EQ(GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward),
-              observer.actions_set());
-    EXPECT_EQ(expected_metadata, observer.WaitForNonEmptyMetadata());
+    observer.WaitForExpectedMetadata(expected_metadata);
+    observer.WaitForExpectedActions(
+        GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward));
   }
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
 TEST_F(MediaSessionImplServiceRoutingTest,
-       DISABLED_NotifyMetadataAndActionsChangeWhenTurningControllable) {
+       NotifyMetadataAndActionsChangeWhenTurningControllable) {
   media_session::MediaMetadata expected_metadata;
   expected_metadata.title = base::ASCIIToUTF16("title");
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
-  expected_metadata.source_title = GetExpectedSourceTitle();
+  expected_metadata.source_title = empty_metadata().source_title;
 
   CreateServiceForFrame(main_frame_);
 
@@ -356,10 +345,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(
         *GetMediaSession());
 
-    observer.WaitForActions();
-
-    EXPECT_TRUE(observer.actions_set().empty());
-    EXPECT_EQ(empty_metadata(), observer.WaitForNonEmptyMetadata());
+    observer.WaitForEmptyActions();
+    observer.WaitForExpectedMetadata(empty_metadata());
   }
 
   {
@@ -368,22 +355,19 @@
 
     StartPlayerForFrame(main_frame_);
 
-    observer.WaitForActions();
-
-    EXPECT_EQ(GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward),
-              observer.actions_set());
-    EXPECT_EQ(expected_metadata, observer.WaitForNonEmptyMetadata());
+    observer.WaitForExpectedMetadata(expected_metadata);
+    observer.WaitForExpectedActions(
+        GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward));
   }
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
 TEST_F(MediaSessionImplServiceRoutingTest,
-       DISABLED_NotifyActionsAndMetadataChangeWhenTurningUncontrollable) {
+       NotifyActionsAndMetadataChangeWhenTurningUncontrollable) {
   media_session::MediaMetadata expected_metadata;
   expected_metadata.title = base::ASCIIToUTF16("title");
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
-  expected_metadata.source_title = GetExpectedSourceTitle();
+  expected_metadata.source_title = empty_metadata().source_title;
 
   CreateServiceForFrame(main_frame_);
 
@@ -403,10 +387,8 @@
     media_session::test::MockMediaSessionMojoObserver observer(
         *GetMediaSession());
 
-    observer.WaitForActions();
-
-    EXPECT_EQ(default_actions(), observer.actions_set());
-    EXPECT_EQ(expected_metadata, observer.WaitForNonEmptyMetadata());
+    observer.WaitForExpectedActions(default_actions());
+    observer.WaitForExpectedMetadata(expected_metadata);
   }
 
   {
@@ -415,10 +397,8 @@
 
     ClearPlayersForFrame(main_frame_);
 
-    observer.WaitForActions();
-
-    EXPECT_TRUE(observer.actions_set().empty());
-    EXPECT_EQ(empty_metadata(), observer.WaitForNonEmptyMetadata());
+    observer.WaitForEmptyActions();
+    observer.WaitForExpectedMetadata(empty_metadata());
   }
 }
 
@@ -592,7 +572,7 @@
   expected_metadata.title = base::ASCIIToUTF16("title");
   expected_metadata.artist = base::ASCIIToUTF16("artist");
   expected_metadata.album = base::ASCIIToUTF16("album");
-  expected_metadata.source_title = GetExpectedSourceTitle();
+  expected_metadata.source_title = empty_metadata().source_title;
 
   CreateServiceForFrame(main_frame_);
   StartPlayerForFrame(main_frame_);
@@ -609,7 +589,7 @@
 
     services_[main_frame_]->SetMetadata(std::move(spec_metadata));
 
-    EXPECT_EQ(expected_metadata, observer.WaitForNonEmptyMetadata());
+    observer.WaitForExpectedMetadata(expected_metadata);
   }
 }
 
@@ -618,9 +598,6 @@
   CreateServiceForFrame(main_frame_);
   StartPlayerForFrame(main_frame_);
 
-  media_session::MediaMetadata expected_metadata;
-  expected_metadata.source_title = GetExpectedSourceTitle();
-
   {
     media_session::test::MockMediaSessionMojoObserver observer(
         *GetMediaSession());
@@ -628,7 +605,7 @@
 
     // When the session becomes controllable we should receive default
     // metadata. The |is_controllable| boolean will also become true.
-    EXPECT_EQ(expected_metadata, observer.WaitForMetadata());
+    observer.WaitForExpectedMetadata(empty_metadata());
     EXPECT_TRUE(observer.session_info()->is_controllable);
   }
 }
@@ -659,15 +636,11 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-
-  EXPECT_EQ(GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward),
-            observer.actions_set());
+  observer.WaitForExpectedActions(
+      GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward));
 
   services_[main_frame_]->DisableAction(MediaSessionAction::kSeekForward);
-  observer.WaitForActions();
-
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest, DefaultActionsAlwaysSupported) {
@@ -678,34 +651,26 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 
   services_[main_frame_]->DisableAction(MediaSessionAction::kPlay);
 
   // This will cause the observer to be flushed with the latest actions and
   // kPlay should still be there even though we disabled it.
   services_[main_frame_]->EnableAction(MediaSessionAction::kSeekForward);
-  observer.WaitForActions();
-
-  EXPECT_EQ(GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward),
-            observer.actions_set());
+  observer.WaitForExpectedActions(
+      GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward));
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
 TEST_F(MediaSessionImplServiceRoutingTest,
-       DISABLED_DefaultActionsRemovedIfUncontrollable) {
+       DefaultActionsRemovedIfUncontrollable) {
   CreateServiceForFrame(main_frame_);
   StartPlayerForFrame(main_frame_, media::MediaContentType::OneShot);
 
   {
     media_session::test::MockMediaSessionMojoObserver observer(
         *GetMediaSession());
-    observer.WaitForActions();
-
-    std::set<MediaSessionAction> expected_actions;
-    EXPECT_EQ(expected_actions, observer.actions_set());
+    observer.WaitForEmptyActions();
   }
 
   {
@@ -713,11 +678,10 @@
         *GetMediaSession());
 
     services_[main_frame_]->EnableAction(MediaSessionAction::kPlay);
-    observer.WaitForActions();
 
     std::set<MediaSessionAction> expected_actions;
     expected_actions.insert(MediaSessionAction::kPlay);
-    EXPECT_EQ(expected_actions, observer.actions_set());
+    observer.WaitForExpectedActions(expected_actions);
   }
 }
 
@@ -728,7 +692,7 @@
 
   media_session::MediaMetadata expected_metadata;
   expected_metadata.source_title = base::ASCIIToUTF16("http://www.google.com");
-  EXPECT_EQ(expected_metadata, observer.WaitForNonEmptyMetadata());
+  observer.WaitForExpectedMetadata(expected_metadata);
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest,
@@ -739,9 +703,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest,
@@ -753,9 +715,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplServiceRoutingTest,
@@ -769,18 +729,14 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-
-  EXPECT_EQ(GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward),
-            observer.actions_set());
+  observer.WaitForExpectedActions(
+      GetDefaultActionsWithExtra(MediaSessionAction::kSeekForward));
 
   DestroyServiceForFrame(main_frame_);
 
   EXPECT_EQ(nullptr, ComputeServiceForRouting());
 
-  observer.WaitForActions();
-
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 }  // namespace content
diff --git a/content/browser/media/session/media_session_impl_unittest.cc b/content/browser/media/session/media_session_impl_unittest.cc
index 8fe6cdf..e559d9d 100644
--- a/content/browser/media/session/media_session_impl_unittest.cc
+++ b/content/browser/media/session/media_session_impl_unittest.cc
@@ -352,8 +352,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplTest, SuspendContent_WithAction) {
@@ -371,8 +370,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplTest, SuspendSystem_WithAction) {
@@ -390,8 +388,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplTest, SuspendUI_WithAction) {
@@ -408,8 +405,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplTest, ResumeUI) {
@@ -425,8 +421,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplTest, ResumeContent_WithAction) {
@@ -444,8 +439,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplTest, ResumeSystem_WithAction) {
@@ -463,8 +457,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 TEST_F(MediaSessionImplTest, ResumeUI_WithAction) {
@@ -482,8 +475,7 @@
 
   media_session::test::MockMediaSessionMojoObserver observer(
       *GetMediaSession());
-  observer.WaitForActions();
-  EXPECT_EQ(default_actions(), observer.actions_set());
+  observer.WaitForExpectedActions(default_actions());
 }
 
 #if !defined(OS_ANDROID)
diff --git a/content/browser/media/session/media_session_service_impl_browsertest.cc b/content/browser/media/session/media_session_service_impl_browsertest.cc
index edd9e47..ab961606 100644
--- a/content/browser/media/session/media_session_service_impl_browsertest.cc
+++ b/content/browser/media/session/media_session_service_impl_browsertest.cc
@@ -110,10 +110,15 @@
   bool ExecuteScriptToSetUpMediaSessionSync() {
     bool result = ExecuteScript(shell(), kSetUpMediaSessionScript);
     media_session::test::MockMediaSessionMojoObserver observer(*GetSession());
-    observer.WaitForActions();
-    EXPECT_TRUE(base::ContainsKey(
-        observer.actions_set(),
-        media_session::mojom::MediaSessionAction::kSeekForward));
+
+    std::set<media_session::mojom::MediaSessionAction> expected_actions;
+    expected_actions.insert(media_session::mojom::MediaSessionAction::kPlay);
+    expected_actions.insert(media_session::mojom::MediaSessionAction::kPause);
+    expected_actions.insert(media_session::mojom::MediaSessionAction::kStop);
+    expected_actions.insert(
+        media_session::mojom::MediaSessionAction::kSeekForward);
+
+    observer.WaitForExpectedActions(expected_actions);
     return result;
   }
 
@@ -146,7 +151,7 @@
 // TODO(crbug.com/850870) Plug the leaks.
 #define MAYBE_ResetServiceWhenNavigatingAway \
   DISABLED_ResetServiceWhenNavigatingAway
-#elif defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX)
+#elif defined(OS_CHROMEOS) || defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_ANDROID)
 // crbug.com/927234.
 #define MAYBE_ResetServiceWhenNavigatingAway \
   DISABLED_ResetServiceWhenNavigatingAway
diff --git a/content/browser/net/accept_header_browsertest.cc b/content/browser/net/accept_header_browsertest.cc
index 54bcbf5..b324b95 100644
--- a/content/browser/net/accept_header_browsertest.cc
+++ b/content/browser/net/accept_header_browsertest.cc
@@ -96,13 +96,13 @@
   // RESOURCE_TYPE_MAIN_FRAME
   EXPECT_EQ(
       "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,"
-      "image/apng,*/*;q=0.8",
+      "image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
       GetFor("/accept-header.html"));
 
   // RESOURCE_TYPE_SUB_FRAME
   EXPECT_EQ(
       "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,"
-      "image/apng,*/*;q=0.8",
+      "image/apng,*/*;q=0.8,application/signed-exchange;v=b3",
       GetFor("/iframe.html"));
 
   // RESOURCE_TYPE_STYLESHEET
@@ -131,7 +131,8 @@
 #endif
 
   // RESOURCE_TYPE_PREFETCH
-  EXPECT_EQ("*/*", GetFor("/prefetch"));
+  EXPECT_EQ("application/signed-exchange;v=b3;q=0.9,*/*;q=0.8",
+            GetFor("/prefetch"));
 
   // RESOURCE_TYPE_XHR
   EXPECT_EQ("*/*", GetFor("/xhr"));
diff --git a/content/browser/net_info_browsertest.cc b/content/browser/net_info_browsertest.cc
index 56ab286..b94d463a4 100644
--- a/content/browser/net_info_browsertest.cc
+++ b/content/browser/net_info_browsertest.cc
@@ -413,7 +413,7 @@
   for (size_t i = 0; i < 10; ++i) {
     // The noise added is a function of the hostname. Varying the hostname
     // should vary the noise.
-    std::string fake_hostname = "example" + base::IntToString(i) + ".com";
+    std::string fake_hostname = "example" + base::NumberToString(i) + ".com";
     EXPECT_TRUE(NavigateToURL(shell(), embedded_test_server()->GetURL(
                                            fake_hostname, "/net_info.html")));
     VerifyRtt(http_rtt, RunScriptExtractInt("getRtt()"));
diff --git a/content/browser/notifications/notification_event_dispatcher_impl.cc b/content/browser/notifications/notification_event_dispatcher_impl.cc
index bff772c5..f423da16 100644
--- a/content/browser/notifications/notification_event_dispatcher_impl.cc
+++ b/content/browser/notifications/notification_event_dispatcher_impl.cc
@@ -443,13 +443,9 @@
     // from the JavaScript point of view there will be two notification objects,
     // and the old one needs to receive a close event before the new one
     // receives a show event.
-    DispatchNonPersistentCloseEvent(
-        notification_id,
-        base::BindOnce(&NotificationEventDispatcherImpl::
-                           ReplaceNonPersistentNotificationListener,
-                       base::Unretained(this), notification_id,
-                       std::move(event_listener_ptr)));
-    return;
+    non_persistent_notification_listeners_[notification_id]->OnClose(
+        base::DoNothing());
+    non_persistent_notification_listeners_.erase(notification_id);
   }
 
   non_persistent_notification_listeners_.emplace(notification_id,
@@ -497,13 +493,6 @@
   std::move(completed_closure).Run();
 }
 
-void NotificationEventDispatcherImpl::ReplaceNonPersistentNotificationListener(
-    const std::string& notification_id,
-    blink::mojom::NonPersistentNotificationListenerPtr event_listener_ptr) {
-  non_persistent_notification_listeners_.emplace(notification_id,
-                                                 std::move(event_listener_ptr));
-}
-
 void NotificationEventDispatcherImpl::
     HandleConnectionErrorForNonPersistentNotificationListener(
         const std::string& notification_id) {
diff --git a/content/browser/notifications/notification_event_dispatcher_impl.h b/content/browser/notifications/notification_event_dispatcher_impl.h
index dd2c10e..230c56d 100644
--- a/content/browser/notifications/notification_event_dispatcher_impl.h
+++ b/content/browser/notifications/notification_event_dispatcher_impl.h
@@ -67,13 +67,6 @@
   void OnNonPersistentCloseComplete(const std::string& notification_id,
                                     base::OnceClosure completed_closure);
 
-  // Replace listener for existing non-persistent notification.
-  // This method is called after a non-persistent notification has
-  // been replaced and |OnNonPersistentCloseComplete| is executed.
-  void ReplaceNonPersistentNotificationListener(
-      const std::string& notification_id,
-      blink::mojom::NonPersistentNotificationListenerPtr event_listener_ptr);
-
   // Removes all references to the listener registered to receive events
   // from the non-persistent notification identified by |notification_id|.
   // Should be called when the connection to this listener goes away.
diff --git a/content/browser/notifications/notification_event_dispatcher_impl_unittest.cc b/content/browser/notifications/notification_event_dispatcher_impl_unittest.cc
index cbc8b8b..325b65f1 100644
--- a/content/browser/notifications/notification_event_dispatcher_impl_unittest.cc
+++ b/content/browser/notifications/notification_event_dispatcher_impl_unittest.cc
@@ -138,6 +138,30 @@
 }
 
 TEST_F(NotificationEventDispatcherImplTest,
+       RegisterNonPersistentListener_SecondListenerGetsOnShow) {
+  auto original_listener = std::make_unique<TestNotificationListener>();
+  dispatcher_->RegisterNonPersistentNotificationListener(
+      kPrimaryUniqueId, original_listener->GetPtr());
+
+  dispatcher_->DispatchNonPersistentShowEvent(kPrimaryUniqueId);
+
+  WaitForMojoTasksToComplete();
+
+  ASSERT_EQ(original_listener->on_show_count(), 1);
+
+  auto replacement_listener = std::make_unique<TestNotificationListener>();
+  dispatcher_->RegisterNonPersistentNotificationListener(
+      kPrimaryUniqueId, replacement_listener->GetPtr());
+
+  dispatcher_->DispatchNonPersistentShowEvent(kPrimaryUniqueId);
+
+  WaitForMojoTasksToComplete();
+
+  ASSERT_EQ(original_listener->on_show_count(), 1);
+  ASSERT_EQ(replacement_listener->on_show_count(), 1);
+}
+
+TEST_F(NotificationEventDispatcherImplTest,
        RegisterNonPersistentListener_ReplacedListenerGetsOnClick) {
   auto original_listener = std::make_unique<TestNotificationListener>();
   dispatcher_->RegisterNonPersistentNotificationListener(
diff --git a/content/browser/notifications/notification_id_generator.cc b/content/browser/notifications/notification_id_generator.cc
index 9ab34c9..68298dd 100644
--- a/content/browser/notifications/notification_id_generator.cc
+++ b/content/browser/notifications/notification_id_generator.cc
@@ -50,11 +50,11 @@
   stream << origin;
   stream << kNotificationTagSeparator;
 
-  stream << base::IntToString(!tag.empty());
+  stream << base::NumberToString(!tag.empty());
   if (tag.size())
     stream << tag;
   else
-    stream << base::Int64ToString(persistent_notification_id);
+    stream << base::NumberToString(persistent_notification_id);
 
   return stream.str();
 }
diff --git a/content/browser/notifications/notification_id_generator_unittest.cc b/content/browser/notifications/notification_id_generator_unittest.cc
index 5cd5460..b803113 100644
--- a/content/browser/notifications/notification_id_generator_unittest.cc
+++ b/content/browser/notifications/notification_id_generator_unittest.cc
@@ -80,7 +80,7 @@
 TEST_F(NotificationIdGeneratorTest, GenerateForPersistent_NumericTagAmbiguity) {
   EXPECT_NE(
       generator_.GenerateForPersistentNotification(
-          origin_.GetURL(), base::Int64ToString(kPersistentNotificationId),
+          origin_.GetURL(), base::NumberToString(kPersistentNotificationId),
           kPersistentNotificationId),
       generator_.GenerateForPersistentNotification(
           origin_.GetURL(), "" /* tag */, kPersistentNotificationId));
diff --git a/content/browser/payments/payment_app_content_unittest_base.cc b/content/browser/payments/payment_app_content_unittest_base.cc
index 78f2910..5194da83 100644
--- a/content/browser/payments/payment_app_content_unittest_base.cc
+++ b/content/browser/payments/payment_app_content_unittest_base.cc
@@ -69,7 +69,7 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc
index 6fbef65d..2d57937 100644
--- a/content/browser/ppapi_plugin_process_host.cc
+++ b/content/browser/ppapi_plugin_process_host.cc
@@ -408,10 +408,10 @@
   const gfx::FontRenderParams font_params =
       gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr);
   cmd_line->AppendSwitchASCII(switches::kPpapiAntialiasedTextEnabled,
-                              base::IntToString(font_params.antialiasing));
+                              base::NumberToString(font_params.antialiasing));
   cmd_line->AppendSwitchASCII(
       switches::kPpapiSubpixelRenderingSetting,
-      base::IntToString(font_params.subpixel_rendering));
+      base::NumberToString(font_params.subpixel_rendering));
 #endif
 
   if (!plugin_launcher.empty())
diff --git a/content/browser/renderer_host/input/touch_action_filter.cc b/content/browser/renderer_host/input/touch_action_filter.cc
index 18bb05e..6cacb57 100644
--- a/content/browser/renderer_host/input/touch_action_filter.cc
+++ b/content/browser/renderer_host/input/touch_action_filter.cc
@@ -361,6 +361,7 @@
           gesture_sequence_.erase(gesture_sequence_.begin(),
                                   gesture_sequence_.end() - 256);
         base::debug::SetCrashKeyString(crash_key, gesture_sequence_);
+        base::debug::DumpWithoutCrashing();
         gesture_sequence_.clear();
       }
       // TODO(xidachen): investigate why the touch action has no value.
diff --git a/content/browser/renderer_host/native_web_keyboard_event_mac_unittest.mm b/content/browser/renderer_host/native_web_keyboard_event_mac_unittest.mm
index f30a913..2440fca 100644
--- a/content/browser/renderer_host/native_web_keyboard_event_mac_unittest.mm
+++ b/content/browser/renderer_host/native_web_keyboard_event_mac_unittest.mm
@@ -7,33 +7,29 @@
 #import <AppKit/AppKit.h>
 #import <Carbon/Carbon.h>
 
-#include "base/mac/scoped_nsobject.h"
 #include "content/browser/renderer_host/input/web_input_event_builders_mac.h"
 #import "testing/gtest_mac.h"
 #include "ui/events/blink/web_input_event.h"
 
 // Going from NSEvent to WebKeyboardEvent and back should round trip.
 TEST(NativeWebKeyboardEventMac, CtrlCmdSpaceKeyDownRoundTrip) {
-  base::scoped_nsobject<NSWindow> window([NSWindow alloc]);
-
   NSEvent* ns_event =
-      [[NSEvent keyEventWithType:NSEventTypeKeyDown
+      [NSEvent keyEventWithType:NSEventTypeKeyDown
                              location:NSZeroPoint
                         modifierFlags:NSEventModifierFlagControl |
                                       NSEventModifierFlagCommand
                             timestamp:0
-                         windowNumber:[window windowNumber]
+                         windowNumber:0
                               context:nil
                            characters:@"\0"  // The control modifier results in
                                              // ' ' being bitmasked with 0x1F
                                              // which == 0x00.
           charactersIgnoringModifiers:@" "
                             isARepeat:NO
-                              keyCode:kVK_Space] retain];
+                              keyCode:kVK_Space];
   blink::WebKeyboardEvent web_event =
       content::WebKeyboardEventBuilder::Build(ns_event);
-  content::NativeWebKeyboardEvent native_event(
-      web_event, gfx::NativeView([window contentView]));
+  content::NativeWebKeyboardEvent native_event(web_event, gfx::NativeView());
 
   NSEvent* round_trip_ns_event = native_event.os_event;
   EXPECT_EQ([round_trip_ns_event type], [ns_event type]);
diff --git a/content/browser/renderer_host/pepper/quota_reservation_unittest.cc b/content/browser/renderer_host/pepper/quota_reservation_unittest.cc
index e75c196..36729eb20 100644
--- a/content/browser/renderer_host/pepper/quota_reservation_unittest.cc
+++ b/content/browser/renderer_host/pepper/quota_reservation_unittest.cc
@@ -97,7 +97,7 @@
   storage::FileSystemURL MakeFileSystemURL(
       const base::FilePath::StringType& file_name) {
     return storage::FileSystemURL::CreateForTest(
-        GURL(kOrigin), kType, MakeFilePath(file_name));
+        url::Origin::Create(GURL(kOrigin)), kType, MakeFilePath(file_name));
   }
 
   scoped_refptr<QuotaReservation> CreateQuotaReservation(
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 90c0a16..9244b9a1 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -71,7 +71,6 @@
 #include "components/tracing/common/tracing_switches.h"
 #include "components/viz/common/switches.h"
 #include "components/viz/host/gpu_client.h"
-#include "content/browser/appcache/appcache_dispatcher_host.h"
 #include "content/browser/appcache/chrome_appcache_service.h"
 #include "content/browser/background_fetch/background_fetch_context.h"
 #include "content/browser/background_fetch/background_fetch_service_impl.h"
@@ -276,9 +275,9 @@
 #endif
 
 #if defined(OS_WIN)
-#define IntToStringType base::IntToString16
+#define NumberToStringType base::NumberToString16
 #else
-#define IntToStringType base::IntToString
+#define NumberToStringType base::NumberToString
 #endif
 
 namespace content {
@@ -2242,7 +2241,7 @@
 #endif  // BUILDFLAG(ENABLE_REPORTING)
 
   registry->AddInterface(base::BindRepeating(
-      &AppCacheDispatcherHost::Create,
+      &ChromeAppCacheService::CreateBackend,
       base::Unretained(storage_partition_impl_->GetAppCacheService()),
       GetID()));
 
@@ -2913,12 +2912,12 @@
 static void AppendCompositorCommandLineFlags(base::CommandLine* command_line) {
   command_line->AppendSwitchASCII(
       switches::kNumRasterThreads,
-      base::IntToString(NumberOfRendererRasterThreads()));
+      base::NumberToString(NumberOfRendererRasterThreads()));
 
   int msaa_sample_count = GpuRasterizationMSAASampleCount();
   if (msaa_sample_count >= 0) {
     command_line->AppendSwitchASCII(switches::kGpuRasterizationMSAASampleCount,
-                                    base::IntToString(msaa_sample_count));
+                                    base::NumberToString(msaa_sample_count));
   }
 
   if (IsZeroCopyUploadEnabled())
@@ -4053,6 +4052,18 @@
         UnmatchedServiceWorkerProcessTracker::MatchWithSite(site_instance);
   }
 
+  // If a process hasn't been selected yet, check whether a default process
+  // has been assigned for this browsing instance and use that. This is used
+  // to group all SiteInstance that don't require a dedicated process into a
+  // single process. This will only be set if
+  // kProcessSharingWithStrictSiteInstances is enabled.
+  if (!render_process_host) {
+    render_process_host = site_instance->GetDefaultProcessIfUsable();
+    DCHECK(!render_process_host ||
+           base::FeatureList::IsEnabled(
+               features::kProcessSharingWithStrictSiteInstances));
+  }
+
   // See if the spare RenderProcessHost can be used.
   SpareRenderProcessHostManager& spare_process_manager =
       g_spare_render_process_host_manager.Get();
@@ -4682,7 +4693,8 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   base::PostTaskAndReplyWithResult(
       &GetAecDumpFileTaskRunner(), FROM_HERE,
-      base::Bind(&CreateFileForProcess, file.AddExtension(IntToStringType(id))),
+      base::Bind(&CreateFileForProcess,
+                 file.AddExtension(NumberToStringType(id))),
       base::Bind(&RenderProcessHostImpl::SendAecDumpFileToRenderer,
                  weak_factory_.GetWeakPtr(), id));
 }
@@ -4701,7 +4713,7 @@
 
 base::FilePath RenderProcessHostImpl::GetAecDumpFilePathWithExtensions(
     const base::FilePath& file) {
-  return file.AddExtension(IntToStringType(GetProcess().Pid()))
+  return file.AddExtension(NumberToStringType(GetProcess().Pid()))
       .AddExtension(kAecDumpFileNameAddition);
 }
 
diff --git a/content/browser/resources/appcache/appcache_internals.html b/content/browser/resources/appcache/appcache_internals.html
index ba2b3c5..c2e529c 100644
--- a/content/browser/resources/appcache/appcache_internals.html
+++ b/content/browser/resources/appcache/appcache_internals.html
@@ -8,11 +8,10 @@
   <head>
     <meta charset="utf-8">
     <title>AppCache</title>
-    <link rel="stylesheet" href="chrome://resources/css/tabs.css">
-    <link rel="stylesheet" href="chrome://resources/css/widgets.css">
+    <link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
     <link rel="stylesheet" href="appcache_internals.css">
   </head>
-  <body style="font-family:$i18n{fontfamily};font-size:$i18n{fontsize}">
+  <body>
     <!-- templates -->
     <div style="display:none">
       <div id="appcache-list-template"
@@ -99,7 +98,6 @@
       <script src="chrome://resources/js/util.js"></script>
       <script src="chrome://resources/js/cr.js"></script>
       <script src="appcache_internals.js"></script>
-      <script src="chrome://resources/js/load_time_data.js"></script>
       <script src="chrome://resources/js/jstemplate_compiled.js"></script>
   </body>
 </html>
diff --git a/content/browser/serial/serial_browsertest.cc b/content/browser/serial/serial_browsertest.cc
index 361bd9d7..1f57636 100644
--- a/content/browser/serial/serial_browsertest.cc
+++ b/content/browser/serial/serial_browsertest.cc
@@ -7,6 +7,7 @@
 
 #include "base/command_line.h"
 #include "base/unguessable_token.h"
+#include "build/build_config.h"
 #include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/serial_chooser.h"
 #include "content/public/browser/serial_delegate.h"
@@ -125,7 +126,13 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(SerialTest, GetPorts) {
+// https://crbug.com/928712 tracks failure on Android tablets.
+#if defined(OS_ANDROID)
+#define MAYBE_GetPorts DISABLED_GetPorts
+#else
+#define MAYBE_GetPorts GetPorts
+#endif
+IN_PROC_BROWSER_TEST_F(SerialTest, MAYBE_GetPorts) {
   NavigateToURL(shell(), GetTestUrl(nullptr, "simple_page.html"));
 
   // Three ports are added but only two will have permission granted.
@@ -151,7 +158,13 @@
   EXPECT_EQ(2, result);
 }
 
-IN_PROC_BROWSER_TEST_F(SerialTest, RequestPort) {
+// https://crbug.com/928712 tracks failure on Android tablets.
+#if defined(OS_ANDROID)
+#define MAYBE_RequestPort DISABLED_RequestPort
+#else
+#define MAYBE_RequestPort RequestPort
+#endif
+IN_PROC_BROWSER_TEST_F(SerialTest, MAYBE_RequestPort) {
   NavigateToURL(shell(), GetTestUrl(nullptr, "simple_page.html"));
 
   auto port = device::mojom::SerialPortInfo::New();
diff --git a/content/browser/service_worker/README.md b/content/browser/service_worker/README.md
index e571d4c..daf1acaa 100644
--- a/content/browser/service_worker/README.md
+++ b/content/browser/service_worker/README.md
@@ -4,7 +4,7 @@
 [content/renderer/service_worker]: /content/renderer/service_worker
 [content/common/service_worker]: /content/common/service_worker
 [disk_cache]: /net/disk_cache/README.md
-[embedded_worker.mojom]: https://codesearch.chromium.org/chromium/src/content/common/service_worker/embedded_worker.mojom
+[embedded_worker.mojom]: https://codesearch.chromium.org/chromium/src/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
 [service_worker_container.mojom]: https://codesearch.chromium.org/chromium/src/third_party/blink/public/mojom/service_worker/service_worker_container.mojom
 [service_worker_database.h]: https://codesearch.chromium.org/chromium/src/content/browser/service_worker/service_worker_database.h
 [third_party/blink/common/service_worker]: /third_party/blink/common/service_worker
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc
index fc320fa..e0ac361 100644
--- a/content/browser/service_worker/embedded_worker_instance.cc
+++ b/content/browser/service_worker/embedded_worker_instance.cc
@@ -158,7 +158,7 @@
 
 using SetupProcessCallback = base::OnceCallback<void(
     blink::ServiceWorkerStatusCode,
-    mojom::EmbeddedWorkerStartParamsPtr,
+    blink::mojom::EmbeddedWorkerStartParamsPtr,
     std::unique_ptr<ServiceWorkerProcessManager::AllocatedProcessInfo>,
     std::unique_ptr<EmbeddedWorkerInstance::DevToolsProxy>,
     std::unique_ptr<
@@ -178,8 +178,8 @@
 // chrome-extension:// as needed.
 void SetupOnUIThread(base::WeakPtr<ServiceWorkerProcessManager> process_manager,
                      bool can_use_existing_process,
-                     mojom::EmbeddedWorkerStartParamsPtr params,
-                     mojom::EmbeddedWorkerInstanceClientRequest request,
+                     blink::mojom::EmbeddedWorkerStartParamsPtr params,
+                     blink::mojom::EmbeddedWorkerInstanceClientRequest request,
                      ServiceWorkerContextCore* context,
                      base::WeakPtr<ServiceWorkerContextCore> weak_context,
                      SetupProcessCallback callback) {
@@ -430,7 +430,7 @@
 
   StartTask(EmbeddedWorkerInstance* instance,
             const GURL& script_url,
-            mojom::EmbeddedWorkerInstanceClientRequest request,
+            blink::mojom::EmbeddedWorkerInstanceClientRequest request,
             base::TimeTicks start_time)
       : instance_(instance),
         request_(std::move(request)),
@@ -503,7 +503,7 @@
     return skip_recording_startup_time_;
   }
 
-  void Start(mojom::EmbeddedWorkerStartParamsPtr params,
+  void Start(blink::mojom::EmbeddedWorkerStartParamsPtr params,
              StatusCallback sent_start_callback) {
     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     DCHECK(instance_->context_);
@@ -541,7 +541,7 @@
   void OnSetupCompleted(
       base::WeakPtr<ServiceWorkerProcessManager> process_manager,
       blink::ServiceWorkerStatusCode status,
-      mojom::EmbeddedWorkerStartParamsPtr params,
+      blink::mojom::EmbeddedWorkerStartParamsPtr params,
       std::unique_ptr<ServiceWorkerProcessManager::AllocatedProcessInfo>
           process_info,
       std::unique_ptr<EmbeddedWorkerInstance::DevToolsProxy> devtools_proxy,
@@ -625,7 +625,7 @@
   EmbeddedWorkerInstance* instance_;
 
   // Ownership is transferred by a PostTask() call after process allocation.
-  mojom::EmbeddedWorkerInstanceClientRequest request_;
+  blink::mojom::EmbeddedWorkerInstanceClientRequest request_;
 
   StatusCallback sent_start_callback_;
   bool did_send_start_ = false;
@@ -654,8 +654,9 @@
   process_handle_.reset();
 }
 
-void EmbeddedWorkerInstance::Start(mojom::EmbeddedWorkerStartParamsPtr params,
-                                   StatusCallback callback) {
+void EmbeddedWorkerInstance::Start(
+    blink::mojom::EmbeddedWorkerStartParamsPtr params,
+    StatusCallback callback) {
   DCHECK(context_);
   restart_count_++;
   DCHECK_EQ(EmbeddedWorkerStatus::STOPPED, status_);
@@ -677,7 +678,7 @@
   params->wait_for_debugger = false;
   params->v8_cache_options = GetV8CacheOptions();
 
-  mojom::EmbeddedWorkerInstanceClientRequest request =
+  blink::mojom::EmbeddedWorkerInstanceClientRequest request =
       mojo::MakeRequest(&client_);
   client_.set_connection_error_handler(
       base::BindOnce(&EmbeddedWorkerInstance::Detach, base::Unretained(this)));
@@ -780,7 +781,7 @@
 }
 
 void EmbeddedWorkerInstance::SendStartWorker(
-    mojom::EmbeddedWorkerStartParamsPtr params,
+    blink::mojom::EmbeddedWorkerStartParamsPtr params,
     scoped_refptr<network::SharedURLLoaderFactory> factory,
     blink::mojom::CacheStoragePtrInfo cache_storage) {
   DCHECK(context_);
@@ -891,7 +892,7 @@
 void EmbeddedWorkerInstance::OnStarted(
     blink::mojom::ServiceWorkerStartStatus start_status,
     int thread_id,
-    mojom::EmbeddedWorkerStartTimingPtr start_timing) {
+    blink::mojom::EmbeddedWorkerStartTimingPtr start_timing) {
   if (!(start_timing->start_worker_received_time <=
             start_timing->script_evaluation_start_time &&
         start_timing->script_evaluation_start_time <=
diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h
index 6368234..6396910 100644
--- a/content/browser/service_worker/embedded_worker_instance.h
+++ b/content/browser/service_worker/embedded_worker_instance.h
@@ -24,11 +24,11 @@
 #include "content/browser/service_worker/embedded_worker_status.h"
 #include "content/browser/service_worker/service_worker_metrics.h"
 #include "content/common/content_export.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #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/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"
 #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h"
 #include "third_party/blink/public/web/web_console_message.h"
@@ -52,7 +52,7 @@
 //
 // Owned by ServiceWorkerVersion. Lives on the IO thread.
 class CONTENT_EXPORT EmbeddedWorkerInstance
-    : public mojom::EmbeddedWorkerInstanceHost {
+    : public blink::mojom::EmbeddedWorkerInstanceHost {
  public:
   class DevToolsProxy;
   using StatusCallback =
@@ -129,7 +129,7 @@
   // the callback is invoked with kOk status, the service worker has not yet
   // finished starting. Observe OnStarted()/OnStopped() for when start completed
   // or failed.
-  void Start(mojom::EmbeddedWorkerStartParamsPtr params,
+  void Start(blink::mojom::EmbeddedWorkerStartParamsPtr params,
              StatusCallback sent_start_callback);
 
   // Stops the worker. It is invalid to call this when the worker is not in
@@ -257,11 +257,11 @@
   // |factory| is used for loading non-installed scripts. It can internally be a
   // bundle of factories instead of just the direct network factory to support
   // non-NetworkService schemes like chrome-extension:// URLs.
-  void SendStartWorker(mojom::EmbeddedWorkerStartParamsPtr params,
+  void SendStartWorker(blink::mojom::EmbeddedWorkerStartParamsPtr params,
                        scoped_refptr<network::SharedURLLoaderFactory> factory,
                        blink::mojom::CacheStoragePtrInfo cache_storage);
 
-  // Implements mojom::EmbeddedWorkerInstanceHost.
+  // Implements blink::mojom::EmbeddedWorkerInstanceHost.
   // These functions all run on the IO thread.
   void RequestTermination(RequestTerminationCallback callback) override;
   void CountFeature(blink::mojom::WebFeature feature) override;
@@ -269,9 +269,10 @@
   void OnScriptLoaded() override;
   void OnScriptEvaluationStart() override;
   // Changes the internal worker status from STARTING to RUNNING.
-  void OnStarted(blink::mojom::ServiceWorkerStartStatus status,
-                 int thread_id,
-                 mojom::EmbeddedWorkerStartTimingPtr start_timing) override;
+  void OnStarted(
+      blink::mojom::ServiceWorkerStartStatus status,
+      int thread_id,
+      blink::mojom::EmbeddedWorkerStartTimingPtr start_timing) override;
   // Resets the embedded worker instance to the initial state. Changes
   // the internal status from STARTING or RUNNING to STOPPED.
   void OnStopped() override;
@@ -312,7 +313,7 @@
   // |client_| is used to send messages to the renderer process. The browser
   // process should not disconnect the pipe because associated interfaces may be
   // using it. The renderer process will disconnect the pipe when appropriate.
-  mojom::EmbeddedWorkerInstanceClientPtr client_;
+  blink::mojom::EmbeddedWorkerInstanceClientPtr client_;
 
   // Binding for EmbeddedWorkerInstanceHost, runs on IO thread.
   mojo::AssociatedBinding<EmbeddedWorkerInstanceHost> instance_host_binding_;
diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc
index d642f49..746876f 100644
--- a/content/browser/service_worker/embedded_worker_instance_unittest.cc
+++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc
@@ -23,7 +23,6 @@
 #include "content/browser/service_worker/service_worker_registration.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "content/public/common/child_process_host.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -31,6 +30,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/mojom/service_worker/embedded_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
@@ -134,8 +134,9 @@
   // Calls worker->Start() and runs until the start IPC is sent.
   //
   // Expects success. For failure cases, call Start() manually.
-  void StartWorkerUntilStartSent(EmbeddedWorkerInstance* worker,
-                                 mojom::EmbeddedWorkerStartParamsPtr params) {
+  void StartWorkerUntilStartSent(
+      EmbeddedWorkerInstance* worker,
+      blink::mojom::EmbeddedWorkerStartParamsPtr params) {
     base::Optional<blink::ServiceWorkerStatusCode> status;
     base::RunLoop loop;
     worker->Start(std::move(params),
@@ -149,16 +150,16 @@
   //
   // Expects success. For failure cases, call Start() manually.
   void StartWorker(EmbeddedWorkerInstance* worker,
-                   mojom::EmbeddedWorkerStartParamsPtr params) {
+                   blink::mojom::EmbeddedWorkerStartParamsPtr params) {
     StartWorkerUntilStartSent(worker, std::move(params));
     // TODO(falken): Listen for OnStarted() instead of this.
     base::RunLoop().RunUntilIdle();
     EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, worker->status());
   }
 
-  mojom::EmbeddedWorkerStartParamsPtr CreateStartParams(
+  blink::mojom::EmbeddedWorkerStartParamsPtr CreateStartParams(
       scoped_refptr<ServiceWorkerVersion> version) {
-    auto params = mojom::EmbeddedWorkerStartParams::New();
+    auto params = blink::mojom::EmbeddedWorkerStartParams::New();
     params->service_worker_version_id = version->version_id();
     params->scope = version->scope();
     params->script_url = version->script_url();
@@ -252,7 +253,7 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
@@ -285,9 +286,9 @@
  private:
   bool force_stall_in_start_ = true;
 
-  std::map<
-      int /* embedded_worker_id */,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtr /* instance_host_ptr */>
+  std::map<int /* embedded_worker_id */,
+           blink::mojom::
+               EmbeddedWorkerInstanceHostAssociatedPtr /* instance_host_ptr */>
       instance_host_ptr_map_;
 };
 
@@ -453,7 +454,8 @@
 
   // Run the start worker sequence and detach during process allocation.
   base::Optional<blink::ServiceWorkerStatusCode> status;
-  mojom::EmbeddedWorkerStartParamsPtr params = CreateStartParams(pair.second);
+  blink::mojom::EmbeddedWorkerStartParamsPtr params =
+      CreateStartParams(pair.second);
   worker->Start(std::move(params), ReceiveStatus(&status, base::DoNothing()));
   worker->Detach();
   base::RunLoop().RunUntilIdle();
@@ -572,7 +574,8 @@
   worker->AddObserver(this);
 
   // Run the start worker sequence until pause after download.
-  mojom::EmbeddedWorkerStartParamsPtr params = CreateStartParams(pair.second);
+  blink::mojom::EmbeddedWorkerStartParamsPtr params =
+      CreateStartParams(pair.second);
   params->pause_after_download = true;
   base::Optional<blink::ServiceWorkerStatusCode> status;
   worker->Start(std::move(params), ReceiveStatus(&status, base::DoNothing()));
@@ -700,7 +703,7 @@
       : EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient(helper) {}
 
  private:
-  void StartWorker(mojom::EmbeddedWorkerStartParamsPtr) override {
+  void StartWorker(blink::mojom::EmbeddedWorkerStartParamsPtr) override {
     helper_->mock_instance_clients()->clear();
   }
 };
@@ -820,7 +823,7 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
@@ -867,7 +870,8 @@
   // Second, test a worker with pause after download.
   {
     // Start the worker until paused.
-    mojom::EmbeddedWorkerStartParamsPtr params = CreateStartParams(pair.second);
+    blink::mojom::EmbeddedWorkerStartParamsPtr params =
+        CreateStartParams(pair.second);
     params->pause_after_download = true;
     worker->Start(std::move(params), base::DoNothing());
     base::RunLoop().RunUntilIdle();
@@ -907,7 +911,8 @@
   // First, test a worker without pause after download.
   {
     // Start the worker.
-    mojom::EmbeddedWorkerStartParamsPtr params = CreateStartParams(pair.second);
+    blink::mojom::EmbeddedWorkerStartParamsPtr params =
+        CreateStartParams(pair.second);
     StartWorker(worker.get(), std::move(params));
 
     // Cache storage should not have been sent.
@@ -921,7 +926,8 @@
   // Second, test a worker with pause after download.
   {
     // Start the worker until paused.
-    mojom::EmbeddedWorkerStartParamsPtr params = CreateStartParams(pair.second);
+    blink::mojom::EmbeddedWorkerStartParamsPtr params =
+        CreateStartParams(pair.second);
     params->pause_after_download = true;
     worker->Start(std::move(params), base::DoNothing());
     base::RunLoop().RunUntilIdle();
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index f678ee4f..a5778dd 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -111,7 +111,7 @@
     ~MockEmbeddedWorkerInstanceClient() {}
 
 void EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient::StartWorker(
-    mojom::EmbeddedWorkerStartParamsPtr params) {
+    blink::mojom::EmbeddedWorkerStartParamsPtr params) {
   if (!helper_)
     return;
 
@@ -153,7 +153,7 @@
 // static
 void EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient::Bind(
     const base::WeakPtr<EmbeddedWorkerTestHelper>& helper,
-    mojom::EmbeddedWorkerInstanceClientRequest request) {
+    blink::mojom::EmbeddedWorkerInstanceClientRequest request) {
   std::vector<std::unique_ptr<MockEmbeddedWorkerInstanceClient>>* clients =
       helper->mock_instance_clients();
   size_t next_client_index = helper->mock_instance_clients_next_index_;
@@ -387,7 +387,8 @@
   void CreateView(mojom::CreateViewParamsPtr) override { NOTREACHED(); }
   void CreateFrame(mojom::CreateFrameParamsPtr) override { NOTREACHED(); }
   void SetUpEmbeddedWorkerChannelForServiceWorker(
-      mojom::EmbeddedWorkerInstanceClientRequest client_request) override {
+      blink::mojom::EmbeddedWorkerInstanceClientRequest client_request)
+      override {
     MockEmbeddedWorkerInstanceClient::Bind(helper_, std::move(client_request));
   }
   void CreateFrameProxy(
@@ -537,7 +538,7 @@
     bool pause_after_download,
     blink::mojom::ServiceWorkerRequest service_worker_request,
     blink::mojom::ControllerServiceWorkerRequest controller_request,
-    mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+    blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
     blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
     blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info) {
   EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id);
@@ -772,7 +773,7 @@
   ASSERT_TRUE(worker);
   ASSERT_TRUE(embedded_worker_id_instance_host_ptr_map_[embedded_worker_id]);
   embedded_worker_id_instance_host_ptr_map_[embedded_worker_id]->OnStarted(
-      status, thread_id, mojom::EmbeddedWorkerStartTiming::New());
+      status, thread_id, blink::mojom::EmbeddedWorkerStartTiming::New());
   base::RunLoop().RunUntilIdle();
 }
 
@@ -823,7 +824,7 @@
 }
 
 void EmbeddedWorkerTestHelper::OnStartWorkerStub(
-    mojom::EmbeddedWorkerStartParamsPtr params) {
+    blink::mojom::EmbeddedWorkerStartParamsPtr params) {
   EmbeddedWorkerInstance* worker =
       registry()->GetWorker(params->embedded_worker_id);
   ASSERT_TRUE(worker);
diff --git a/content/browser/service_worker/embedded_worker_test_helper.h b/content/browser/service_worker/embedded_worker_test_helper.h
index b31aa82..c299639 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.h
+++ b/content/browser/service_worker/embedded_worker_test_helper.h
@@ -18,12 +18,12 @@
 #include "base/time/time.h"
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/url_loader_factory_getter.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "net/cookies/cookie_change_dispatcher.h"
 #include "net/http/http_response_info.h"
 #include "services/network/public/mojom/cookie_manager.mojom.h"
 #include "third_party/blink/public/common/service_worker/service_worker_status_code.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"
 #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h"
@@ -63,18 +63,19 @@
   enum class Event { Install, Activate };
 
   class MockEmbeddedWorkerInstanceClient
-      : public mojom::EmbeddedWorkerInstanceClient {
+      : public blink::mojom::EmbeddedWorkerInstanceClient {
    public:
     explicit MockEmbeddedWorkerInstanceClient(
         base::WeakPtr<EmbeddedWorkerTestHelper> helper);
     ~MockEmbeddedWorkerInstanceClient() override;
 
     static void Bind(const base::WeakPtr<EmbeddedWorkerTestHelper>& helper,
-                     mojom::EmbeddedWorkerInstanceClientRequest request);
+                     blink::mojom::EmbeddedWorkerInstanceClientRequest request);
 
    protected:
-    // mojom::EmbeddedWorkerInstanceClient implementation.
-    void StartWorker(mojom::EmbeddedWorkerStartParamsPtr params) override;
+    // blink::mojom::EmbeddedWorkerInstanceClient implementation.
+    void StartWorker(
+        blink::mojom::EmbeddedWorkerStartParamsPtr params) override;
     void StopWorker() override;
     void ResumeAfterDownload() override;
     void AddMessageToConsole(blink::mojom::ConsoleMessageLevel level,
@@ -84,7 +85,7 @@
         blink::mojom::DevToolsAgentAssociatedRequest) override {}
 
     base::WeakPtr<EmbeddedWorkerTestHelper> helper_;
-    mojo::Binding<mojom::EmbeddedWorkerInstanceClient> binding_;
+    mojo::Binding<blink::mojom::EmbeddedWorkerInstanceClient> binding_;
 
     base::Optional<int> embedded_worker_id_;
 
@@ -158,7 +159,7 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr
           installed_scripts_info);
@@ -251,7 +252,7 @@
     return embedded_worker_id_host_map_[embedded_worker_id].get();
   }
 
-  mojom::EmbeddedWorkerInstanceHostProxy* GetEmbeddedWorkerInstanceHost(
+  blink::mojom::EmbeddedWorkerInstanceHostProxy* GetEmbeddedWorkerInstanceHost(
       int embedded_worker_id) {
     return embedded_worker_id_instance_host_ptr_map_[embedded_worker_id].get();
   }
@@ -268,7 +269,7 @@
       int embedded_worker_id,
       blink::mojom::ServiceWorkerHostAssociatedPtrInfo service_worker_host,
       blink::mojom::ServiceWorkerRegistrationObjectInfoPtr registration_info);
-  void OnStartWorkerStub(mojom::EmbeddedWorkerStartParamsPtr params);
+  void OnStartWorkerStub(blink::mojom::EmbeddedWorkerStartParamsPtr params);
   void OnResumeAfterDownloadStub(int embedded_worker_id);
   void OnStopWorkerStub(int embedded_worker_id);
   void OnActivateEventStub(
@@ -351,9 +352,9 @@
 
   std::map<int, int64_t> embedded_worker_id_service_worker_version_id_map_;
 
-  std::map<
-      int /* embedded_worker_id */,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtr /* instance_host_ptr */>
+  std::map<int /* embedded_worker_id */,
+           blink::mojom::
+               EmbeddedWorkerInstanceHostAssociatedPtr /* instance_host_ptr */>
       embedded_worker_id_instance_host_ptr_map_;
   std::map<int /* embedded_worker_id */, ServiceWorkerRemoteProviderEndpoint>
       embedded_worker_id_remote_provider_map_;
diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc
index d1fe774..c7f31a4 100644
--- a/content/browser/service_worker/service_worker_browsertest.cc
+++ b/content/browser/service_worker/service_worker_browsertest.cc
@@ -46,6 +46,7 @@
 #include "content/browser/service_worker/service_worker_test_utils.h"
 #include "content/browser/service_worker/service_worker_version.h"
 #include "content/browser/web_contents/web_contents_impl.h"
+#include "content/browser/web_package/signed_exchange_consts.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_task_traits.h"
@@ -2570,7 +2571,7 @@
   EXPECT_EQ("OK", GetString(*dict, "statusText"));
   EXPECT_TRUE(CheckHeader(*dict, "content-type", "text/html"));
   EXPECT_TRUE(CheckHeader(*dict, "content-length",
-                          base::IntToString(sizeof(kPage) - 1)));
+                          base::NumberToString(sizeof(kPage) - 1)));
 }
 
 IN_PROC_BROWSER_TEST_F(ServiceWorkerNavigationPreloadTest, NotEnabled) {
@@ -3714,7 +3715,9 @@
   ASSERT_TRUE(dict);
 
   // Default headers are present.
-  EXPECT_TRUE(CheckHeader(*dict, "accept", network::kFrameAcceptHeader));
+  EXPECT_TRUE(CheckHeader(*dict, "accept",
+                          std::string(network::kFrameAcceptHeader) +
+                              std::string(kAcceptHeaderSignedExchangeSuffix)));
   // Injected headers are present.
   EXPECT_TRUE(CheckHeader(*dict, "x-injected", "injected value"));
 
diff --git a/content/browser/service_worker/service_worker_cache_writer.cc b/content/browser/service_worker/service_worker_cache_writer.cc
index 8acd338..6df907e 100644
--- a/content/browser/service_worker/service_worker_cache_writer.cc
+++ b/content/browser/service_worker/service_worker_cache_writer.cc
@@ -131,10 +131,47 @@
 
 ServiceWorkerCacheWriter::~ServiceWorkerCacheWriter() {}
 
+std::unique_ptr<ServiceWorkerCacheWriter>
+ServiceWorkerCacheWriter::CreateForCopy(
+    std::unique_ptr<ServiceWorkerResponseReader> copy_reader,
+    std::unique_ptr<ServiceWorkerResponseWriter> writer) {
+  DCHECK(copy_reader);
+  DCHECK(writer);
+  return base::WrapUnique(new ServiceWorkerCacheWriter(
+      nullptr /* compare_reader */, std::move(copy_reader), std::move(writer),
+      false /* pause_when_not_identical*/));
+}
+
+std::unique_ptr<ServiceWorkerCacheWriter>
+ServiceWorkerCacheWriter::CreateForWriteBack(
+    std::unique_ptr<ServiceWorkerResponseWriter> writer) {
+  DCHECK(writer);
+  return base::WrapUnique(new ServiceWorkerCacheWriter(
+      nullptr /* compare_reader */, nullptr /* copy_reader */,
+      std::move(writer), false /* pause_when_not_identical*/));
+}
+
+std::unique_ptr<ServiceWorkerCacheWriter>
+ServiceWorkerCacheWriter::CreateForComparison(
+    std::unique_ptr<ServiceWorkerResponseReader> compare_reader,
+    std::unique_ptr<ServiceWorkerResponseReader> copy_reader,
+    std::unique_ptr<ServiceWorkerResponseWriter> writer,
+    bool pause_when_not_identical) {
+  // |compare_reader| reads data for the comparison. |copy_reader| reads
+  // data for copy.
+  DCHECK(compare_reader);
+  DCHECK(copy_reader);
+  DCHECK(writer);
+  return base::WrapUnique(new ServiceWorkerCacheWriter(
+      std::move(compare_reader), std::move(copy_reader), std::move(writer),
+      pause_when_not_identical));
+}
+
 net::Error ServiceWorkerCacheWriter::MaybeWriteHeaders(
     HttpResponseInfoIOBuffer* headers,
     OnWriteCompleteCallback callback) {
   DCHECK(!io_pending_);
+  DCHECK(!IsCopying());
 
   headers_to_write_ = headers;
   pending_callback_ = std::move(callback);
@@ -163,6 +200,7 @@
     size_t buf_size,
     OnWriteCompleteCallback callback) {
   DCHECK(!io_pending_);
+  DCHECK(!IsCopying());
 
   data_to_write_ = buf;
   len_to_write_ = buf_size;
@@ -202,6 +240,7 @@
   DCHECK(pause_when_not_identical_);
   DCHECK_EQ(STATE_PAUSING, state_);
   DCHECK(io_pending_);
+  DCHECK(!IsCopying());
 
   io_pending_ = false;
   pending_callback_ = std::move(callback);
@@ -231,11 +270,42 @@
   return result >= 0 ? net::OK : static_cast<net::Error>(result);
 }
 
+net::Error ServiceWorkerCacheWriter::StartCopy(
+    OnWriteCompleteCallback callback) {
+  DCHECK(IsCopying());
+  pending_callback_ = std::move(callback);
+
+  int result = DoLoop(net::OK);
+
+  // Synchronous completions are always STATE_DONE.
+  if (result != net::ERR_IO_PENDING)
+    DCHECK_EQ(STATE_DONE, state_);
+
+  // Asynchronous completion means the state machine must be waiting in one of
+  // the Done states for an IO operation to complete:
+  if (result == net::ERR_IO_PENDING) {
+    DCHECK(state_ == STATE_READ_HEADERS_FOR_COPY_DONE ||
+           state_ == STATE_WRITE_HEADERS_FOR_COPY_DONE ||
+           state_ == STATE_READ_DATA_FOR_COPY_DONE ||
+           state_ == STATE_WRITE_DATA_FOR_COPY_DONE)
+        << "Unexpected state: " << state_;
+  }
+
+  return result >= 0 ? net::OK : static_cast<net::Error>(result);
+}
+
+bool ServiceWorkerCacheWriter::IsCopying() const {
+  return !compare_reader_ && copy_reader_;
+}
+
 int ServiceWorkerCacheWriter::DoStart(int result) {
   bytes_written_ = 0;
   if (compare_reader_) {
     state_ = STATE_READ_HEADERS_FOR_COMPARE;
     comparing_ = true;
+  } else if (IsCopying()) {
+    state_ = STATE_READ_HEADERS_FOR_COPY;
+    comparing_ = false;
   } else {
     // No existing reader, just write the headers back directly.
     state_ = STATE_WRITE_HEADERS_FOR_PASSTHROUGH;
@@ -362,12 +432,14 @@
 
 // Write the just-read headers back to the cache.
 // Note that this *discards* the read headers and replaces them with the net
+// headers if the cache writer is not for copy, otherwise write the read
 // headers.
 int ServiceWorkerCacheWriter::DoWriteHeadersForCopy(int result) {
   DCHECK_GE(result, 0);
   DCHECK(writer_);
   state_ = STATE_WRITE_HEADERS_FOR_COPY_DONE;
-  return WriteInfoHelper(writer_, headers_to_write_.get());
+  return WriteInfoHelper(
+      writer_, IsCopying() ? headers_to_read_.get() : headers_to_write_.get());
 }
 
 int ServiceWorkerCacheWriter::DoWriteHeadersForCopyDone(int result) {
@@ -381,13 +453,22 @@
 
 int ServiceWorkerCacheWriter::DoReadDataForCopy(int result) {
   DCHECK_GE(result, 0);
-  size_t to_read = std::min(kCopyBufferSize, bytes_compared_ - bytes_copied_);
+
+  // If the cache writer is only for copy, get the total size to read from
+  // header data instead of |bytes_compared_| as no comparison is done.
+  size_t total_size_to_read =
+      IsCopying() ? headers_to_read_->response_data_size : bytes_compared_;
+  size_t to_read =
+      std::min(kCopyBufferSize, total_size_to_read - bytes_copied_);
+
   // At this point, all compared bytes have been read. Currently
-  // |data_to_write_| and |len_to_write_| hold the chunk of network input that
-  // caused the comparison failure, so those need to be written back and this
-  // object needs to go into passthrough mode.
+  // If the cache write is not just for copy, |data_to_write_| and
+  // |len_to_write_| hold the chunk of network input that caused the comparison
+  // failure, so those need to be written back and this object needs to go into
+  // passthrough mode. If the cache writer is just for copy, change state to
+  // STATE_DONE as there is no more data to copy.
   if (to_read == 0) {
-    state_ = STATE_WRITE_DATA_FOR_PASSTHROUGH;
+    state_ = IsCopying() ? STATE_DONE : STATE_WRITE_DATA_FOR_PASSTHROUGH;
     return net::OK;
   }
   state_ = STATE_READ_DATA_FOR_COPY_DONE;
diff --git a/content/browser/service_worker/service_worker_cache_writer.h b/content/browser/service_worker/service_worker_cache_writer.h
index e8866e14..ee455028 100644
--- a/content/browser/service_worker/service_worker_cache_writer.h
+++ b/content/browser/service_worker/service_worker_cache_writer.h
@@ -39,15 +39,28 @@
  public:
   using OnWriteCompleteCallback = base::OnceCallback<void(net::Error)>;
 
-  // The |compare_reader| may be null, in which case this instance will
-  // unconditionally write back data supplied to |MaybeWriteHeaders| and
-  // |MaybeWriteData|.
-  //
+  // Create a cache writer instance that copies a script already in storage. The
+  // script is read by |copy_reader|.
+  static std::unique_ptr<ServiceWorkerCacheWriter> CreateForCopy(
+      std::unique_ptr<ServiceWorkerResponseReader> copy_reader,
+      std::unique_ptr<ServiceWorkerResponseWriter> writer);
+
+  // Create a cache writer instance that unconditionally write back data
+  // supplied to |MaybeWriteHeaders| and |MaybeWriteData| to storage.
+  static std::unique_ptr<ServiceWorkerCacheWriter> CreateForWriteBack(
+      std::unique_ptr<ServiceWorkerResponseWriter> writer);
+
+  // Create a cache writer that compares between a script in storage and data
+  // from network (supplied with |MaybeWriteHeaders| and |MaybeWriteData|).
+  // Nothing would be written to storage if it compares to be identical.
   // When |pause_when_not_identical| is true and the cache writer detects a
   // difference between bodies from the network and from the storage, the
-  // comparison stops immediately and the cache writer returns
-  // net::ERR_IO_PENDING, with nothing written to the storage.
-  ServiceWorkerCacheWriter(
+  // comparison stops immediately and the cache writer is paused and returns
+  // net::ERR_IO_PENDING, with nothing written to the storage. It can be
+  // resumed later. If |pause_when_not_identical| is false, and the data is
+  // different, it would be written to storage directly. |copy_reader| is used
+  // for copying identical data blocks during writing.
+  static std::unique_ptr<ServiceWorkerCacheWriter> CreateForComparison(
       std::unique_ptr<ServiceWorkerResponseReader> compare_reader,
       std::unique_ptr<ServiceWorkerResponseReader> copy_reader,
       std::unique_ptr<ServiceWorkerResponseWriter> writer,
@@ -87,6 +100,15 @@
   // and |state_| is STATE_PAUSING.
   net::Error Resume(OnWriteCompleteCallback callback);
 
+  // Start to copy a script in storage to a new position. |callback| is
+  // called when the work is done. This is used when an installed script
+  // is used by a new service worker with no content change, thus downloading
+  // could be avoided.
+  net::Error StartCopy(OnWriteCompleteCallback callback);
+
+  // Returns true when the cache writer is created by CreateForCopy().
+  bool IsCopying() const;
+
  private:
   // States for the state machine.
   //
@@ -122,8 +144,9 @@
     // Control flows linearly through these states, with each pass from
     // READ_DATA_FOR_COPY to WRITE_DATA_FOR_COPY_DONE copying one block of data
     // at a time. Control loops from WRITE_DATA_FOR_COPY_DONE back to
-    // READ_DATA_FOR_COPY if there is more data to copy, or exits to
-    // WRITE_DATA_FOR_PASSTHROUGH.
+    // READ_DATA_FOR_COPY if there is more data to copy. If there is no more
+    // data, it exits to WRITE_DATA_FOR_PASSTHROUGH in case IsCopying()
+    // returns false or exits to DONE in case IsCopying() returns true.
     STATE_READ_HEADERS_FOR_COPY,
     STATE_READ_HEADERS_FOR_COPY_DONE,
     STATE_WRITE_HEADERS_FOR_COPY,
@@ -144,6 +167,12 @@
     STATE_DONE,
   };
 
+  ServiceWorkerCacheWriter(
+      std::unique_ptr<ServiceWorkerResponseReader> compare_reader,
+      std::unique_ptr<ServiceWorkerResponseReader> copy_reader,
+      std::unique_ptr<ServiceWorkerResponseWriter> writer,
+      bool pause_when_not_identical);
+
   // Drives this class's state machine. This function steps the state machine
   // until one of:
   //   a) One of the state functions returns an error
diff --git a/content/browser/service_worker/service_worker_cache_writer_unittest.cc b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
index f934101..93b41aa 100644
--- a/content/browser/service_worker/service_worker_cache_writer_unittest.cc
+++ b/content/browser/service_worker/service_worker_cache_writer_unittest.cc
@@ -8,6 +8,7 @@
 
 #include <list>
 #include <string>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/containers/queue.h"
@@ -22,33 +23,49 @@
 
 class ServiceWorkerCacheWriterTest : public ::testing::Test {
  public:
+  // Cache writer is created differently depending on diffrerent usage.
+  enum class CacheWriterUsage {
+    kForCopy,
+    kForWriteBack,
+    kForComparison,
+  };
+
   ServiceWorkerCacheWriterTest() {}
   ~ServiceWorkerCacheWriterTest() override {}
 
   MockServiceWorkerResponseReader* ExpectReader() {
-    std::unique_ptr<MockServiceWorkerResponseReader> reader(
-        new MockServiceWorkerResponseReader);
+    auto reader = std::make_unique<MockServiceWorkerResponseReader>();
     MockServiceWorkerResponseReader* borrowed_reader = reader.get();
     readers_.push_back(std::move(reader));
     return borrowed_reader;
   }
 
   MockServiceWorkerResponseWriter* ExpectWriter() {
-    std::unique_ptr<MockServiceWorkerResponseWriter> writer(
-        new MockServiceWorkerResponseWriter);
+    auto writer = std::make_unique<MockServiceWorkerResponseWriter>();
     MockServiceWorkerResponseWriter* borrowed_writer = writer.get();
     writers_.push_back(std::move(writer));
     return borrowed_writer;
   }
 
   // This should be called after ExpectReader() and ExpectWriter().
-  void Initialize(bool pause_when_not_identical) {
-    std::unique_ptr<ServiceWorkerResponseReader> compare_reader(CreateReader());
-    std::unique_ptr<ServiceWorkerResponseReader> copy_reader(CreateReader());
-    std::unique_ptr<ServiceWorkerResponseWriter> writer(CreateWriter());
-    cache_writer_.reset(new ServiceWorkerCacheWriter(
-        std::move(compare_reader), std::move(copy_reader), std::move(writer),
-        pause_when_not_identical));
+  void Initialize(CacheWriterUsage type, bool pause_when_not_identical) {
+    switch (type) {
+      case CacheWriterUsage::kForCopy:
+        cache_writer_ = ServiceWorkerCacheWriter::CreateForCopy(CreateReader(),
+                                                                CreateWriter());
+        break;
+      case CacheWriterUsage::kForWriteBack:
+        cache_writer_ =
+            ServiceWorkerCacheWriter::CreateForWriteBack(CreateWriter());
+        break;
+      case CacheWriterUsage::kForComparison:
+        auto compare_reader = CreateReader();
+        auto copy_reader = CreateReader();
+        cache_writer_ = ServiceWorkerCacheWriter::CreateForComparison(
+            std::move(compare_reader), std::move(copy_reader), CreateWriter(),
+            pause_when_not_identical);
+        break;
+    };
   }
 
  protected:
@@ -113,7 +130,8 @@
   const size_t kHeaderSize = 16;
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   writer->ExpectWriteInfoOk(kHeaderSize, false);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForWriteBack,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(kHeaderSize);
   EXPECT_EQ(net::OK, error);
@@ -126,7 +144,8 @@
   size_t kHeaderSize = 16;
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   writer->ExpectWriteInfoOk(kHeaderSize, true);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForWriteBack,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(kHeaderSize);
   EXPECT_EQ(net::ERR_IO_PENDING, error);
@@ -147,7 +166,8 @@
   writer->ExpectWriteInfoOk(response_size, false);
   writer->ExpectWriteDataOk(data1.size(), false);
   writer->ExpectWriteDataOk(data2.size(), false);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForWriteBack,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(response_size);
   EXPECT_EQ(net::OK, error);
@@ -169,7 +189,8 @@
   writer->ExpectWriteInfoOk(response_size, false);
   writer->ExpectWriteDataOk(data1.size(), true);
   writer->ExpectWriteDataOk(data2.size(), true);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForWriteBack,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(response_size);
   EXPECT_EQ(net::OK, error);
@@ -192,7 +213,8 @@
   const size_t kHeaderSize = 16;
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   writer->ExpectWriteInfo(kHeaderSize, false, net::ERR_FAILED);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForWriteBack,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(kHeaderSize);
   EXPECT_EQ(net::ERR_FAILED, error);
@@ -205,7 +227,8 @@
   size_t kHeaderSize = 16;
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   writer->ExpectWriteInfo(kHeaderSize, true, net::ERR_FAILED);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForWriteBack,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(kHeaderSize);
   EXPECT_EQ(net::ERR_IO_PENDING, error);
@@ -223,7 +246,8 @@
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   writer->ExpectWriteInfoOk(data.size(), false);
   writer->ExpectWriteData(data.size(), false, net::ERR_FAILED);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForWriteBack,
+             false /* pause_when_not_identical */);
 
   EXPECT_EQ(net::OK, WriteHeaders(data.size()));
   EXPECT_EQ(net::ERR_FAILED, WriteData(data));
@@ -236,7 +260,8 @@
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   writer->ExpectWriteInfoOk(data.size(), false);
   writer->ExpectWriteData(data.size(), true, net::ERR_FAILED);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForWriteBack,
+             false /* pause_when_not_identical */);
 
   EXPECT_EQ(net::OK, WriteHeaders(data.size()));
 
@@ -257,8 +282,13 @@
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   MockServiceWorkerResponseReader* reader = ExpectReader();
 
+  // Create a copy reader as it's needed to create cache writer for comparison
+  // though not used in this test.
+  ExpectReader();
+
   reader->ExpectReadInfoOk(response_size, false);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(response_size);
   EXPECT_EQ(net::OK, error);
@@ -273,9 +303,14 @@
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   MockServiceWorkerResponseReader* reader = ExpectReader();
 
+  // Create a copy reader as it's needed to create cache writer for comparison
+  // though not used in this test.
+  ExpectReader();
+
   reader->ExpectReadInfoOk(response_size, false);
   reader->ExpectReadDataOk(data1, false);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(response_size);
   EXPECT_EQ(net::OK, error);
@@ -293,8 +328,13 @@
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   MockServiceWorkerResponseReader* reader = ExpectReader();
 
+  // Create a copy reader as it's needed to create cache writer for comparison
+  // though not used in this test.
+  ExpectReader();
+
   reader->ExpectReadInfo(response_size, false, net::ERR_FAILED);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   EXPECT_EQ(net::ERR_FAILED, WriteHeaders(response_size));
   EXPECT_TRUE(writer->AllExpectedWritesDone());
@@ -308,9 +348,14 @@
   MockServiceWorkerResponseWriter* writer = ExpectWriter();
   MockServiceWorkerResponseReader* reader = ExpectReader();
 
+  // Create a copy reader as it's needed to create cache writer for comparison
+  // though not used in this test.
+  ExpectReader();
+
   reader->ExpectReadInfoOk(response_size, false);
   reader->ExpectReadData(data1.c_str(), data1.length(), false, net::ERR_FAILED);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(response_size);
   EXPECT_EQ(net::OK, error);
@@ -338,7 +383,14 @@
   reader->ExpectReadDataOk(cache_data3, false);
   reader->ExpectReadDataOk(cache_data4, false);
   reader->ExpectReadDataOk(data5, false);
-  Initialize(false /* pause_when_not_identical */);
+
+  // Create a copy reader and writer as they're needed to create cache writer
+  // for comparison though not used in this test.
+  ExpectReader();
+  ExpectWriter();
+
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(kHeaderSize);
   EXPECT_EQ(net::OK, error);
@@ -358,9 +410,15 @@
 
   MockServiceWorkerResponseReader* reader = ExpectReader();
 
+  // Create a copy reader and writer as they're needed to create cache writer
+  // for comparison though not used in this test.
+  ExpectReader();
+  ExpectWriter();
+
   reader->ExpectReadInfoOk(response_size, true);
   reader->ExpectReadDataOk(data1, true);
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(response_size);
   EXPECT_EQ(net::ERR_IO_PENDING, error);
@@ -384,11 +442,17 @@
 
   MockServiceWorkerResponseReader* reader = ExpectReader();
 
+  // Create a copy reader and writer as they're needed to create cache writer
+  // for comparison though not used in this test.
+  ExpectReader();
+  ExpectWriter();
+
   reader->ExpectReadInfoOk(response_size, true);
   for (size_t i = 0; i < base::size(expected_data); ++i) {
     reader->ExpectReadDataOk(expected_data[i], true);
   }
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(response_size);
   EXPECT_EQ(net::ERR_IO_PENDING, error);
@@ -433,7 +497,8 @@
   writer->ExpectWriteDataOk(net_data2.size(), false);
   writer->ExpectWriteDataOk(data3.size(), false);
 
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(net_response_size);
   EXPECT_EQ(net::OK, error);
@@ -475,7 +540,8 @@
   writer->ExpectWriteDataOk(net_data2.size(), false);
   writer->ExpectWriteDataOk(data3.size(), false);
 
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(net_response_size);
   EXPECT_EQ(net::OK, error);
@@ -519,7 +585,8 @@
   writer->ExpectWriteDataOk(data1.size(), false);
   writer->ExpectWriteDataOk(net_data2.size(), false);
 
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(net_size);
   EXPECT_EQ(net::OK, error);
@@ -582,7 +649,8 @@
   for (const auto& data : data_expected)
     writer->ExpectWriteDataOk(data.size(), false);
 
-  Initialize(false /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             false /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(bytes_from_net);
   EXPECT_EQ(net::OK, error);
@@ -639,7 +707,8 @@
   for (const auto& data : data_expected)
     writer->ExpectWriteDataOk(data.size(), false);
 
-  Initialize(true /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             true /* pause_when_not_identical */);
 
   net::Error error = WriteHeaders(bytes_from_net);
   EXPECT_EQ(net::OK, error);
@@ -709,7 +778,8 @@
   for (const auto& data : data_expected)
     writer->ExpectWriteDataOk(data.size(), true);
 
-  Initialize(true /* pause_when_not_identical */);
+  Initialize(CacheWriterUsage::kForComparison,
+             true /* pause_when_not_identical */);
 
   write_complete_ = false;
   net::Error error = WriteHeaders(bytes_from_net);
@@ -766,5 +836,229 @@
   EXPECT_TRUE(compare_reader->AllExpectedReadsDone());
 }
 
+// Tests behavior of a cache writer used to copy script which finishes
+// asynchronously.
+TEST_F(ServiceWorkerCacheWriterTest, CopyScript_Async) {
+  // Data from |copy_reader|.
+  const std::vector<std::string> data_from_cache{"abcd"};
+
+  // The written data should be the same as |data_from_cache|.
+  const std::vector<std::string> data_expected{"abcd"};
+
+  size_t bytes_cached = 0;
+  size_t bytes_expected = 0;
+
+  for (const auto& data : data_from_cache)
+    bytes_cached += data.size();
+
+  for (const auto& data : data_expected)
+    bytes_expected += data.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* copy_reader = ExpectReader();
+
+  copy_reader->ExpectReadInfoOk(bytes_cached, true);
+  for (const auto& data : data_from_cache)
+    copy_reader->ExpectReadDataOk(data, true);
+
+  writer->ExpectWriteInfoOk(bytes_expected, true);
+  for (const auto& data : data_expected)
+    writer->ExpectWriteDataOk(data.size(), true);
+
+  Initialize(CacheWriterUsage::kForCopy, false /* pause_when_not_identical */);
+
+  write_complete_ = false;
+  net::Error error = cache_writer_->StartCopy(CreateWriteCallback());
+  EXPECT_EQ(net::ERR_IO_PENDING, error);
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous read of the header. This doesn't finish all the
+  // read to the storage, so the callback isn't called yet.
+  copy_reader->CompletePendingRead();
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous write of the header. This doesn't finish all the
+  // write to the storage, so the callback isn't called yet.
+  writer->CompletePendingWrite();
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous read of the data. This finishes all the
+  // read to the storage. But the write has not ben performed, so the
+  // callback isn't called yet.
+  copy_reader->CompletePendingRead();
+  EXPECT_FALSE(write_complete_);
+  EXPECT_TRUE(copy_reader->AllExpectedReadsDone());
+
+  // Complete the asynchronous write of the data. This finishes all the
+  // write to the storage, so the callback is called.
+  writer->CompletePendingWrite();
+  EXPECT_TRUE(write_complete_);
+  EXPECT_EQ(net::OK, last_error_);
+  EXPECT_EQ(bytes_expected, cache_writer_->bytes_written());
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+}
+
+// Tests behavior of a cache writer used to copy script that read multiple
+// times and finishes asynchronously.
+TEST_F(ServiceWorkerCacheWriterTest, CopyScript_AsyncMultipleRead) {
+  // Data from |copy_reader|.
+  const std::vector<std::string> data_from_cache{"a", "bc", "d"};
+
+  // The written data should be the same as |data_from_cache|.
+  const std::vector<std::string> data_expected{"a", "bc", "d"};
+
+  size_t bytes_cached = 0;
+  size_t bytes_expected = 0;
+
+  for (const auto& data : data_from_cache)
+    bytes_cached += data.size();
+
+  for (const auto& data : data_expected)
+    bytes_expected += data.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* copy_reader = ExpectReader();
+
+  copy_reader->ExpectReadInfoOk(bytes_cached, true);
+  for (const auto& data : data_from_cache)
+    copy_reader->ExpectReadDataOk(data, true);
+
+  writer->ExpectWriteInfoOk(bytes_expected, true);
+  for (const auto& data : data_expected)
+    writer->ExpectWriteDataOk(data.size(), true);
+
+  Initialize(CacheWriterUsage::kForCopy, false /* pause_when_not_identical */);
+
+  write_complete_ = false;
+  net::Error error = cache_writer_->StartCopy(CreateWriteCallback());
+  EXPECT_EQ(net::ERR_IO_PENDING, error);
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous read of the header. This doesn't finish all the
+  // read to the storage, so the callback isn't called yet.
+  copy_reader->CompletePendingRead();
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous write of the header. This doesn't finish all the
+  // write to the storage, so the callback isn't called yet.
+  writer->CompletePendingWrite();
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous read of data block "a". This doesn't finish all
+  // the read to the storage, so the callback isn't called yet.
+  copy_reader->CompletePendingRead();
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous write of data block "a". This doesn't finish all
+  // the write to the storage, so the callback isn't called yet.
+  writer->CompletePendingWrite();
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous read of data block "bc". This doesn't finish all
+  // the read to the storage, so the callback isn't called yet.
+  copy_reader->CompletePendingRead();
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous write of the data block "bc". This doesn't finish
+  // all the write to the storage, so the callback isn't called yet.
+  writer->CompletePendingWrite();
+  EXPECT_FALSE(write_complete_);
+
+  // Complete the asynchronous read of data block "d". This finishes all the
+  // read to the storage. But the write has not ben performed, so the
+  // callback isn't called yet.
+  copy_reader->CompletePendingRead();
+  EXPECT_FALSE(write_complete_);
+  EXPECT_TRUE(copy_reader->AllExpectedReadsDone());
+
+  // Complete the asynchronous write of data block "d". This finishes all the
+  // write to the storage, so the callback is called.
+  writer->CompletePendingWrite();
+  EXPECT_TRUE(write_complete_);
+  EXPECT_EQ(net::OK, last_error_);
+  EXPECT_EQ(bytes_expected, cache_writer_->bytes_written());
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+}
+
+// Tests behavior of a cache writer used to copy script which finishes
+// synchronously.
+TEST_F(ServiceWorkerCacheWriterTest, CopyScript_Sync) {
+  // Data from |copy_reader|.
+  const std::vector<std::string> data_from_cache{"abcd"};
+
+  // The written data should be the same as |data_from_cache|.
+  const std::vector<std::string> data_expected{"abcd"};
+
+  size_t bytes_cached = 0;
+  size_t bytes_expected = 0;
+
+  for (const auto& data : data_from_cache)
+    bytes_cached += data.size();
+
+  for (const auto& data : data_expected)
+    bytes_expected += data.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* copy_reader = ExpectReader();
+
+  copy_reader->ExpectReadInfoOk(bytes_cached, false);
+  for (const auto& data : data_from_cache)
+    copy_reader->ExpectReadDataOk(data, false);
+
+  writer->ExpectWriteInfoOk(bytes_expected, false);
+  for (const auto& data : data_expected)
+    writer->ExpectWriteDataOk(data.size(), false);
+
+  Initialize(CacheWriterUsage::kForCopy, false /* pause_when_not_identical */);
+
+  net::Error error = cache_writer_->StartCopy(CreateWriteCallback());
+
+  // In synchronous read and write, everything finishes synchronously.
+  EXPECT_EQ(net::OK, error);
+  EXPECT_EQ(bytes_expected, cache_writer_->bytes_written());
+  EXPECT_TRUE(copy_reader->AllExpectedReadsDone());
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+}
+
+// Tests behavior of a cache writer used to copy script that read multiple
+// times and finishes synchronously.
+TEST_F(ServiceWorkerCacheWriterTest, CopyScript_SyncMultiRead) {
+  // Data from |copy_reader|.
+  const std::vector<std::string> data_from_cache{"a", "bc", "d"};
+
+  // The written data should be the same as |data_from_cache|.
+  const std::vector<std::string> data_expected{"a", "bc", "d"};
+
+  size_t bytes_cached = 0;
+  size_t bytes_expected = 0;
+
+  for (const auto& data : data_from_cache)
+    bytes_cached += data.size();
+
+  for (const auto& data : data_expected)
+    bytes_expected += data.size();
+
+  MockServiceWorkerResponseWriter* writer = ExpectWriter();
+  MockServiceWorkerResponseReader* copy_reader = ExpectReader();
+
+  copy_reader->ExpectReadInfoOk(bytes_cached, false);
+  for (const auto& data : data_from_cache)
+    copy_reader->ExpectReadDataOk(data, false);
+
+  writer->ExpectWriteInfoOk(bytes_expected, false);
+  for (const auto& data : data_expected)
+    writer->ExpectWriteDataOk(data.size(), false);
+
+  Initialize(CacheWriterUsage::kForCopy, false /* pause_when_not_identical */);
+
+  net::Error error = cache_writer_->StartCopy(CreateWriteCallback());
+
+  // In synchronous read and write, everything finishes synchronously.
+  EXPECT_EQ(net::OK, error);
+  EXPECT_EQ(bytes_expected, cache_writer_->bytes_written());
+  EXPECT_TRUE(copy_reader->AllExpectedReadsDone());
+  EXPECT_TRUE(writer->AllExpectedWritesDone());
+}
+
 }  // namespace
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc
index 42ce645..6932e5e 100644
--- a/content/browser/service_worker/service_worker_context_core.cc
+++ b/content/browser/service_worker/service_worker_context_core.cc
@@ -713,12 +713,11 @@
 
 void ServiceWorkerContextCore::CheckHasServiceWorker(
     const GURL& url,
-    const GURL& other_url,
     ServiceWorkerContext::CheckHasServiceWorkerCallback callback) {
   storage()->FindRegistrationForDocument(
       url, base::BindOnce(&ServiceWorkerContextCore::
                               DidFindRegistrationForCheckHasServiceWorker,
-                          AsWeakPtr(), other_url, std::move(callback)));
+                          AsWeakPtr(), std::move(callback)));
 }
 
 void ServiceWorkerContextCore::UpdateVersionFailureCount(
@@ -868,7 +867,6 @@
 }
 
 void ServiceWorkerContextCore::DidFindRegistrationForCheckHasServiceWorker(
-    const GURL& other_url,
     ServiceWorkerContext::CheckHasServiceWorkerCallback callback,
     blink::ServiceWorkerStatusCode status,
     scoped_refptr<ServiceWorkerRegistration> registration) {
@@ -877,11 +875,6 @@
     return;
   }
 
-  if (!ServiceWorkerUtils::ScopeMatches(registration->scope(), other_url)) {
-    std::move(callback).Run(ServiceWorkerCapability::NO_SERVICE_WORKER);
-    return;
-  }
-
   if (registration->is_uninstalling() || registration->is_uninstalled()) {
     std::move(callback).Run(ServiceWorkerCapability::NO_SERVICE_WORKER);
     return;
diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h
index e2b1e02..f2bb084 100644
--- a/content/browser/service_worker/service_worker_context_core.h
+++ b/content/browser/service_worker/service_worker_context_core.h
@@ -270,12 +270,11 @@
 
   void ClearAllServiceWorkersForTest(base::OnceClosure callback);
 
-  // Determines if there is a ServiceWorker registration that matches |url|, and
-  // if |other_url| falls inside the scope of the same registration. See
-  // ServiceWorkerContext::CheckHasServiceWorker for more details.
+  // Determines if there is a ServiceWorker registration that matches
+  // |url|. See ServiceWorkerContext::CheckHasServiceWorker for more
+  // details.
   void CheckHasServiceWorker(
       const GURL& url,
-      const GURL& other_url,
       const ServiceWorkerContext::CheckHasServiceWorkerCallback callback);
 
   void UpdateVersionFailureCount(int64_t version_id,
@@ -336,7 +335,6 @@
           registrations);
 
   void DidFindRegistrationForCheckHasServiceWorker(
-      const GURL& other_url,
       ServiceWorkerContext::CheckHasServiceWorkerCallback callback,
       blink::ServiceWorkerStatusCode status,
       scoped_refptr<ServiceWorkerRegistration> registration);
diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc
index 5611afb..8d08d7b 100644
--- a/content/browser/service_worker/service_worker_context_unittest.cc
+++ b/content/browser/service_worker/service_worker_context_unittest.cc
@@ -192,7 +192,7 @@
   const std::vector<Message>& events() const { return events_; }
 
  protected:
-  void StartWorker(mojom::EmbeddedWorkerStartParamsPtr params) override {
+  void StartWorker(blink::mojom::EmbeddedWorkerStartParamsPtr params) override {
     events_.push_back(Message::StartWorker);
     EmbeddedWorkerTestHelper::MockEmbeddedWorkerInstanceClient::StartWorker(
         std::move(params));
diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc
index 21f41e0..7a90824 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.cc
+++ b/content/browser/service_worker/service_worker_context_wrapper.cc
@@ -430,13 +430,12 @@
 
 void ServiceWorkerContextWrapper::CheckHasServiceWorker(
     const GURL& url,
-    const GURL& other_url,
     CheckHasServiceWorkerCallback callback) {
   if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
     base::PostTaskWithTraits(
         FROM_HERE, {BrowserThread::IO},
         base::BindOnce(&ServiceWorkerContextWrapper::CheckHasServiceWorker,
-                       this, url, other_url, std::move(callback)));
+                       this, url, std::move(callback)));
     return;
   }
   if (!context_core_) {
@@ -447,7 +446,7 @@
     return;
   }
   context()->CheckHasServiceWorker(
-      net::SimplifyUrlForRequest(url), net::SimplifyUrlForRequest(other_url),
+      net::SimplifyUrlForRequest(url),
       base::BindOnce(&ServiceWorkerContextWrapper::DidCheckHasServiceWorker,
                      this, std::move(callback)));
 }
@@ -1008,8 +1007,7 @@
 base::WeakPtr<ServiceWorkerProviderHost>
 ServiceWorkerContextWrapper::PreCreateHostForSharedWorker(
     int process_id,
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr*
-        out_provider_info) {
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr* out_provider_info) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
 
   return ServiceWorkerProviderHost::PreCreateForSharedWorker(
diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h
index eedc20a..d2b8ad8c 100644
--- a/content/browser/service_worker/service_worker_context_wrapper.h
+++ b/content/browser/service_worker/service_worker_context_wrapper.h
@@ -133,7 +133,6 @@
   void DeleteForOrigin(const GURL& origin, ResultCallback callback) override;
   void PerformStorageCleanup(base::OnceClosure callback) override;
   void CheckHasServiceWorker(const GURL& url,
-                             const GURL& other_url,
                              CheckHasServiceWorkerCallback callback) override;
   void ClearAllServiceWorkersForTest(base::OnceClosure callback) override;
   void StartWorkerForScope(const GURL& scope,
@@ -291,8 +290,7 @@
   // Must be called on the IO thread.
   base::WeakPtr<ServiceWorkerProviderHost> PreCreateHostForSharedWorker(
       int process_id,
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr*
-          out_provider_info);
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr* out_provider_info);
 
  private:
   friend class BackgroundSyncManagerTest;
diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc
index 79422c7..0137f9a8 100644
--- a/content/browser/service_worker/service_worker_database.cc
+++ b/content/browser/service_worker/service_worker_database.cc
@@ -31,7 +31,7 @@
 // =======================
 //
 // NOTE
-// - int64_t value is serialized as a string by base::Int64ToString().
+// - int64_t value is serialized as a string by base::NumberToString().
 // - GURL value is serialized as a string by GURL::spec().
 //
 // Version 1 (in sorted order)
@@ -148,19 +148,19 @@
 }
 
 std::string CreateRegistrationKey(int64_t registration_id, const GURL& origin) {
-  return CreateRegistrationKeyPrefix(origin)
-      .append(base::Int64ToString(registration_id));
+  return CreateRegistrationKeyPrefix(origin).append(
+      base::NumberToString(registration_id));
 }
 
 std::string CreateResourceRecordKeyPrefix(int64_t version_id) {
   return base::StringPrintf("%s%s%c", service_worker_internals::kResKeyPrefix,
-                            base::Int64ToString(version_id).c_str(),
+                            base::NumberToString(version_id).c_str(),
                             service_worker_internals::kKeySeparator);
 }
 
 std::string CreateResourceRecordKey(int64_t version_id, int64_t resource_id) {
-  return CreateResourceRecordKeyPrefix(version_id).append(
-      base::Int64ToString(resource_id));
+  return CreateResourceRecordKeyPrefix(version_id)
+      .append(base::NumberToString(resource_id));
 }
 
 std::string CreateUniqueOriginKey(const GURL& origin) {
@@ -169,14 +169,14 @@
 }
 
 std::string CreateResourceIdKey(const char* key_prefix, int64_t resource_id) {
-  return base::StringPrintf(
-      "%s%s", key_prefix, base::Int64ToString(resource_id).c_str());
+  return base::StringPrintf("%s%s", key_prefix,
+                            base::NumberToString(resource_id).c_str());
 }
 
 std::string CreateUserDataKeyPrefix(int64_t registration_id) {
   return base::StringPrintf("%s%s%c",
                             service_worker_internals::kRegUserDataKeyPrefix,
-                            base::Int64ToString(registration_id).c_str(),
+                            base::NumberToString(registration_id).c_str(),
                             service_worker_internals::kKeySeparator);
 }
 
@@ -194,13 +194,13 @@
 std::string CreateHasUserDataKey(int64_t registration_id,
                                  const std::string& user_data_name) {
   return CreateHasUserDataKeyPrefix(user_data_name)
-      .append(base::Int64ToString(registration_id));
+      .append(base::NumberToString(registration_id));
 }
 
 std::string CreateRegistrationIdToOriginKey(int64_t registration_id) {
   return base::StringPrintf("%s%s",
                             service_worker_internals::kRegIdToOriginKeyPrefix,
-                            base::Int64ToString(registration_id).c_str());
+                            base::NumberToString(registration_id).c_str());
 }
 
 void PutUniqueOriginToBatch(const GURL& origin,
@@ -1817,7 +1817,7 @@
     // Write database default values.
     batch->Put(
         service_worker_internals::kDatabaseVersionKey,
-        base::Int64ToString(service_worker_internals::kCurrentSchemaVersion));
+        base::NumberToString(service_worker_internals::kCurrentSchemaVersion));
     state_ = DATABASE_STATE_INITIALIZED;
   }
 
@@ -1834,7 +1834,7 @@
   if (next_avail_registration_id_ <= used_id) {
     next_avail_registration_id_ = used_id + 1;
     batch->Put(service_worker_internals::kNextRegIdKey,
-               base::Int64ToString(next_avail_registration_id_));
+               base::NumberToString(next_avail_registration_id_));
   }
 }
 
@@ -1845,7 +1845,7 @@
   if (next_avail_resource_id_ <= used_id) {
     next_avail_resource_id_ = used_id + 1;
     batch->Put(service_worker_internals::kNextResIdKey,
-               base::Int64ToString(next_avail_resource_id_));
+               base::NumberToString(next_avail_resource_id_));
   }
 }
 
@@ -1856,7 +1856,7 @@
   if (next_avail_version_id_ <= used_id) {
     next_avail_version_id_ = used_id + 1;
     batch->Put(service_worker_internals::kNextVerIdKey,
-               base::Int64ToString(next_avail_version_id_));
+               base::NumberToString(next_avail_version_id_));
   }
 }
 
diff --git a/content/browser/service_worker/service_worker_database_unittest.cc b/content/browser/service_worker/service_worker_database_unittest.cc
index 305f0ff..2ba6e26 100644
--- a/content/browser/service_worker/service_worker_database_unittest.cc
+++ b/content/browser/service_worker/service_worker_database_unittest.cc
@@ -180,7 +180,7 @@
   // Emulate an obsolete schema version.
   int64_t old_db_version = 1;
   leveldb::WriteBatch batch;
-  batch.Put("INITDATA_DB_VERSION", base::Int64ToString(old_db_version));
+  batch.Put("INITDATA_DB_VERSION", base::NumberToString(old_db_version));
   ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, database->WriteBatch(&batch));
   db_version = -1;
   EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
@@ -220,7 +220,7 @@
   // Emulate a corrupted schema version.
   int64_t corrupted_db_version = -10;
   leveldb::WriteBatch batch;
-  batch.Put("INITDATA_DB_VERSION", base::Int64ToString(corrupted_db_version));
+  batch.Put("INITDATA_DB_VERSION", base::NumberToString(corrupted_db_version));
   ASSERT_EQ(ServiceWorkerDatabase::STATUS_OK, database->WriteBatch(&batch));
   db_version = -1;
   EXPECT_EQ(ServiceWorkerDatabase::STATUS_ERROR_CORRUPTED,
diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc
index 3fd03a4..4940cfc 100644
--- a/content/browser/service_worker/service_worker_internals_ui.cc
+++ b/content/browser/service_worker/service_worker_internals_ui.cc
@@ -146,7 +146,7 @@
   }
 
   info->SetString("script_url", version.script_url.spec());
-  info->SetString("version_id", base::Int64ToString(version.version_id));
+  info->SetString("version_id", base::NumberToString(version.version_id));
   info->SetInteger("process_id",
                    static_cast<int>(GetRealProcessId(version.process_id)));
   info->SetInteger("process_host_id", version.process_id);
@@ -162,7 +162,7 @@
     auto registration_info = std::make_unique<DictionaryValue>();
     registration_info->SetString("scope", registration.scope.spec());
     registration_info->SetString(
-        "registration_id", base::Int64ToString(registration.registration_id));
+        "registration_id", base::NumberToString(registration.registration_id));
     registration_info->SetBoolean("navigation_preload_enabled",
                                   registration.navigation_preload_enabled);
     registration_info->SetInteger(
@@ -254,7 +254,7 @@
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     web_ui_->CallJavascriptFunctionUnsafe(
         "serviceworker.onRunningStateChanged", Value(partition_id_),
-        Value(base::Int64ToString(version_id)));
+        Value(base::NumberToString(version_id)));
   }
   void OnVersionStateChanged(int64_t version_id,
                              const GURL& scope,
@@ -262,14 +262,14 @@
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     web_ui_->CallJavascriptFunctionUnsafe(
         "serviceworker.onVersionStateChanged", Value(partition_id_),
-        Value(base::Int64ToString(version_id)));
+        Value(base::NumberToString(version_id)));
   }
   void OnErrorReported(int64_t version_id,
                        const ErrorInfo& info) override {
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     std::vector<std::unique_ptr<const Value>> args;
     args.push_back(std::make_unique<Value>(partition_id_));
-    args.push_back(std::make_unique<Value>(base::Int64ToString(version_id)));
+    args.push_back(std::make_unique<Value>(base::NumberToString(version_id)));
     auto value = std::make_unique<DictionaryValue>();
     value->SetString("message", info.error_message);
     value->SetInteger("lineNumber", info.line_number);
@@ -284,7 +284,7 @@
     DCHECK_CURRENTLY_ON(BrowserThread::UI);
     std::vector<std::unique_ptr<const Value>> args;
     args.push_back(std::make_unique<Value>(partition_id_));
-    args.push_back(std::make_unique<Value>(base::Int64ToString(version_id)));
+    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);
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index fb9753cb..97746e0 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -504,11 +504,11 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
-    mojom::EmbeddedWorkerInstanceHostAssociatedPtr instance_host_ptr;
+    blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtr instance_host_ptr;
     instance_host_ptr.Bind(std::move(instance_host));
     instance_host_ptr->OnStopped();
     base::RunLoop().RunUntilIdle();
@@ -1012,7 +1012,7 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
@@ -1054,7 +1054,7 @@
         version->SetMainScriptHttpResponseInfo(
             EmbeddedWorkerTestHelper::CreateHttpResponseInfo());
 
-        mojom::EmbeddedWorkerInstanceHostAssociatedPtr instance_host_ptr;
+        blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtr instance_host_ptr;
         instance_host_ptr.Bind(std::move(instance_host));
         instance_host_ptr->OnScriptLoaded();
         base::RunLoop().RunUntilIdle();
@@ -1155,7 +1155,7 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
@@ -1928,7 +1928,7 @@
   }
 
  protected:
-  void StartWorker(mojom::EmbeddedWorkerStartParamsPtr params) override {
+  void StartWorker(blink::mojom::EmbeddedWorkerStartParamsPtr params) override {
     ASSERT_TRUE(next_pause_after_download_.has_value());
     EXPECT_EQ(next_pause_after_download_.value(), params->pause_after_download);
     num_of_startworker_++;
diff --git a/content/browser/service_worker/service_worker_metrics.h b/content/browser/service_worker/service_worker_metrics.h
index 0f81f50..4112e80 100644
--- a/content/browser/service_worker/service_worker_metrics.h
+++ b/content/browser/service_worker/service_worker_metrics.h
@@ -14,10 +14,10 @@
 #include "content/browser/service_worker/service_worker_context_request_handler.h"
 #include "content/browser/service_worker/service_worker_database.h"
 #include "content/browser/service_worker/service_worker_installed_script_reader.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/browser/service_worker_context.h"
 #include "services/network/public/mojom/fetch_api.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/embedded_worker.mojom.h"
 #include "ui/base/page_transition_types.h"
 
 class GURL;
diff --git a/content/browser/service_worker/service_worker_new_script_loader.cc b/content/browser/service_worker/service_worker_new_script_loader.cc
index 5389fe6..2ebf5855 100644
--- a/content/browser/service_worker/service_worker_new_script_loader.cc
+++ b/content/browser/service_worker/service_worker_new_script_loader.cc
@@ -106,18 +106,19 @@
     resource_request.load_flags |= net::LOAD_BYPASS_CACHE;
   }
 
-  // Create response readers only when we have to do the byte-for-byte check.
-  std::unique_ptr<ServiceWorkerResponseReader> compare_reader;
-  std::unique_ptr<ServiceWorkerResponseReader> copy_reader;
   ServiceWorkerStorage* storage = version_->context()->storage();
   if (incumbent_cache_resource_id != kInvalidServiceWorkerResourceId) {
-    compare_reader = storage->CreateResponseReader(incumbent_cache_resource_id);
-    copy_reader = storage->CreateResponseReader(incumbent_cache_resource_id);
+    // Create response readers only when we have to do the byte-for-byte check.
+    cache_writer_ = ServiceWorkerCacheWriter::CreateForComparison(
+        storage->CreateResponseReader(incumbent_cache_resource_id),
+        storage->CreateResponseReader(incumbent_cache_resource_id),
+        storage->CreateResponseWriter(cache_resource_id),
+        false /* pause_when_not_identical */);
+  } else {
+    // The script is new, create a cache writer for write back.
+    cache_writer_ = ServiceWorkerCacheWriter::CreateForWriteBack(
+        storage->CreateResponseWriter(cache_resource_id));
   }
-  cache_writer_ = std::make_unique<ServiceWorkerCacheWriter>(
-      std::move(compare_reader), std::move(copy_reader),
-      storage->CreateResponseWriter(cache_resource_id),
-      false /* pause_when_not_identical */);
 
   version_->script_cache_map()->NotifyStartedCaching(request_url_,
                                                      cache_resource_id);
diff --git a/content/browser/service_worker/service_worker_object_host_unittest.cc b/content/browser/service_worker/service_worker_object_host_unittest.cc
index 7dcbd52..25aeb8f 100644
--- a/content/browser/service_worker/service_worker_object_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_object_host_unittest.cc
@@ -80,11 +80,11 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
-    mojom::EmbeddedWorkerInstanceHostAssociatedPtr instance_host_ptr;
+    blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtr instance_host_ptr;
     instance_host_ptr.Bind(std::move(instance_host));
     instance_host_ptr->OnStopped();
     base::RunLoop().RunUntilIdle();
diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc
index 350603de2..e4a55e8 100644
--- a/content/browser/service_worker/service_worker_provider_host.cc
+++ b/content/browser/service_worker/service_worker_provider_host.cc
@@ -254,8 +254,7 @@
 ServiceWorkerProviderHost::PreCreateForSharedWorker(
     base::WeakPtr<ServiceWorkerContextCore> context,
     int process_id,
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr*
-        out_provider_info) {
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr* out_provider_info) {
   auto host = base::WrapUnique(new ServiceWorkerProviderHost(
       ChildProcessHost::kInvalidUniqueID,
       blink::mojom::ServiceWorkerProviderHostInfo::New(
diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h
index d3d6b42..cb560981 100644
--- a/content/browser/service_worker/service_worker_provider_host.h
+++ b/content/browser/service_worker/service_worker_provider_host.h
@@ -167,8 +167,7 @@
   static base::WeakPtr<ServiceWorkerProviderHost> PreCreateForSharedWorker(
       base::WeakPtr<ServiceWorkerContextCore> context,
       int process_id,
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr*
-          out_provider_info);
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr* out_provider_info);
 
   // Used to create a ServiceWorkerProviderHost when the renderer-side provider
   // is created. This ProviderHost will be created for the process specified by
diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc
index 33c69d2..58d394c 100644
--- a/content/browser/service_worker/service_worker_provider_host_unittest.cc
+++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc
@@ -884,7 +884,7 @@
        ReservedClientsAreNotExposedToClientsAPI) {
   {
     auto provider_info =
-        blink::mojom::ServiceWorkerProviderInfoForSharedWorker::New();
+        blink::mojom::ServiceWorkerProviderInfoForWorker::New();
     base::WeakPtr<ServiceWorkerProviderHost> host =
         ServiceWorkerProviderHost::PreCreateForSharedWorker(
             context_->AsWeakPtr(), helper_->mock_render_process_id(),
@@ -947,8 +947,7 @@
 
 // Tests the client phase transitions for a shared worker.
 TEST_P(ServiceWorkerProviderHostTest, ClientPhaseForSharedWorker) {
-  auto provider_info =
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorker::New();
+  auto provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
   base::WeakPtr<ServiceWorkerProviderHost> host =
       ServiceWorkerProviderHost::PreCreateForSharedWorker(
           context_->AsWeakPtr(), helper_->mock_render_process_id(),
diff --git a/content/browser/service_worker/service_worker_script_loader_factory.cc b/content/browser/service_worker/service_worker_script_loader_factory.cc
index 86b6e12..9e6dd9b 100644
--- a/content/browser/service_worker/service_worker_script_loader_factory.cc
+++ b/content/browser/service_worker/service_worker_script_loader_factory.cc
@@ -5,6 +5,10 @@
 #include "content/browser/service_worker/service_worker_script_loader_factory.h"
 
 #include <memory>
+#include <string>
+#include <utility>
+
+#include "content/browser/service_worker/service_worker_cache_writer.h"
 #include "content/browser/service_worker/service_worker_context_core.h"
 #include "content/browser/service_worker/service_worker_installed_script_loader.h"
 #include "content/browser/service_worker/service_worker_new_script_loader.h"
@@ -23,7 +27,8 @@
     scoped_refptr<network::SharedURLLoaderFactory> loader_factory)
     : context_(context),
       provider_host_(provider_host),
-      loader_factory_(std::move(loader_factory)) {
+      loader_factory_(std::move(loader_factory)),
+      weak_factory_(this) {
   DCHECK(provider_host_->IsProviderForServiceWorker());
   DCHECK(loader_factory_);
 }
@@ -55,10 +60,22 @@
   //    error. This happens when the script is newly imported after
   //    installation, which is disallowed by the spec.
   // C) service worker is not installed, script is installed: serve from
-  //    storage (use ServceWorkerInstalledScriptLoader)
-  // D) service worker is not installed, script is not installed: serve from
-  //    network with installing the script (use ServceWorkerNewScriptLoader)
-  //    This is the common case: load the script and install it.
+  //    storage (use ServiceWorkerInstalledScriptLoader)
+  // D) service worker is not installed, script is not installed:
+  //    When ServiceWorkerImportedScriptsUpdateCheck is enabled, there are
+  //    three sub cases:
+  //    1) If compared script info exists and specifies that the script is
+  //       installed in an old service worker and content is not changed, then
+  //       copy the old script into the new service worker and load it using
+  //       ServiceWorkerInstalledScriptLoader.
+  //    2) If compared script info exists and specifies that the script is
+  //       installed in an old service worker but content has changed, then
+  //       resume the paused state in the compared script info to load the
+  //       script.
+  //    3) For other cases or if ServiceWorkerImportedScriptsUpdateCheck is not
+  //       enabled, serve from network with installing the script
+  //       (use ServiceWorkerNewScriptLoader).
+  //       This is the common case: load the script and install it.
 
   // Case A and C:
   scoped_refptr<ServiceWorkerVersion> version =
@@ -82,6 +99,44 @@
   }
 
   // Case D:
+  // Compared script info is used to decide which sub case should be used.
+  // If there is no compared script info, goto D.3 directly.
+  const auto& compared_script_info_map = version->compared_script_info_map();
+  if (!compared_script_info_map.empty()) {
+    // ServiceWorkerImportedScriptsUpdateCheck must be enabled if there is
+    // compared script info.
+    DCHECK(blink::ServiceWorkerUtils::IsImportedScriptUpdateCheckEnabled());
+    auto it = compared_script_info_map.find(resource_request.url);
+    if (it != compared_script_info_map.end()) {
+      switch (it->second.result) {
+        case ServiceWorkerSingleScriptUpdateChecker::Result::kFailed:
+          // Network failure is treated as D.1
+        case ServiceWorkerSingleScriptUpdateChecker::Result::kIdentical:
+          // Case D.1:
+          CopyScript(
+              it->first, it->second.old_resource_id,
+              base::BindOnce(
+                  &ServiceWorkerScriptLoaderFactory::OnCopyScriptFinished,
+                  weak_factory_.GetWeakPtr(), std::move(request), options,
+                  resource_request, std::move(client)));
+          return;
+        case ServiceWorkerSingleScriptUpdateChecker::Result::kDifferent:
+          // Case D.2:
+          // TODO(https://crbug.com/648295): Currently, this case is treated
+          // the same as case D.3. In future, the paused state in compared
+          // script info should be resumed instead of a fresh download.
+          NOTIMPLEMENTED();
+          break;
+        case ServiceWorkerSingleScriptUpdateChecker::Result::kNotCompared:
+          // This is invalid, as scripts in compared script info must have been
+          // compared.
+          NOTREACHED();
+          return;
+      }
+    }
+  }
+
+  // Case D.3:
   mojo::MakeStrongBinding(
       std::make_unique<ServiceWorkerNewScriptLoader>(
           routing_id, request_id, options, resource_request, std::move(client),
@@ -128,4 +183,62 @@
   return true;
 }
 
+void ServiceWorkerScriptLoaderFactory::CopyScript(
+    const GURL& url,
+    int64_t resource_id,
+    base::OnceCallback<void(int64_t, net::Error)> callback) {
+  ServiceWorkerStorage* storage = context_->storage();
+  int64_t new_resource_id = storage->NewResourceId();
+
+  cache_writer_ = ServiceWorkerCacheWriter::CreateForCopy(
+      storage->CreateResponseReader(resource_id),
+      storage->CreateResponseWriter(new_resource_id));
+
+  scoped_refptr<ServiceWorkerVersion> version =
+      provider_host_->running_hosted_version();
+  version->script_cache_map()->NotifyStartedCaching(url, new_resource_id);
+
+  net::Error error = cache_writer_->StartCopy(
+      base::BindOnce(std::move(callback), new_resource_id));
+
+  // Run the callback directly if the operation completed or failed
+  // synchronously.
+  if (net::ERR_IO_PENDING != error) {
+    std::move(callback).Run(new_resource_id, error);
+  }
+}
+
+void ServiceWorkerScriptLoaderFactory::OnCopyScriptFinished(
+    network::mojom::URLLoaderRequest request,
+    uint32_t options,
+    const network::ResourceRequest& resource_request,
+    network::mojom::URLLoaderClientPtr client,
+    int64_t new_resource_id,
+    net::Error error) {
+  int64_t resource_size = cache_writer_->bytes_written();
+  cache_writer_.reset();
+  scoped_refptr<ServiceWorkerVersion> version =
+      provider_host_->running_hosted_version();
+
+  if (error != net::OK) {
+    version->script_cache_map()->NotifyFinishedCaching(
+        resource_request.url, resource_size, error,
+        kServiceWorkerCopyScriptError);
+
+    client->OnComplete(network::URLLoaderCompletionStatus(error));
+    return;
+  }
+
+  // The copy operation is successful, add the newly copied resource record to
+  // the script cache map to identify that the script is installed.
+  version->script_cache_map()->NotifyFinishedCaching(
+      resource_request.url, resource_size, net::OK, std::string());
+
+  // Use ServiceWorkerInstalledScriptLoader to load the new copy.
+  mojo::MakeStrongBinding(
+      std::make_unique<ServiceWorkerInstalledScriptLoader>(
+          options, std::move(client),
+          context_->storage()->CreateResponseReader(new_resource_id)),
+      std::move(request));
+}
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_script_loader_factory.h b/content/browser/service_worker/service_worker_script_loader_factory.h
index ca286493..9567a92 100644
--- a/content/browser/service_worker/service_worker_script_loader_factory.h
+++ b/content/browser/service_worker/service_worker_script_loader_factory.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_SCRIPT_LOADER_FACTORY_H_
 #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_SCRIPT_LOADER_FACTORY_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "content/common/content_export.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
@@ -16,6 +18,7 @@
 
 namespace content {
 
+class ServiceWorkerCacheWriter;
 class ServiceWorkerContextCore;
 class ServiceWorkerProviderHost;
 
@@ -59,12 +62,40 @@
   bool CheckIfScriptRequestIsValid(
       const network::ResourceRequest& resource_request);
 
+  // Used only when ServiceWorkerImportedScriptUpdateCheck is enabled.
+  //
+  // The callback is called once the copy is done. It normally runs
+  // asynchronously, and would be synchronous if the operation completes
+  // synchronously. The first parameter of the callback is the new resource id
+  // and the second parameter is the result of the operation. net::OK means
+  // success.
+  void CopyScript(const GURL& url,
+                  int64_t resource_id,
+                  base::OnceCallback<void(int64_t, net::Error)> callback);
+
+  // This method is called to notify that the operation triggered by
+  // CopyScript() completed.
+  //
+  // If the copy operation is successful, a ServiceWorkerInstalledScriptLoader
+  // would be created to load the new copy.
+  void OnCopyScriptFinished(network::mojom::URLLoaderRequest request,
+                            uint32_t options,
+                            const network::ResourceRequest& resource_request,
+                            network::mojom::URLLoaderClientPtr client,
+                            int64_t new_resource_id,
+                            net::Error error);
+
   base::WeakPtr<ServiceWorkerContextCore> context_;
   base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
   scoped_refptr<network::SharedURLLoaderFactory> loader_factory_;
 
   mojo::BindingSet<network::mojom::URLLoaderFactory> bindings_;
 
+  // Used to copy script started at CopyScript().
+  std::unique_ptr<ServiceWorkerCacheWriter> cache_writer_;
+
+  base::WeakPtrFactory<ServiceWorkerScriptLoaderFactory> weak_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(ServiceWorkerScriptLoaderFactory);
 };
 
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.cc b/content/browser/service_worker/service_worker_single_script_update_checker.cc
index ed114993..5851558d 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.cc
@@ -64,7 +64,6 @@
 
 ServiceWorkerSingleScriptUpdateChecker::ServiceWorkerSingleScriptUpdateChecker(
     const GURL& url,
-    int64_t resource_id,
     bool is_main_script,
     scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
     std::unique_ptr<ServiceWorkerResponseReader> compare_reader,
@@ -72,7 +71,6 @@
     std::unique_ptr<ServiceWorkerResponseWriter> writer,
     ResultCallback callback)
     : script_url_(url),
-      resource_id_(resource_id),
       network_client_binding_(this),
       network_watcher_(FROM_HERE,
                        mojo::SimpleWatcher::ArmingPolicy::MANUAL,
@@ -89,10 +87,7 @@
 
   // TODO(momohatt): Handle cases where force_bypass_cache is enabled.
 
-  // |compare_reader| shouldn't be a nullptr, which forces
-  // ServiceWorkerCacheWriter to do the comparison.
-  DCHECK(compare_reader);
-  cache_writer_ = std::make_unique<ServiceWorkerCacheWriter>(
+  cache_writer_ = ServiceWorkerCacheWriter::CreateForComparison(
       std::move(compare_reader), std::move(copy_reader), std::move(writer),
       true /* pause_when_not_identical */);
 
@@ -377,14 +372,13 @@
     auto paused_state = std::make_unique<PausedState>(
         std::move(cache_writer_), std::move(network_loader_),
         network_client_binding_.Unbind(), std::move(network_consumer_));
-    std::move(callback_).Run(script_url_, resource_id_, result,
-                             std::move(paused_state));
+    std::move(callback_).Run(script_url_, result, std::move(paused_state));
     return;
   }
   network_loader_.reset();
   network_client_binding_.Close();
   network_consumer_.reset();
-  std::move(callback_).Run(script_url_, resource_id_, result, nullptr);
+  std::move(callback_).Run(script_url_, result, nullptr);
 }
 
 ServiceWorkerSingleScriptUpdateChecker::PausedState::PausedState(
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker.h b/content/browser/service_worker/service_worker_single_script_update_checker.h
index bbd365a60..81e2ce97 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker.h
+++ b/content/browser/service_worker/service_worker_single_script_update_checker.h
@@ -61,14 +61,13 @@
   // is transferred to the callback in the PausedState only when the result is
   // Result::kDifferent. Otherwise it's set to nullptr.
   using ResultCallback = base::OnceCallback<
-      void(const GURL&, int64_t, Result, std::unique_ptr<PausedState>)>;
+      void(const GURL&, Result, std::unique_ptr<PausedState>)>;
 
   // Both |compare_reader| and |copy_reader| should be created from the same
   // resource ID, and this ID should locate where the script specified with
   // |url| is stored. |writer| should be created with a new resource ID.
   ServiceWorkerSingleScriptUpdateChecker(
       const GURL& url,
-      int64_t resource_id,
       bool is_main_script,
       scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
       std::unique_ptr<ServiceWorkerResponseReader> compare_reader,
@@ -120,7 +119,6 @@
   void Finish(Result result);
 
   const GURL script_url_;
-  const int64_t resource_id_;
 
   network::mojom::URLLoaderPtr network_loader_;
   mojo::Binding<network::mojom::URLLoaderClient> network_client_binding_;
diff --git a/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
index 0c98e06d..38c55e6 100644
--- a/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
+++ b/content/browser/service_worker/service_worker_single_script_update_checker_unittest.cc
@@ -29,12 +29,10 @@
   struct CheckResult {
     CheckResult(
         const GURL& script_url,
-        int64_t id,
         ServiceWorkerSingleScriptUpdateChecker::Result compare_result,
         std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
             paused_state)
         : url(script_url),
-          resource_id(id),
           result(compare_result),
           paused_state(std::move(paused_state)) {}
 
@@ -45,7 +43,6 @@
     ~CheckResult() = default;
 
     GURL url;
-    int64_t resource_id;
     ServiceWorkerSingleScriptUpdateChecker::Result result;
     std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
         paused_state;
@@ -81,18 +78,18 @@
       base::Optional<CheckResult>* out_check_result) {
     helper_->SetNetworkFactory(loader_factory);
     return std::make_unique<ServiceWorkerSingleScriptUpdateChecker>(
-        GURL(url), 0 /* resource_id */, true /* is_main_script */,
+        GURL(url), true /* is_main_script */,
         helper_->url_loader_factory_getter()->GetNetworkFactory(),
         std::move(compare_reader), std::move(copy_reader), std::move(writer),
         base::BindOnce(
             [](base::Optional<CheckResult>* out_check_result_param,
-               const GURL& script_url, int64_t resource_id,
+               const GURL& script_url,
                ServiceWorkerSingleScriptUpdateChecker::Result result,
                std::unique_ptr<
                    ServiceWorkerSingleScriptUpdateChecker::PausedState>
                    paused_state) {
-              *out_check_result_param = CheckResult(
-                  script_url, resource_id, result, std::move(paused_state));
+              *out_check_result_param =
+                  CheckResult(script_url, result, std::move(paused_state));
             },
             out_check_result));
   }
@@ -148,7 +145,6 @@
   EXPECT_EQ(check_result.value().result,
             ServiceWorkerSingleScriptUpdateChecker::Result::kIdentical);
   EXPECT_EQ(check_result.value().url, kScriptURL);
-  EXPECT_EQ(check_result.value().resource_id, 0);
   EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
 }
 
@@ -181,7 +177,6 @@
   EXPECT_EQ(check_result.value().result,
             ServiceWorkerSingleScriptUpdateChecker::Result::kDifferent);
   EXPECT_EQ(check_result.value().url, kScriptURL);
-  EXPECT_EQ(check_result.value().resource_id, 0);
   EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
 }
 
@@ -215,7 +210,6 @@
   EXPECT_EQ(check_result.value().result,
             ServiceWorkerSingleScriptUpdateChecker::Result::kDifferent);
   EXPECT_EQ(check_result.value().url, kScriptURL);
-  EXPECT_EQ(check_result.value().resource_id, 0);
   EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
 }
 
@@ -247,7 +241,6 @@
   EXPECT_EQ(check_result.value().result,
             ServiceWorkerSingleScriptUpdateChecker::Result::kDifferent);
   EXPECT_EQ(check_result.value().url, kScriptURL);
-  EXPECT_EQ(check_result.value().resource_id, 0);
   EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
 }
 
@@ -283,7 +276,6 @@
   EXPECT_EQ(check_result.value().result,
             ServiceWorkerSingleScriptUpdateChecker::Result::kDifferent);
   EXPECT_EQ(check_result.value().url, kScriptURL);
-  EXPECT_EQ(check_result.value().resource_id, 0);
   EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
 }
 
@@ -330,7 +322,6 @@
   EXPECT_EQ(check_result.value().result,
             ServiceWorkerSingleScriptUpdateChecker::Result::kIdentical);
   EXPECT_EQ(check_result.value().url, kScriptURL);
-  EXPECT_EQ(check_result.value().resource_id, 0);
   EXPECT_FALSE(check_result.value().paused_state);
   EXPECT_TRUE(compare_reader_rawptr->AllExpectedReadsDone());
 }
diff --git a/content/browser/service_worker/service_worker_update_checker.cc b/content/browser/service_worker/service_worker_update_checker.cc
index 396768b..2626e09 100644
--- a/content/browser/service_worker/service_worker_update_checker.cc
+++ b/content/browser/service_worker/service_worker_update_checker.cc
@@ -33,8 +33,8 @@
 }
 
 void ServiceWorkerUpdateChecker::OnOneUpdateCheckFinished(
-    const GURL& script_url,
     int64_t old_resource_id,
+    const GURL& script_url,
     ServiceWorkerSingleScriptUpdateChecker::Result result,
     std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
         paused_state) {
@@ -96,10 +96,10 @@
 
   auto writer = storage->CreateResponseWriter(storage->NewResourceId());
   running_checker_ = std::make_unique<ServiceWorkerSingleScriptUpdateChecker>(
-      url, resource_id, is_main_script, loader_factory_,
-      std::move(compare_reader), std::move(copy_reader), std::move(writer),
+      url, is_main_script, loader_factory_, std::move(compare_reader),
+      std::move(copy_reader), std::move(writer),
       base::BindOnce(&ServiceWorkerUpdateChecker::OnOneUpdateCheckFinished,
-                     weak_factory_.GetWeakPtr()));
+                     weak_factory_.GetWeakPtr(), resource_id));
 }
 
 ServiceWorkerUpdateChecker::ComparedScriptInfo::ComparedScriptInfo() = default;
diff --git a/content/browser/service_worker/service_worker_update_checker.h b/content/browser/service_worker/service_worker_update_checker.h
index 819fa0e..a89fa53a 100644
--- a/content/browser/service_worker/service_worker_update_checker.h
+++ b/content/browser/service_worker/service_worker_update_checker.h
@@ -64,8 +64,8 @@
   std::map<GURL, ComparedScriptInfo> TakeComparedResults();
 
   void OnOneUpdateCheckFinished(
-      const GURL& script_url,
       int64_t old_resource_id,
+      const GURL& script_url,
       ServiceWorkerSingleScriptUpdateChecker::Result result,
       std::unique_ptr<ServiceWorkerSingleScriptUpdateChecker::PausedState>
           paused_state);
diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
index 91d62dc..3cf47e1e 100644
--- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc
@@ -505,7 +505,7 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
@@ -541,7 +541,7 @@
   bool pause_after_download_;
   blink::mojom::ServiceWorkerRequest start_worker_request_;
   blink::mojom::ControllerServiceWorkerRequest controller_request_;
-  mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo
+  blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo
       start_worker_instance_host_;
   blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info_;
   blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info_;
diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc
index 3965af63..f9841aa 100644
--- a/content/browser/service_worker/service_worker_version.cc
+++ b/content/browser/service_worker/service_worker_version.cc
@@ -32,7 +32,6 @@
 #include "content/browser/service_worker/service_worker_context_wrapper.h"
 #include "content/browser/service_worker/service_worker_installed_scripts_sender.h"
 #include "content/browser/service_worker/service_worker_registration.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "content/common/service_worker/service_worker_utils.h"
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/content_browser_client.h"
@@ -45,6 +44,7 @@
 #include "third_party/blink/public/common/origin_trials/trial_token_validator.h"
 #include "third_party/blink/public/common/service_worker/service_worker_type_converters.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
+#include "third_party/blink/public/mojom/service_worker/embedded_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_error_type.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
@@ -1609,7 +1609,7 @@
   provider_host_ = ServiceWorkerProviderHost::PreCreateForController(
       context(), base::WrapRefCounted(this), &provider_info);
 
-  auto params = mojom::EmbeddedWorkerStartParams::New();
+  auto params = blink::mojom::EmbeddedWorkerStartParams::New();
   params->service_worker_version_id = version_id_;
   params->scope = scope_;
   params->script_url = script_url_;
@@ -2134,4 +2134,5 @@
 ServiceWorkerVersion::TakePausedStateOfChangedScript(const GURL& script_url) {
   return std::move(compared_script_info_map_[script_url].paused_state);
 }
+
 }  // namespace content
diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc
index 6843f66..a60edc92 100644
--- a/content/browser/service_worker/service_worker_version_unittest.cc
+++ b/content/browser/service_worker/service_worker_version_unittest.cc
@@ -250,7 +250,7 @@
       bool pause_after_download,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       blink::mojom::ServiceWorkerInstalledScriptsInfoPtr installed_scripts_info)
       override {
@@ -297,9 +297,9 @@
   uint32_t current_mock_instance_index_ = 0;
   StartMode mode_ = StartMode::STALL;
 
-  std::map<
-      int /* embedded_worker_id */,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtr /* instance_host_ptr */>
+  std::map<int /* embedded_worker_id */,
+           blink::mojom::
+               EmbeddedWorkerInstanceHostAssociatedPtr /* instance_host_ptr */>
       instance_host_ptr_map_;
   std::map<int /* embedded_worker_id */, blink::mojom::ServiceWorkerRequest>
       service_worker_request_map_;
diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.cc b/content/browser/service_worker/service_worker_write_to_cache_job.cc
index 9bbea4f..95be06c 100644
--- a/content/browser/service_worker/service_worker_write_to_cache_job.cc
+++ b/content/browser/service_worker/service_worker_write_to_cache_job.cc
@@ -118,19 +118,17 @@
     return;
   }
 
-  // Create response readers only when we have to do the byte-for-byte check.
-  std::unique_ptr<ServiceWorkerResponseReader> compare_reader;
-  std::unique_ptr<ServiceWorkerResponseReader> copy_reader;
   if (ShouldByteForByteCheck()) {
-    compare_reader =
-        context_->storage()->CreateResponseReader(incumbent_resource_id_);
-    copy_reader =
-        context_->storage()->CreateResponseReader(incumbent_resource_id_);
+    // Create response readers only when we have to do the byte-for-byte check.
+    cache_writer_ = ServiceWorkerCacheWriter::CreateForComparison(
+        context_->storage()->CreateResponseReader(incumbent_resource_id_),
+        context_->storage()->CreateResponseReader(incumbent_resource_id_),
+        context_->storage()->CreateResponseWriter(resource_id_),
+        false /* pause_when_not_identical */);
+  } else {
+    cache_writer_ = ServiceWorkerCacheWriter::CreateForWriteBack(
+        context_->storage()->CreateResponseWriter(resource_id_));
   }
-  cache_writer_ = std::make_unique<ServiceWorkerCacheWriter>(
-      std::move(compare_reader), std::move(copy_reader),
-      context_->storage()->CreateResponseWriter(resource_id_),
-      false /* pause_when_not_identical */);
 
   version_->script_cache_map()->NotifyStartedCaching(url_, resource_id_);
   did_notify_started_ = true;
diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc
index b8b2749a..eb202b8 100644
--- a/content/browser/site_instance_impl.cc
+++ b/content/browser/site_instance_impl.cc
@@ -21,6 +21,7 @@
 #include "content/public/browser/render_process_host_factory.h"
 #include "content/public/browser/site_isolation_policy.h"
 #include "content/public/browser/web_ui_controller_factory.h"
+#include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
@@ -102,6 +103,33 @@
   return browsing_instance_->isolation_context();
 }
 
+RenderProcessHost* SiteInstanceImpl::GetDefaultProcessIfUsable() {
+  if (!base::FeatureList::IsEnabled(
+          features::kProcessSharingWithStrictSiteInstances)) {
+    return nullptr;
+  }
+  if (RequiresDedicatedProcess())
+    return nullptr;
+  return browsing_instance_->default_process();
+}
+
+void SiteInstanceImpl::MaybeSetBrowsingInstanceDefaultProcess() {
+  if (!base::FeatureList::IsEnabled(
+          features::kProcessSharingWithStrictSiteInstances)) {
+    return;
+  }
+  // Wait until this SiteInstance both has a site and a process
+  // assigned, so that we can be sure that RequiresDedicatedProcess()
+  // is accurate and we actually have a process to set.
+  if (!process_ || !has_site_ || RequiresDedicatedProcess())
+    return;
+  if (browsing_instance_->default_process()) {
+    DCHECK_EQ(process_, browsing_instance_->default_process());
+    return;
+  }
+  browsing_instance_->SetDefaultProcess(process_);
+}
+
 // static
 BrowsingInstanceId SiteInstanceImpl::NextBrowsingInstanceId() {
   return BrowsingInstance::NextBrowsingInstanceId();
@@ -152,6 +180,8 @@
     CHECK(process_);
     process_->AddObserver(this);
 
+    MaybeSetBrowsingInstanceDefaultProcess();
+
     // If we are using process-per-site, we need to register this process
     // for the current site so that we can find it again.  (If no site is set
     // at this time, we will register it in SetSite().)
@@ -223,6 +253,7 @@
       RenderProcessHostImpl::RegisterSoleProcessHostForSite(browser_context,
                                                             process_, this);
     }
+    MaybeSetBrowsingInstanceDefaultProcess();
   }
 }
 
diff --git a/content/browser/site_instance_impl.h b/content/browser/site_instance_impl.h
index 0ab3f78..da5a0ef 100644
--- a/content/browser/site_instance_impl.h
+++ b/content/browser/site_instance_impl.h
@@ -275,6 +275,10 @@
   // about the current BrowsingInstance.
   const IsolationContext& GetIsolationContext();
 
+  // If this SiteInstance doesn't require a dedicated process, this will return
+  // the BrowsingInstance's default process.
+  RenderProcessHost* GetDefaultProcessIfUsable();
+
  private:
   friend class BrowsingInstance;
   friend class SiteInstanceTestBrowserClient;
@@ -293,6 +297,13 @@
   // Used to restrict a process' origin access rights.
   void LockToOriginIfNeeded();
 
+  // If kProcessSharingWithStrictSiteInstances is enabled, this will check
+  // whether both a site and a process have been assigned to this SiteInstance,
+  // and if this doesn't require a dedicated process, will offer process_ to
+  // BrowsingInstance as the default process for SiteInstances that don't need
+  // a dedicated process.
+  void MaybeSetBrowsingInstanceDefaultProcess();
+
   // An object used to construct RenderProcessHosts.
   static const RenderProcessHostFactory* g_render_process_host_factory_;
 
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index 001baf7..72efd82 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -2295,8 +2295,14 @@
       nested_iframe_node->render_manager()->GetProxyToParent();
   CrossProcessFrameConnector* connector =
       proxy_to_parent->cross_process_frame_connector();
-  ASSERT_EQ(blink::mojom::FrameVisibility::kRenderedOutOfViewport,
-            connector->visibility());
+
+  while (blink::mojom::FrameVisibility::kRenderedOutOfViewport !=
+         connector->visibility()) {
+    base::RunLoop run_loop;
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, run_loop.QuitClosure(), TestTimeouts::tiny_timeout());
+    run_loop.Run();
+  }
 }
 
 // This test verifies that smooth scrolling works correctly inside nested OOPIFs
@@ -3799,9 +3805,9 @@
 
     NavigateFrameToURL(child, urls[i]);
 
-    EXPECT_EQ(base::IntToString(current_margin_width),
+    EXPECT_EQ(base::NumberToString(current_margin_width),
               EvalJs(child, "document.body.getAttribute('marginwidth');"));
-    EXPECT_EQ(base::IntToString(current_margin_height),
+    EXPECT_EQ(base::NumberToString(current_margin_height),
               EvalJs(child, "document.body.getAttribute('marginheight');"));
 
     current_margin_width += 5;
diff --git a/content/browser/speech/audio_encoder.cc b/content/browser/speech/audio_encoder.cc
index c036c2d..e32723b 100644
--- a/content/browser/speech/audio_encoder.cc
+++ b/content/browser/speech/audio_encoder.cc
@@ -82,7 +82,7 @@
 
 std::string AudioEncoder::GetMimeType() {
   return std::string(kContentTypeFLAC) +
-      base::UintToString(FLAC__stream_encoder_get_sample_rate(encoder_));
+         base::NumberToString(FLAC__stream_encoder_get_sample_rate(encoder_));
 }
 
 int AudioEncoder::GetBitsPerSample() {
diff --git a/content/browser/speech/speech_recognition_engine.cc b/content/browser/speech/speech_recognition_engine.cc
index 3bb628a..aac4f39 100644
--- a/content/browser/speech/speech_recognition_engine.cc
+++ b/content/browser/speech/speech_recognition_engine.cc
@@ -582,7 +582,7 @@
     uint32_t max_alternatives =
         std::min(kMaxMaxAlternatives, config_.max_hypotheses);
     upstream_args.push_back("maxAlternatives=" +
-                            base::UintToString(max_alternatives));
+                            base::NumberToString(max_alternatives));
   }
   upstream_args.push_back("app=chromium");
   for (const blink::mojom::SpeechRecognitionGrammar& grammar :
diff --git a/content/browser/speech/tts_win.cc b/content/browser/speech/tts_win.cc
index 78d1a58..ddddb25 100644
--- a/content/browser/speech/tts_win.cc
+++ b/content/browser/speech/tts_win.cc
@@ -109,8 +109,7 @@
     // The TTS api allows a range of -10 to 10 for speech pitch.
     // TODO(dtseng): cleanup if we ever use any other properties that
     // require xml.
-    std::wstring pitch_value =
-        base::IntToString16(static_cast<int>(params.pitch * 10 - 10));
+    std::wstring pitch_value = base::NumberToString16(params.pitch * 10 - 10);
     prefix = L"<pitch absmiddle=\"" + pitch_value + L"\">";
     suffix = L"</pitch>";
   }
diff --git a/content/browser/startup_helper.cc b/content/browser/startup_helper.cc
index 72d753a2..9d979d5 100644
--- a/content/browser/startup_helper.cc
+++ b/content/browser/startup_helper.cc
@@ -24,37 +24,25 @@
   // Mobile config, for iOS see ios/web/app/web_main_loop.cc.
   return std::make_unique<base::TaskScheduler::InitParams>(
       base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(2, 8, 0.1, 0),
+          base::RecommendedMaxNumberOfThreadsInPool(4, 8, 0.2, 0),
           base::TimeDelta::FromSeconds(30)),
       base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(2, 8, 0.1, 0),
-          base::TimeDelta::FromSeconds(30)),
-      base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.3, 0),
-          base::TimeDelta::FromSeconds(30)),
-      base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.3, 0),
-          base::TimeDelta::FromSeconds(60)));
+          base::RecommendedMaxNumberOfThreadsInPool(6, 8, 0.6, 0),
+          base::TimeDelta::FromSeconds(30)));
 #else
   // Desktop config.
   return std::make_unique<base::TaskScheduler::InitParams>(
       base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.1, 0),
+          base::RecommendedMaxNumberOfThreadsInPool(6, 8, 0.2, 0),
           base::TimeDelta::FromSeconds(30)),
       base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.1, 0),
-          base::TimeDelta::FromSeconds(40)),
-      base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(8, 32, 0.3, 0),
-          base::TimeDelta::FromSeconds(30)),
-      base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(8, 32, 0.3, 0),
-          base::TimeDelta::FromSeconds(60))
+          base::RecommendedMaxNumberOfThreadsInPool(16, 32, 0.6, 0),
+          base::TimeDelta::FromSeconds(30))
 #if defined(OS_WIN)
           ,
       base::TaskScheduler::InitParams::SharedWorkerPoolEnvironment::COM_MTA
 #endif  // defined(OS_WIN)
-      );
+  );
 #endif
 }
 
diff --git a/content/browser/streams/stream_url_request_job.cc b/content/browser/streams/stream_url_request_job.cc
index 89fb1ebe..0fc62ae 100644
--- a/content/browser/streams/stream_url_request_job.cc
+++ b/content/browser/streams/stream_url_request_job.cc
@@ -184,7 +184,7 @@
   error_code_ = net::ERR_METHOD_NOT_SUPPORTED;
 
   std::string status("HTTP/1.1 ");
-  status.append(base::IntToString(status_code));
+  status.append(base::NumberToString(status_code));
   status.append(" ");
   status.append(net::GetHttpReasonPhrase(status_code));
   status.append("\0\0", 2);
diff --git a/content/browser/vibration_browsertest.cc b/content/browser/vibration_browsertest.cc
index 5713aae..df3bba70 100644
--- a/content/browser/vibration_browsertest.cc
+++ b/content/browser/vibration_browsertest.cc
@@ -52,7 +52,7 @@
     bool result;
     RenderFrameHost* frame = shell()->web_contents()->GetMainFrame();
     std::string script = "domAutomationController.send(navigator.vibrate(" +
-                         base::IntToString(duration) + "))";
+                         base::NumberToString(duration) + "))";
     EXPECT_TRUE(ExecuteScriptAndExtractBool(frame, script, &result));
     return result;
   }
diff --git a/content/browser/web_package/http_structured_header.cc b/content/browser/web_package/http_structured_header.cc
index cab233b..b3284e56 100644
--- a/content/browser/web_package/http_structured_header.cc
+++ b/content/browser/web_package/http_structured_header.cc
@@ -50,6 +50,30 @@
     return input_.empty();
   }
 
+  // Parses a List of Lists ([SH] 4.2.4).
+  base::Optional<ListOfLists> ReadListOfLists() {
+    ListOfLists result;
+    while (true) {
+      std::vector<std::string> inner_list;
+      while (true) {
+        base::Optional<std::string> item = ReadItem();
+        if (!item)
+          return base::nullopt;
+        inner_list.push_back(*item);
+        SkipWhitespaces();
+        if (!ConsumeChar(';'))
+          break;
+        SkipWhitespaces();
+      }
+      result.push_back(std::move(inner_list));
+      SkipWhitespaces();
+      if (!ConsumeChar(','))
+        break;
+      SkipWhitespaces();
+    }
+    return result;
+  }
+
   // Parses an Item ([SH] 4.2.7).
   // Currently only limited types (non-negative integers, strings, tokens and
   // byte sequences) are supported, and all types are returned as a string
@@ -287,5 +311,13 @@
   return base::nullopt;
 }
 
+base::Optional<ListOfLists> ParseListOfLists(const base::StringPiece& str) {
+  StructuredHeaderParser parser(str);
+  base::Optional<ListOfLists> list_of_lists = parser.ReadListOfLists();
+  if (list_of_lists && parser.FinishParsing())
+    return list_of_lists;
+  return base::nullopt;
+}
+
 }  // namespace http_structured_header
 }  // namespace content
diff --git a/content/browser/web_package/http_structured_header.h b/content/browser/web_package/http_structured_header.h
index d31dbad3..8515445a 100644
--- a/content/browser/web_package/http_structured_header.h
+++ b/content/browser/web_package/http_structured_header.h
@@ -27,12 +27,15 @@
   ~ParameterisedIdentifier();
 };
 
-typedef std::vector<ParameterisedIdentifier> ParameterisedList;
+using ParameterisedList = std::vector<ParameterisedIdentifier>;
+using ListOfLists = std::vector<std::vector<std::string>>;
 
 CONTENT_EXPORT base::Optional<std::string> ParseItem(
     const base::StringPiece& str);
 CONTENT_EXPORT base::Optional<ParameterisedList> ParseParameterisedList(
     const base::StringPiece& str);
+CONTENT_EXPORT base::Optional<ListOfLists> ParseListOfLists(
+    const base::StringPiece& str);
 
 }  // namespace http_structured_header
 }  // namespace content
diff --git a/content/browser/web_package/http_structured_header_unittest.cc b/content/browser/web_package/http_structured_header_unittest.cc
index 083a2ec..14d8a21c 100644
--- a/content/browser/web_package/http_structured_header_unittest.cc
+++ b/content/browser/web_package/http_structured_header_unittest.cc
@@ -77,6 +77,41 @@
   }
 }
 
+TEST(StructuredHeaderTest, ParseListOfLists) {
+  struct TestCase {
+    const char* name;
+    const char* raw;
+    ListOfLists expected;  // empty if parse error is expected
+  } cases[] = {
+      {"basic list of lists", "1;2, 42;43", {{"1", "2"}, {"42", "43"}}},
+      {"empty list of lists", "", {}},
+      {"single item list of lists", "42", {{"42"}}},
+      {"no whitespace list of lists", "1,42", {{"1"}, {"42"}}},
+      {"no inner whitespace list of lists",
+       "1;2, 42;43",
+       {{"1", "2"}, {"42", "43"}}},
+      {"extra whitespace list of lists", "1 , 42", {{"1"}, {"42"}}},
+      {"extra inner whitespace list of lists",
+       "1 ; 2,42 ; 43",
+       {{"1", "2"}, {"42", "43"}}},
+      {"trailing comma list of lists", "1;2, 42,", {}},
+      {"trailing semicolon list of lists", "1;2, 42;43;", {}},
+      {"leading comma list of lists", ",1;2, 42", {}},
+      {"leading semicolon list of lists", ";1;2, 42;43", {}},
+      {"empty item list of lists", "1,,42", {}},
+      {"empty inner item list of lists", "1;;2,42", {}},
+  };
+  for (const auto& c : cases) {
+    base::Optional<ListOfLists> result = ParseListOfLists(c.raw);
+    if (!c.expected.empty()) {
+      EXPECT_TRUE(result.has_value()) << c.name;
+      EXPECT_EQ(*result, c.expected) << c.name;
+    } else {
+      EXPECT_FALSE(result.has_value()) << c.name;
+    }
+  }
+}
+
 inline bool operator==(const ParameterisedIdentifier& lhs,
                        const ParameterisedIdentifier& rhs) {
   return lhs.identifier == rhs.identifier && lhs.params == rhs.params;
diff --git a/content/browser/web_package/signed_exchange_loader.cc b/content/browser/web_package/signed_exchange_loader.cc
index d18ee15..c6975db0 100644
--- a/content/browser/web_package/signed_exchange_loader.cc
+++ b/content/browser/web_package/signed_exchange_loader.cc
@@ -66,7 +66,9 @@
   return base::LowerCaseEqualsASCII(content_type_options, kNoSniffHeaderValue);
 }
 
-constexpr static int kDefaultBufferSize = 64 * 1024;
+// The buffer size of DataPipe which is used to send the body to the renderer.
+// Use the same size as regular resource loading.
+constexpr static int kDefaultBufferSize = 512 * 1024;
 
 SignedExchangeHandlerFactory* g_signed_exchange_factory_for_testing_ = nullptr;
 
@@ -226,7 +228,11 @@
 }
 
 void SignedExchangeLoader::OnComplete(
-    const network::URLLoaderCompletionStatus& status) {}
+    const network::URLLoaderCompletionStatus& status) {
+  DCHECK(!encoded_data_length_);
+  encoded_data_length_ = status.encoded_data_length;
+  NotifyClientOnCompleteIfReady();
+}
 
 void SignedExchangeLoader::FollowRedirect(
     const std::vector<std::string>& removed_headers,
@@ -347,10 +353,23 @@
 }
 
 void SignedExchangeLoader::FinishReadingBody(int result) {
-  // TODO(https://crbug.com/803774): Fill the data length information too.
+  DCHECK(!decoded_body_read_result_);
+  decoded_body_read_result_ = result;
+  NotifyClientOnCompleteIfReady();
+}
+
+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_)
+    return;
+
+  // TODO(https://crbug.com/803774): Fill the data length information (
+  // encoded_body_length, decoded_body_length) too.
   network::URLLoaderCompletionStatus status;
-  status.error_code = result;
+  status.error_code = *decoded_body_read_result_;
   status.completion_time = base::TimeTicks::Now();
+  status.encoded_data_length = *encoded_data_length_;
 
   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 7346ab5..e555fbdb 100644
--- a/content/browser/web_package/signed_exchange_loader.h
+++ b/content/browser/web_package/signed_exchange_loader.h
@@ -114,6 +114,7 @@
       std::unique_ptr<net::SourceStream> payload_stream);
 
   void FinishReadingBody(int result);
+  void NotifyClientOnCompleteIfReady();
 
   const network::ResourceRequest outer_request_;
 
@@ -159,6 +160,11 @@
   base::Optional<GURL> fallback_url_;
   base::Optional<GURL> inner_request_url_;
 
+  // Set when URLLoaderClient::OnComplete() is called.
+  base::Optional<int64_t> encoded_data_length_;
+  // Set when |body_data_pipe_adapter_| finishes loading the decoded body.
+  base::Optional<int> decoded_body_read_result_;
+
   base::WeakPtrFactory<SignedExchangeLoader> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(SignedExchangeLoader);
diff --git a/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc b/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc
index 65b3ade..1634e0e3 100644
--- a/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc
+++ b/content/browser/webrtc/webrtc_audio_debug_recordings_browsertest.cc
@@ -20,9 +20,9 @@
 #include "net/test/embedded_test_server/embedded_test_server.h"
 
 #if defined(OS_WIN)
-#define IntToStringType base::IntToString16
+#define NumberToStringType base::NumberToString16
 #else
-#define IntToStringType base::IntToString
+#define NumberToStringType base::NumberToString
 #endif
 
 namespace {
@@ -39,9 +39,9 @@
 // "/tmp/.com.google.Chrome.Z6UC3P.12345.aec_dump.1".
 base::FilePath GetExpectedAecDumpFileName(const base::FilePath& base_file_path,
                                           int render_process_id) {
-  return base_file_path.AddExtension(IntToStringType(render_process_id))
+  return base_file_path.AddExtension(NumberToStringType(render_process_id))
       .AddExtension(FILE_PATH_LITERAL("aec_dump"))
-      .AddExtension(IntToStringType(kExpectedConsumerId));
+      .AddExtension(NumberToStringType(kExpectedConsumerId));
 }
 
 // Get the file names of the recordings. The name will be
diff --git a/content/browser/webrtc/webrtc_internals.cc b/content/browser/webrtc/webrtc_internals.cc
index 86c3b4b..c0d0ace6 100644
--- a/content/browser/webrtc/webrtc_internals.cc
+++ b/content/browser/webrtc/webrtc_internals.cc
@@ -34,12 +34,6 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "ui/shell_dialogs/select_file_policy.h"
 
-#if defined(OS_WIN)
-#define IntToStringType base::IntToString16
-#else
-#define IntToStringType base::IntToString
-#endif
-
 using base::ProcessId;
 using std::string;
 
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 20cd7e8..baf2e0e 100644
--- a/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
+++ b/content/browser/webrtc/webrtc_video_capture_service_enumeration_browsertest.cc
@@ -88,7 +88,11 @@
   }
 
   void RemoveVirtualDevice(const std::string& device_id) {
+    base::RunLoop wait_loop;
+    closure_to_be_called_on_devices_changed_ = wait_loop.QuitClosure();
     virtual_devices_by_id_.erase(device_id);
+    // Wait for confirmation from the service.
+    wait_loop.Run();
   }
 
   void DisconnectFromService() {
diff --git a/content/browser/worker_host/README.md b/content/browser/worker_host/README.md
index 878337f..0eab4cf8 100644
--- a/content/browser/worker_host/README.md
+++ b/content/browser/worker_host/README.md
@@ -1,8 +1,13 @@
-# Web Worker Host
+# Web Worker in Browser
 
-`content/browser/worker_host` implements the host side of web workers (dedicated
-workers and shared workers). It tracks the security principal of the worker in
-the renderer and uses it to broker access to mojo interfaces providing powerful
-web APIs. See: [Design doc].
+[content/browser/worker_host] implements the browser side of web workers
+(dedicated workers and shared workers). It tracks the security principal of the
+worker in the renderer and uses it to broker access to mojo interfaces providing
+powerful web APIs. See: [Design doc].
+
+The renderer side implementations are in [content/renderer/worker].
+
+[content/browser/worker_host]: /content/browser/worker_host
+[content/renderer/worker]: /content/renderer/worker
 
 [Design doc]: https://docs.google.com/document/d/1Bg84lQqeJ8D2J-_wOOLlRVAtZNMstEUHmVhJqCxpjUk/edit?usp=sharing
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 8466118..2a4550f 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -12,6 +12,7 @@
 #include "content/browser/interface_provider_filtering.h"
 #include "content/browser/renderer_interface_binders.h"
 #include "content/browser/websockets/websocket_manager.h"
+#include "content/public/browser/browser_thread.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
@@ -26,7 +27,7 @@
 
 // A host for a single dedicated worker. Its lifetime is managed by the
 // DedicatedWorkerGlobalScope of the corresponding worker in the renderer via a
-// StrongBinding.
+// StrongBinding. This lives on the UI thread.
 class DedicatedWorkerHost : public service_manager::mojom::InterfaceProvider {
  public:
   DedicatedWorkerHost(int process_id,
@@ -35,12 +36,14 @@
       : process_id_(process_id),
         ancestor_render_frame_id_(ancestor_render_frame_id),
         origin_(origin) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     RegisterMojoInterfaces();
   }
 
   // service_manager::mojom::InterfaceProvider:
   void GetInterface(const std::string& interface_name,
                     mojo::ScopedMessagePipeHandle interface_pipe) override {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     RenderProcessHost* process = RenderProcessHost::FromID(process_id_);
     if (!process)
       return;
@@ -56,6 +59,7 @@
 
  private:
   void RegisterMojoInterfaces() {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     registry_.AddInterface(base::BindRepeating(
         &DedicatedWorkerHost::CreateWebSocket, base::Unretained(this)));
     registry_.AddInterface(base::BindRepeating(
@@ -65,6 +69,7 @@
   }
 
   void CreateWebUsbService(blink::mojom::WebUsbServiceRequest request) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     auto* host =
         RenderFrameHostImpl::FromID(process_id_, ancestor_render_frame_id_);
     GetContentClient()->browser()->CreateWebUsbService(host,
@@ -72,6 +77,7 @@
   }
 
   void CreateWebSocket(network::mojom::WebSocketRequest request) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     network::mojom::AuthenticationHandlerPtr auth_handler;
     auto* frame =
         RenderFrameHost::FromID(process_id_, ancestor_render_frame_id_);
@@ -93,6 +99,7 @@
 
   void CreateDedicatedWorker(
       blink::mojom::DedicatedWorkerFactoryRequest request) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     CreateDedicatedWorkerHostFactory(process_id_, ancestor_render_frame_id_,
                                      origin_, std::move(request));
   }
@@ -110,7 +117,7 @@
 };
 
 // A factory for creating DedicatedWorkerHosts. Its lifetime is managed by
-// the renderer over mojo via a StrongBinding.
+// the renderer over mojo via a StrongBinding. This lives on the UI thread.
 class DedicatedWorkerFactoryImpl : public blink::mojom::DedicatedWorkerFactory {
  public:
   DedicatedWorkerFactoryImpl(int process_id,
@@ -118,12 +125,15 @@
                              const url::Origin& parent_context_origin)
       : process_id_(process_id),
         ancestor_render_frame_id_(ancestor_render_frame_id),
-        parent_context_origin_(parent_context_origin) {}
+        parent_context_origin_(parent_context_origin) {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
+  }
 
   // blink::mojom::DedicatedWorkerFactory:
   void CreateDedicatedWorker(
       const url::Origin& origin,
       service_manager::mojom::InterfaceProviderRequest request) override {
+    DCHECK_CURRENTLY_ON(BrowserThread::UI);
     // TODO(crbug.com/729021): Once |parent_context_origin_| is no longer races
     // with the request for |DedicatedWorkerFactory|, enforce that the worker's
     // origin either matches the creating document's origin, or is unique.
@@ -149,6 +159,7 @@
     int ancestor_render_frame_id,
     const url::Origin& origin,
     blink::mojom::DedicatedWorkerFactoryRequest request) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   mojo::MakeStrongBinding(std::make_unique<DedicatedWorkerFactoryImpl>(
                               process_id, ancestor_render_frame_id, origin),
                           std::move(request));
diff --git a/content/browser/worker_host/dedicated_worker_host.h b/content/browser/worker_host/dedicated_worker_host.h
index b802678..e25d8bac 100644
--- a/content/browser/worker_host/dedicated_worker_host.h
+++ b/content/browser/worker_host/dedicated_worker_host.h
@@ -13,6 +13,7 @@
 
 namespace content {
 
+// Creates a host for a dedicated worker. This must be called on the UI thread.
 void CreateDedicatedWorkerHostFactory(
     int process_id,
     int parent_render_frame_id,
diff --git a/content/browser/worker_host/mock_shared_worker.cc b/content/browser/worker_host/mock_shared_worker.cc
index 8679ca43..da0ad9d 100644
--- a/content/browser/worker_host/mock_shared_worker.cc
+++ b/content/browser/worker_host/mock_shared_worker.cc
@@ -104,7 +104,7 @@
     blink::mojom::RendererPreferencesPtr renderer_preferences,
     blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
     blink::mojom::WorkerContentSettingsProxyPtr content_settings,
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
         service_worker_provider_info,
     int appcache_host_id,
     network::mojom::URLLoaderFactoryAssociatedPtrInfo
diff --git a/content/browser/worker_host/mock_shared_worker.h b/content/browser/worker_host/mock_shared_worker.h
index b5fc971..ea408bc 100644
--- a/content/browser/worker_host/mock_shared_worker.h
+++ b/content/browser/worker_host/mock_shared_worker.h
@@ -79,7 +79,7 @@
       blink::mojom::RendererPreferencesPtr renderer_preferences,
       blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
       blink::mojom::WorkerContentSettingsProxyPtr content_settings,
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
           service_worker_provider_info,
       int appcache_host_id,
       network::mojom::URLLoaderFactoryAssociatedPtrInfo
diff --git a/content/browser/worker_host/shared_worker_host.cc b/content/browser/worker_host/shared_worker_host.cc
index e63a9779f..380881b 100644
--- a/content/browser/worker_host/shared_worker_host.cc
+++ b/content/browser/worker_host/shared_worker_host.cc
@@ -144,7 +144,7 @@
 
 void SharedWorkerHost::Start(
     blink::mojom::SharedWorkerFactoryPtr factory,
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
         service_worker_provider_info,
     network::mojom::URLLoaderFactoryAssociatedPtrInfo
         main_script_loader_factory,
@@ -225,16 +225,9 @@
 
   // Set the default factory to the bundle for subresource loading to pass to
   // the renderer when NetworkService is on. When S13nServiceWorker is on, the
-  // default factory is already provided by SharedWorkerServiceImpl.
+  // default factory is already provided by
+  // WorkerScriptFetchInitiator::CreateFactoryBundle().
   if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    // If the caller has supplied a URLLoaderFactory for AppCache, use that.
-    if (subresource_loader_params &&
-        subresource_loader_params->appcache_loader_factory_info.is_valid()) {
-      subresource_loader_factories->appcache_factory_info() =
-          std::move(subresource_loader_params->appcache_loader_factory_info);
-    }
-
-    // Set-up the default network loader factory.
     network::mojom::URLLoaderFactoryPtrInfo default_factory_info;
     CreateNetworkFactory(mojo::MakeRequest(&default_factory_info));
     subresource_loader_factories->default_factory_info() =
diff --git a/content/browser/worker_host/shared_worker_host.h b/content/browser/worker_host/shared_worker_host.h
index 3b4a6b3f..fd74d1ae 100644
--- a/content/browser/worker_host/shared_worker_host.h
+++ b/content/browser/worker_host/shared_worker_host.h
@@ -92,7 +92,7 @@
   // service worker controller is sent via ServiceWorkerContainer#SetController.
   void Start(
       blink::mojom::SharedWorkerFactoryPtr factory,
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
           service_worker_provider_info,
       network::mojom::URLLoaderFactoryAssociatedPtrInfo
           main_script_loader_factory,
diff --git a/content/browser/worker_host/shared_worker_host_unittest.cc b/content/browser/worker_host/shared_worker_host_unittest.cc
index cf08710..153b34e 100644
--- a/content/browser/worker_host/shared_worker_host_unittest.cc
+++ b/content/browser/worker_host/shared_worker_host_unittest.cc
@@ -73,8 +73,7 @@
 
   void StartWorker(SharedWorkerHost* host,
                    blink::mojom::SharedWorkerFactoryPtr factory) {
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr provider_info =
-        nullptr;
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr provider_info = nullptr;
     network::mojom::URLLoaderFactoryAssociatedPtrInfo
         main_script_loader_factory;
     blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params;
@@ -85,8 +84,7 @@
     // Set up various mocks based on NetworkService/S13nServiceWorker
     // configuration. See the comment on SharedWorkerHost::Start() for details.
     if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-      provider_info =
-          blink::mojom::ServiceWorkerProviderInfoForSharedWorker::New();
+      provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
       ServiceWorkerProviderHost::PreCreateForSharedWorker(
           helper_->context()->AsWeakPtr(), mock_render_process_host_.GetID(),
           &provider_info);
@@ -104,8 +102,7 @@
       subresource_loader_params->appcache_loader_factory_info =
           loader_factory_ptr.PassInterface();
     } else if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
-      provider_info =
-          blink::mojom::ServiceWorkerProviderInfoForSharedWorker::New();
+      provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
       ServiceWorkerProviderHost::PreCreateForSharedWorker(
           helper_->context()->AsWeakPtr(), mock_render_process_host_.GetID(),
           &provider_info);
diff --git a/content/browser/worker_host/shared_worker_service_impl.cc b/content/browser/worker_host/shared_worker_service_impl.cc
index 8a06b22..cf071655 100644
--- a/content/browser/worker_host/shared_worker_service_impl.cc
+++ b/content/browser/worker_host/shared_worker_service_impl.cc
@@ -238,7 +238,7 @@
     int process_id,
     int frame_id,
     const blink::MessagePortChannel& message_port,
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
         service_worker_provider_info,
     network::mojom::URLLoaderFactoryAssociatedPtrInfo
         main_script_loader_factory,
@@ -274,7 +274,7 @@
     int process_id,
     int frame_id,
     const blink::MessagePortChannel& message_port,
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
         service_worker_provider_info,
     network::mojom::URLLoaderFactoryAssociatedPtrInfo
         main_script_loader_factory,
diff --git a/content/browser/worker_host/shared_worker_service_impl.h b/content/browser/worker_host/shared_worker_service_impl.h
index 6c6344bd..5024919 100644
--- a/content/browser/worker_host/shared_worker_service_impl.h
+++ b/content/browser/worker_host/shared_worker_service_impl.h
@@ -87,7 +87,7 @@
       int process_id,
       int frame_id,
       const blink::MessagePortChannel& message_port,
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
           service_worker_provider_info,
       network::mojom::URLLoaderFactoryAssociatedPtrInfo
           main_script_loader_factory,
@@ -103,7 +103,7 @@
       int process_id,
       int frame_id,
       const blink::MessagePortChannel& message_port,
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
           service_worker_provider_info,
       network::mojom::URLLoaderFactoryAssociatedPtrInfo
           main_script_loader_factory,
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.cc b/content/browser/worker_host/worker_script_fetch_initiator.cc
index 218cd19..01042296 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.cc
+++ b/content/browser/worker_host/worker_script_fetch_initiator.cc
@@ -58,9 +58,9 @@
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
   DCHECK(storage_partition);
-
-  // TODO(nhiroki): Support DedicatedWorker (https://crbug.com/906991).
-  DCHECK_EQ(RESOURCE_TYPE_SHARED_WORKER, resource_type);
+  DCHECK(resource_type == RESOURCE_TYPE_WORKER ||
+         resource_type == RESOURCE_TYPE_SHARED_WORKER)
+      << resource_type;
 
   bool constructor_uses_file_url =
       request_initiator.scheme() == url::kFileScheme;
@@ -173,6 +173,7 @@
 void WorkerScriptFetchInitiator::AddAdditionalRequestHeaders(
     network::ResourceRequest* resource_request,
     BrowserContext* browser_context) {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
 
   // TODO(nhiroki): Return early when the request is neither HTTP nor HTTPS
@@ -237,8 +238,7 @@
   DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
 
   // Set up for service worker.
-  auto provider_info =
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorker::New();
+  auto provider_info = blink::mojom::ServiceWorkerProviderInfoForWorker::New();
   base::WeakPtr<ServiceWorkerProviderHost> host =
       context->PreCreateHostForSharedWorker(process_id, &provider_info);
 
@@ -320,7 +320,7 @@
 
 void WorkerScriptFetchInitiator::DidCreateScriptLoaderOnIO(
     CompletionCallback callback,
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
         service_worker_provider_info,
     network::mojom::URLLoaderFactoryAssociatedPtrInfo
         main_script_loader_factory,
@@ -330,6 +330,16 @@
     base::Optional<SubresourceLoaderParams> subresource_loader_params,
     bool success) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
+
+  // NetworkService (PlzWorker):
+  // If a URLLoaderFactory for AppCache is supplied, use that.
+  if (subresource_loader_params &&
+      subresource_loader_params->appcache_loader_factory_info) {
+    DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
+    subresource_loader_factories->appcache_factory_info() =
+        std::move(subresource_loader_params->appcache_loader_factory_info);
+  }
+
   base::PostTaskWithTraits(
       FROM_HERE, {BrowserThread::UI},
       base::BindOnce(std::move(callback),
diff --git a/content/browser/worker_host/worker_script_fetch_initiator.h b/content/browser/worker_host/worker_script_fetch_initiator.h
index c1cbedb..8bf63d9 100644
--- a/content/browser/worker_host/worker_script_fetch_initiator.h
+++ b/content/browser/worker_host/worker_script_fetch_initiator.h
@@ -42,7 +42,7 @@
 class WorkerScriptFetchInitiator {
  public:
   using CompletionCallback = base::OnceCallback<void(
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr,
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr,
       network::mojom::URLLoaderFactoryAssociatedPtrInfo,
       std::unique_ptr<blink::URLLoaderFactoryBundleInfo>,
       blink::mojom::WorkerMainScriptLoadParamsPtr,
@@ -63,13 +63,18 @@
       CompletionCallback callback);
 
  private:
+  // Creates a loader factory bundle. Must be called on the UI thread.
   static std::unique_ptr<blink::URLLoaderFactoryBundleInfo> CreateFactoryBundle(
       int process_id,
       StoragePartitionImpl* storage_partition,
       bool file_support);
+
+  // Adds additional request headers to |resource_request|. Must be called on
+  // the UI thread.
   static void AddAdditionalRequestHeaders(
       network::ResourceRequest* resource_request,
       BrowserContext* browser_context);
+
   static void CreateScriptLoaderOnIO(
       int process_id,
       std::unique_ptr<network::ResourceRequest> resource_request,
@@ -85,7 +90,7 @@
       CompletionCallback callback);
   static void DidCreateScriptLoaderOnIO(
       CompletionCallback callback,
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
           service_worker_provider_info,
       network::mojom::URLLoaderFactoryAssociatedPtrInfo
           main_script_loader_factory,
diff --git a/content/browser/worker_host/worker_script_loader_factory.cc b/content/browser/worker_host/worker_script_loader_factory.cc
index 729be651..c64846a 100644
--- a/content/browser/worker_host/worker_script_loader_factory.cc
+++ b/content/browser/worker_host/worker_script_loader_factory.cc
@@ -33,6 +33,10 @@
       loader_factory_(std::move(loader_factory)) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(blink::ServiceWorkerUtils::IsServicificationEnabled());
+  // In the current implementation, dedicated workers use
+  // ServiceWorkerProviderHost w/ kForSharedWorker.
+  // TODO(nhiroki): Rename it to kForWorker for both dedicated workers and
+  // shared workers, or add kForDedicatedWorker (https://crbug.com/906991).
   DCHECK_EQ(service_worker_provider_host_->provider_type(),
             blink::mojom::ServiceWorkerProviderType::kForSharedWorker);
 }
@@ -54,10 +58,10 @@
   // When NetworkService is not enabled, this function is called from the
   // renderer process, so use ReportBadMessage() instead of DCHECK().
   if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
-    // Handle only the main script (RESOURCE_TYPE_SHARED_WORKER). Import scripts
+    // Handle only the main script. Import scripts (RESOURCE_TYPE_SCRIPT)
     // should go to the network loader or controller.
-    // TODO(nhiroki): Support dedicated workers (https://crbug.com/906991).
-    if (resource_request.resource_type != RESOURCE_TYPE_SHARED_WORKER) {
+    if (resource_request.resource_type != RESOURCE_TYPE_WORKER &&
+        resource_request.resource_type != RESOURCE_TYPE_SHARED_WORKER) {
       mojo::ReportBadMessage(
           "WorkerScriptLoaderFactory should only get requests for worker "
           "scripts");
@@ -69,7 +73,9 @@
       return;
     }
   }
-  DCHECK_EQ(RESOURCE_TYPE_SHARED_WORKER, resource_request.resource_type);
+  DCHECK(resource_request.resource_type == RESOURCE_TYPE_WORKER ||
+         resource_request.resource_type == RESOURCE_TYPE_SHARED_WORKER)
+      << resource_request.resource_type;
   DCHECK(!script_loader_);
 
   // Create a WorkerScriptLoader to load the script.
diff --git a/content/browser/worker_host/worker_script_loader_factory.h b/content/browser/worker_host/worker_script_loader_factory.h
index b15edbd..6500e3de 100644
--- a/content/browser/worker_host/worker_script_loader_factory.h
+++ b/content/browser/worker_host/worker_script_loader_factory.h
@@ -21,16 +21,14 @@
 class WorkerScriptLoader;
 
 // S13nServiceWorker:
-// Created per one running web worker for loading its script.
+// WorkerScriptLoaderFactory creates a WorkerScriptLoader to load the main
+// script for a web worker (dedicated worker or shared worker), which follows
+// redirects and sets the controller service worker on the web worker if needed.
+// It's an error to call CreateLoaderAndStart() more than a total of one time
+// across this object or any of its clones.
 //
-// Shared worker script loads require special logic because they are similiar to
-// navigations from the point of view of web platform features like service
-// worker.
-//
-// This creates a WorkerScriptLoader to load the script, which follows redirects
-// and sets the controller service worker on the web worker if needed. It's an
-// error to call CreateLoaderAndStart() more than a total of one time across
-// this object or any of its clones.
+// This is created per one web worker. All functions of this class must be
+// called on the IO thread.
 class WorkerScriptLoaderFactory : public network::mojom::URLLoaderFactory {
  public:
   // |loader_factory| is used to load the script if the load is not intercepted
diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_init_impl_win.cc b/content/child/dwrite_font_proxy/dwrite_font_proxy_init_impl_win.cc
index b5cd80b..4996a9e 100644
--- a/content/child/dwrite_font_proxy/dwrite_font_proxy_init_impl_win.cc
+++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_init_impl_win.cc
@@ -15,7 +15,7 @@
 #include "content/child/dwrite_font_proxy/font_fallback_win.h"
 #include "content/child/font_warmup_win.h"
 #include "content/public/common/service_names.mojom.h"
-#include "skia/ext/fontmgr_default_win.h"
+#include "skia/ext/fontmgr_default.h"
 #include "third_party/blink/public/web/win/web_font_rendering.h"
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/ports/SkTypeface_win.h"
@@ -78,7 +78,7 @@
       factory.Get(), g_font_collection, g_font_fallback);
   blink::WebFontRendering::SetSkiaFontManager(skia_font_manager);
 
-  SetDefaultSkiaFactory(std::move(skia_font_manager));
+  skia::OverrideDefaultSkFontMgr(std::move(skia_font_manager));
 
   // When IDWriteFontFallback is not available (prior to Win8.1) Skia will
   // still attempt to use DirectWrite to determine fallback fonts (in
diff --git a/content/child/font_warmup_win.cc b/content/child/font_warmup_win.cc
index 93b698c3..87de0591 100644
--- a/content/child/font_warmup_win.cc
+++ b/content/child/font_warmup_win.cc
@@ -25,7 +25,6 @@
 #include "base/win/windows_version.h"
 #include "build/build_config.h"
 #include "ppapi/buildflags/buildflags.h"
-#include "skia/ext/fontmgr_default_win.h"
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/ports/SkTypeface_win.h"
diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc
index 6b61bcb2..148d2d6 100644
--- a/content/child/runtime_features.cc
+++ b/content/child/runtime_features.cc
@@ -322,6 +322,10 @@
   WebRuntimeFeatures::EnablePaymentRequest(
       base::FeatureList::IsEnabled(features::kWebPayments));
 
+  WebRuntimeFeatures::EnablePaymentRequestHasEnrolledInstrument(
+      base::FeatureList::IsEnabled(
+          features::kPaymentRequestHasEnrolledInstrument));
+
   if (base::FeatureList::IsEnabled(features::kServiceWorkerPaymentApps))
     WebRuntimeFeatures::EnablePaymentApp(true);
 
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 0eeee56b..d64927e 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -500,7 +500,6 @@
     "render_widget_window_tree_client_factory.mojom",
     "renderer.mojom",
     "renderer_host.mojom",
-    "service_worker/embedded_worker.mojom",
     "widget.mojom",
   ]
 
diff --git a/content/common/ax_content_node_data.cc b/content/common/ax_content_node_data.cc
index 7dec9c68..01166e0 100644
--- a/content/common/ax_content_node_data.cc
+++ b/content/common/ax_content_node_data.cc
@@ -8,7 +8,7 @@
 
 #include "base/strings/string_number_conversions.h"
 
-using base::IntToString;
+using base::NumberToString;
 
 namespace content {
 
@@ -76,7 +76,7 @@
   std::string result = AXNodeData::ToString();
 
   for (auto iter : content_int_attributes) {
-    std::string value = IntToString(iter.second);
+    std::string value = NumberToString(iter.second);
     switch (iter.first) {
       case AX_CONTENT_ATTR_CHILD_ROUTING_ID:
         result += " child_routing_id=" + value;
@@ -106,9 +106,9 @@
   std::string result = AXTreeData::ToString();
 
   if (routing_id != -1)
-    result += " routing_id=" + IntToString(routing_id);
+    result += " routing_id=" + NumberToString(routing_id);
   if (parent_routing_id != -1)
-    result += " parent_routing_id=" + IntToString(parent_routing_id);
+    result += " parent_routing_id=" + NumberToString(parent_routing_id);
 
   return result;
 }
diff --git a/content/common/content_switches_internal.cc b/content/common/content_switches_internal.cc
index 5ff0c8b..df9ba8b 100644
--- a/content/common/content_switches_internal.cc
+++ b/content/common/content_switches_internal.cc
@@ -68,7 +68,7 @@
   return !command_line.HasSwitch(switches::kDisablePinch);
 }
 
-V8CacheOptions GetV8CacheOptions() {
+blink::mojom::V8CacheOptions GetV8CacheOptions() {
   const base::CommandLine& command_line =
       *base::CommandLine::ForCurrentProcess();
   std::string v8_cache_options =
@@ -76,11 +76,11 @@
   if (v8_cache_options.empty())
     v8_cache_options = base::FieldTrialList::FindFullName("V8CacheOptions");
   if (v8_cache_options == "none") {
-    return V8_CACHE_OPTIONS_NONE;
+    return blink::mojom::V8CacheOptions::kNone;
   } else if (v8_cache_options == "code") {
-    return V8_CACHE_OPTIONS_CODE;
+    return blink::mojom::V8CacheOptions::kCode;
   } else {
-    return V8_CACHE_OPTIONS_DEFAULT;
+    return blink::mojom::V8CacheOptions::kDefault;
   }
 }
 
@@ -95,7 +95,7 @@
   title += label;  // makes attaching to process easier
   std::string message = label;
   message += " starting with pid: ";
-  message += base::IntToString(base::GetCurrentProcId());
+  message += base::NumberToString(base::GetCurrentProcId());
   ::MessageBox(NULL, base::UTF8ToWide(message).c_str(),
                base::UTF8ToWide(title).c_str(), MB_OK | MB_SETFOREGROUND);
 #elif defined(OS_POSIX)
diff --git a/content/common/content_switches_internal.h b/content/common/content_switches_internal.h
index 5e67fbc..886bdf0 100644
--- a/content/common/content_switches_internal.h
+++ b/content/common/content_switches_internal.h
@@ -17,7 +17,7 @@
 
 bool IsPinchToZoomEnabled();
 
-V8CacheOptions GetV8CacheOptions();
+blink::mojom::V8CacheOptions GetV8CacheOptions();
 
 void WaitForDebugger(const std::string& label);
 
diff --git a/content/common/renderer.mojom b/content/common/renderer.mojom
index 45cc39c..7a53a7a 100644
--- a/content/common/renderer.mojom
+++ b/content/common/renderer.mojom
@@ -6,13 +6,13 @@
 
 import "content/common/document_scoped_interface_bundle.mojom";
 import "content/common/native_types.mojom";
-import "content/common/service_worker/embedded_worker.mojom";
 import "ipc/constants.mojom";
 import "mojo/public/mojom/base/time.mojom";
 import "mojo/public/mojom/base/unguessable_token.mojom";
 import "services/network/public/mojom/network_types.mojom";
 import "services/service_manager/public/mojom/service.mojom";
 import "third_party/blink/public/mojom/renderer_preferences.mojom";
+import "third_party/blink/public/mojom/service_worker/embedded_worker.mojom";
 import "ui/gfx/geometry/mojo/geometry.mojom";
 
 struct CreateViewParams {
@@ -194,7 +194,7 @@
   // TODO(shimazu): Send all params for starting service worker to reduce the
   // number of IPCs.
   SetUpEmbeddedWorkerChannelForServiceWorker(
-      EmbeddedWorkerInstanceClient& client_request);
+      blink.mojom.EmbeddedWorkerInstanceClient& client_request);
 
   // Tells the renderer that the network type has changed so that
   // navigator.onLine and navigator.connection can be updated.
diff --git a/content/common/sandbox_init_win.cc b/content/common/sandbox_init_win.cc
index b5f1619..3abd9134 100644
--- a/content/common/sandbox_init_win.cc
+++ b/content/common/sandbox_init_win.cc
@@ -10,7 +10,6 @@
 #include "base/logging.h"
 #include "base/trace_event/trace_event.h"
 #include "base/win/scoped_process_information.h"
-#include "content/common/content_switches_internal.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/sandbox_init.h"
 #include "content/public/common/sandboxed_process_launcher_delegate.h"
diff --git a/content/common/service_worker/embedded_worker.mojom b/content/common/service_worker/embedded_worker.mojom
deleted file mode 100644
index 7a60504..0000000
--- a/content/common/service_worker/embedded_worker.mojom
+++ /dev/null
@@ -1,182 +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.
-
-module content.mojom;
-
-import "content/common/native_types.mojom";
-import "mojo/public/mojom/base/string16.mojom";
-import "mojo/public/mojom/base/time.mojom";
-import "mojo/public/mojom/base/unguessable_token.mojom";
-import "services/service_manager/public/mojom/interface_provider.mojom";
-import "third_party/blink/public/mojom/devtools/console_message.mojom";
-import "third_party/blink/public/mojom/devtools/devtools_agent.mojom";
-import "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom";
-import "third_party/blink/public/mojom/renderer_preference_watcher.mojom";
-import "third_party/blink/public/mojom/renderer_preferences.mojom";
-import "third_party/blink/public/mojom/script/script_type.mojom";
-import "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom";
-import "third_party/blink/public/mojom/service_worker/service_worker.mojom";
-import "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom";
-import "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom";
-import "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom";
-import "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom";
-import "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom";
-import "third_party/blink/public/platform/web_feature.mojom";
-import "url/mojom/url.mojom";
-
-// Parameters to launch a service worker. This is passed from the browser to the
-// renderer at mojom::EmbeddedWorkerInstanceClient::StartWorker().
-struct EmbeddedWorkerStartParams {
-  // DEPRECATED: This is only used in unit tests.
-  // TODO(https://crbug.com/927651): Remove this.
-  int32 embedded_worker_id;
-
-  // The id of the service worker being started. This remains fixed even if the
-  // worker is stopped and restarted, or even if the browser restarts.
-  int64 service_worker_version_id;
-  // This service worker's registration's scope:
-  // https://w3c.github.io/ServiceWorker/#service-worker-registration-scope
-  url.mojom.Url scope;
-  // This service worker's script url:
-  // https://w3c.github.io/ServiceWorker/#dom-serviceworker-scripturl
-  url.mojom.Url script_url;
-  // This service worker's script type:
-  // https://w3c.github.io/ServiceWorker/#dfn-type
-  blink.mojom.ScriptType script_type;
-  // The string used for "user-agent" HTTP header.
-  string user_agent;
-  // The id to talk with the DevTools agent for the worker.
-  int32 worker_devtools_agent_route_id;
-  // Unique token identifying this worker for DevTools.
-  mojo_base.mojom.UnguessableToken devtools_worker_token;
-  // When true, worker script evaluation is blocked until
-  // EmbeddedWorkerInstanceClient::ResumeAfterDownload() is called.
-  bool pause_after_download;
-  // True if starting the worker should wait until DevTools gets ready.
-  bool wait_for_debugger;
-  // True if this service worker has been installed.
-  bool is_installed;
-  // Determines how eagerly V8 creates the code cache.
-  V8CacheOptions v8_cache_options;
-  // Used to set up fetch requests.
-  blink.mojom.RendererPreferences renderer_preferences;
-
-  // Used to talk to the service worker from the browser process.
-  blink.mojom.ServiceWorker& service_worker_request;
-  // S13nServiceWorker: cloned and passed to each controllee to directly
-  // dispatch events from the controllees.
-  blink.mojom.ControllerServiceWorker& controller_request;
-  // Information to transfer installed scripts from the browser to the renderer.
-  blink.mojom.ServiceWorkerInstalledScriptsInfo? installed_scripts_info;
-  // Interface for the renderer to send the status updates to the browser.
-  associated EmbeddedWorkerInstanceHost instance_host;
-  // Information for creating ServiceWorkerProviderContext on the renderer.
-  blink.mojom.ServiceWorkerProviderInfoForStartWorker provider_info;
-  // Interface for the renderer to query the content settings in the browser.
-  blink.mojom.WorkerContentSettingsProxy content_settings_proxy;
-  // Interface for keeping track of the renderer preferences.
-  blink.mojom.RendererPreferenceWatcher& preference_watcher_request;
-  // S13nServiceWorker: Used to load subresources in the service worker.
-  // This allows the service worker to load chrome-extension:// URLs which
-  // the renderer's default loader factory can't load.
-  blink.mojom.URLLoaderFactoryBundle? subresource_loader_factories;
-};
-
-// Holds timing information about the start worker sequence for UMA.
-//
-// Keep this in sync with the validation check in
-// EmbeddedWorkerInstance::OnStarted.
-// TODO(falken): Make a typemap just for the validation check?
-struct EmbeddedWorkerStartTiming {
-  // When the start worker message was received by the renderer.
-  mojo_base.mojom.TimeTicks start_worker_received_time;
-  // When JavaScript evaluation on the worker thread started.
-  mojo_base.mojom.TimeTicks script_evaluation_start_time;
-  // When JavaScript evaluation on the worker thread finished.
-  mojo_base.mojom.TimeTicks script_evaluation_end_time;
-};
-
-// EmbeddedWorkerInstanceClient is the renderer-side ("Client") of
-// EmbeddedWorkerInstanceHost. It allows control of a renderer-side
-// embedded worker. The browser uses this interface to start, stop, and
-// issue commands to the worker.
-//
-// This interface is the master interface of a dedicated message pipe. It has
-// some interfaces associated with it, like EmbeddedWorkerInstanceHost.
-interface EmbeddedWorkerInstanceClient {
-  // Called back as various functions in EmbeddedWorkerInstanceHost, such
-  // as OnThreadStarted(), OnStarted().
-  StartWorker(EmbeddedWorkerStartParams params);
-  // The response is sent back via EmbeddedWorkerInstanceHost.OnStopped().
-  StopWorker();
-  ResumeAfterDownload();
-  AddMessageToConsole(blink.mojom.ConsoleMessageLevel level, string message);
-  // Returns a DevToolsAgent interface for this embedded worker, used for
-  // remote debugging. See DevToolsAgent for details.
-  BindDevToolsAgent(associated blink.mojom.DevToolsAgentHost agent_host,
-                    associated blink.mojom.DevToolsAgent& agent);
-};
-
-// EmbeddedWorkerInstanceHost is the browser-side ("Host") of
-// EmbeddedWorkerInstanceClient. It allows control of a browser-side embedded
-// worker instance. The renderer uses this interface to report embedded worker
-// state back to the browser, or request termination of the worker. This
-// interface is associated with the master interface
-// EmbeddedWorkerInstanceClient, so it lives on the same message pipe as
-// EmbeddedWorkerInstanceClient.
-interface EmbeddedWorkerInstanceHost {
-  // S13nServiceWorker:
-  // Called when the worker requests to be terminated. The worker will request
-  // to be terminated when it realizes it has been idle for some time. The
-  // browser doesn't terminate the worker when there are inflight events or
-  // DevTools is attached, and in that case the callback will be called with
-  // false. Note that the browser can terminate the worker at any time even if
-  // RequestTermination() is not called. For example, if the worker thread is
-  // continuously busy and the browser's periodic ping message has been missed,
-  // the browser will terminate the service worker.
-  RequestTermination() => (bool will_be_terminated);
-
-  // Tells the browser process that this service worker used |feature|, for
-  // UseCounter purposes. The browser process propagates the feature usage bit
-  // to all clients controlled by the service worker. See
-  // https://crbug.com/376039 for background.
-  // Note: Because CountFeature() is possible to be called on the main thread
-  // during service worker startup and is also called on the worker thread after
-  // that, we put it here rather than interface ServiceWorkerHost, so that we
-  // can still keep interface ServiceWorkerHost being used solely on the worker
-  // thread in the renderer process.
-  CountFeature(blink.mojom.WebFeature feature);
-
-  // The following are called during startup:
-  //
-  // Indicates that the worker is ready for inspection. This message is needed
-  // because DevTools requires the shadow page to have been created before
-  // inspecting the worker.
-  OnReadyForInspection();
-  // Indicates that the worker has finished loading the main script.
-  //
-  // This is only called for new (non-installed) workers. It's used so the
-  // browser process knows it can resume the paused worker via
-  // ResumeAfterDownloaded().
-  OnScriptLoaded();
-  // Indicates that initial JavaScript evaluation is starting. This is useful
-  // for the browser process to start enforcing timeouts on script execution.
-  OnScriptEvaluationStart();
-  // Indicates that the worker has started. |thread_id| is the platform
-  // thread id the worker runs on.
-  OnStarted(blink.mojom.ServiceWorkerStartStatus status,
-            int32 thread_id,
-            EmbeddedWorkerStartTiming start_timing);
-
-  // Reports that an uncaught exception occurred in the worker.
-  OnReportException(mojo_base.mojom.String16 error_message, int32 line_number,
-                    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,
-                         mojo_base.mojom.String16 message, int32 line_number,
-                         url.mojom.Url source_url);
-  // Indicates that the worker has stopped.
-  OnStopped();
-};
diff --git a/content/common/service_worker/service_worker_types.cc b/content/common/service_worker/service_worker_types.cc
index ed12484..f85c1aa4 100644
--- a/content/common/service_worker/service_worker_types.cc
+++ b/content/common/service_worker/service_worker_types.cc
@@ -31,5 +31,6 @@
 const char kServiceWorkerRedirectError[] =
     "The script resource is behind a redirect, which is disallowed.";
 const char kServiceWorkerAllowed[] = "Service-Worker-Allowed";
-
+const char kServiceWorkerCopyScriptError[] =
+    "An unknown error occurred when copying the script.";
 }  // namespace content
diff --git a/content/common/service_worker/service_worker_types.h b/content/common/service_worker/service_worker_types.h
index 9628c64..04f5ec5 100644
--- a/content/common/service_worker/service_worker_types.h
+++ b/content/common/service_worker/service_worker_types.h
@@ -50,6 +50,7 @@
 extern const char kServiceWorkerNoMIMEError[];
 extern const char kServiceWorkerRedirectError[];
 extern const char kServiceWorkerAllowed[];
+extern const char kServiceWorkerCopyScriptError[];
 
 // Constants for invalid identifiers.
 static const int kInvalidEmbeddedWorkerThreadId = -1;
diff --git a/content/common/unique_name_helper.cc b/content/common/unique_name_helper.cc
index 26d8a5e2..f372bd0a 100644
--- a/content/common/unique_name_helper.cc
+++ b/content/common/unique_name_helper.cc
@@ -89,7 +89,7 @@
   new_name += base::JoinString(ancestor_names, "/");
 
   new_name += "/<!--frame";
-  new_name += base::IntToString(frame->GetSiblingCount());
+  new_name += base::NumberToString(frame->GetSiblingCount());
   new_name += "-->-->";
 
   // NOTE: This name might not be unique - see http://crbug.com/588800.
@@ -102,7 +102,7 @@
       frame->GetFramePosition(FrameAdapter::BeginPoint::kParentFrame);
   for (int position : positions) {
     position_string += '-';
-    position_string += base::IntToString(position);
+    position_string += base::NumberToString(position);
   }
 
   // NOTE: The generated string is not guaranteed to be unique, but should
@@ -134,7 +134,7 @@
   candidate += '/';
   while (true) {
     size_t current_length = candidate.size();
-    candidate += base::IntToString(number_of_retries++);
+    candidate += base::NumberToString(number_of_retries++);
     candidate += "-->";
     if (frame->IsCandidateUnique(candidate))
       break;
diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
index 5a82ff02..1bd0c48 100644
--- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java
@@ -187,6 +187,9 @@
 
     private boolean mInitialized;
 
+    // Remember the stack for clearing native the native stack for debugging use after destroy.
+    private Throwable mNativeDestroyThrowable;
+
     private static class WebContentsInternalsImpl implements WebContentsInternals {
         public UserDataHost userDataHost;
         public ViewAndroidDelegate viewAndroidDelegate;
@@ -194,6 +197,7 @@
 
     private WebContentsImpl(
             long nativeWebContentsAndroid, NavigationController navigationController) {
+        assert nativeWebContentsAndroid != 0;
         mNativeWebContentsAndroid = nativeWebContentsAndroid;
         mNavigationController = navigationController;
     }
@@ -246,6 +250,7 @@
 
     @CalledByNative
     private void clearNativePtr() {
+        mNativeDestroyThrowable = new RuntimeException("clearNativePtr");
         mNativeWebContentsAndroid = 0;
         mNavigationController = null;
         if (mObserverProxy != null) {
@@ -936,7 +941,8 @@
 
     private void checkNotDestroyed() {
         if (mNativeWebContentsAndroid != 0) return;
-        throw new IllegalStateException("Native WebContents already destroyed");
+        throw new IllegalStateException(
+                "Native WebContents already destroyed", mNativeDestroyThrowable);
     }
 
     // This is static to avoid exposing a public destroy method on the native side of this class.
diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn
index e0d85c4..09f7ea90 100644
--- a/content/public/browser/BUILD.gn
+++ b/content/public/browser/BUILD.gn
@@ -205,6 +205,7 @@
     "notification_service.h",
     "notification_source.h",
     "notification_types.h",
+    "origin_policy_commands.h",
     "origin_policy_error_reason.h",
     "overlay_window.h",
     "overscroll_configuration.h",
diff --git a/content/public/browser/content_browser_client.cc b/content/public/browser/content_browser_client.cc
index 61e4af6..b411105 100644
--- a/content/public/browser/content_browser_client.cc
+++ b/content/public/browser/content_browser_client.cc
@@ -880,8 +880,7 @@
 
 base::Optional<std::string> ContentBrowserClient::GetOriginPolicyErrorPage(
     OriginPolicyErrorReason error_reason,
-    const url::Origin& origin,
-    const GURL& url) {
+    content::NavigationHandle* handle) {
   return base::nullopt;
 }
 
diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h
index 80c4bae..a12bf4c 100644
--- a/content/public/browser/content_browser_client.h
+++ b/content/public/browser/content_browser_client.h
@@ -1395,8 +1395,7 @@
   // errors.
   virtual base::Optional<std::string> GetOriginPolicyErrorPage(
       OriginPolicyErrorReason error_reason,
-      const url::Origin& origin,
-      const GURL& url);
+      content::NavigationHandle* navigation_handle);
 
   // Returns true if it is OK to ignore errors for certificates specified by the
   // --ignore-certificate-errors-spki-list command line flag. The embedder may
diff --git a/content/public/browser/desktop_media_id.cc b/content/public/browser/desktop_media_id.cc
index 34f784fc..e866b15 100644
--- a/content/public/browser/desktop_media_id.cc
+++ b/content/public/browser/desktop_media_id.cc
@@ -195,11 +195,11 @@
 
   // Screen and Window types.
   prefix.append(":");
-  prefix.append(base::Int64ToString(id));
+  prefix.append(base::NumberToString(id));
 
 #if defined(USE_AURA)
   prefix.append(":");
-  prefix.append(base::Int64ToString(aura_id));
+  prefix.append(base::NumberToString(aura_id));
 #endif  // defined(USE_AURA)
 
   return prefix;
diff --git a/content/public/browser/origin_policy_commands.h b/content/public/browser/origin_policy_commands.h
new file mode 100644
index 0000000..358f1f7
--- /dev/null
+++ b/content/public/browser/origin_policy_commands.h
@@ -0,0 +1,22 @@
+// 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 CONTENT_PUBLIC_BROWSER_ORIGIN_POLICY_COMMANDS_H_
+#define CONTENT_PUBLIC_BROWSER_ORIGIN_POLICY_COMMANDS_H_
+
+#include "content/common/content_export.h"
+
+class GURL;
+
+namespace content {
+
+// Instruct the Origin Policy throttle to disregard errors for the given URL.
+//
+// Intended use: This should be called by the browser when the user selects
+// "proceed" on the security interstitial page for the given URL.
+CONTENT_EXPORT void OriginPolicyAddExceptionFor(const GURL& url);
+
+}  // namespace content
+
+#endif  // CONTENT_PUBLIC_BROWSER_ORIGIN_POLICY_COMMANDS_H_
diff --git a/content/public/browser/service_worker_context.h b/content/public/browser/service_worker_context.h
index c2044686..ee597aa6 100644
--- a/content/public/browser/service_worker_context.h
+++ b/content/public/browser/service_worker_context.h
@@ -148,19 +148,14 @@
   virtual void PerformStorageCleanup(base::OnceClosure callback) = 0;
 
   // Returns ServiceWorkerCapability describing existence and properties of a
-  // Service Worker registration matching |url|. Found service worker
-  // registration must also encompass the |other_url|, otherwise it will be
-  // considered non existent by this method. Note that the longest matching
-  // registration for |url| is described, which is not necessarily the longest
-  // matching registration for |other_url|. In case the service worker is being
-  // installed as of calling this method, it will wait for the installation to
-  // finish before coming back with the result.
+  // Service Worker registration matching |url|. In case the service
+  // worker is being installed as of calling this method, it will wait for the
+  // installation to finish before coming back with the result.
   //
   // This function can be called from any thread, but the callback will always
   // be called on the UI thread.
   virtual void CheckHasServiceWorker(
       const GURL& url,
-      const GURL& other_url,
       CheckHasServiceWorkerCallback callback) = 0;
 
   // Stops all running service workers and unregisters all service worker
diff --git a/content/public/browser/web_contents_media_capture_id.cc b/content/public/browser/web_contents_media_capture_id.cc
index 2835818b..bfaeb2a 100644
--- a/content/public/browser/web_contents_media_capture_id.cc
+++ b/content/public/browser/web_contents_media_capture_id.cc
@@ -104,9 +104,9 @@
 
 std::string WebContentsMediaCaptureId::ToString() const {
   std::string s = kWebContentsCaptureScheme;
-  s.append(base::Int64ToString(render_process_id));
+  s.append(base::NumberToString(render_process_id));
   s.append(":");
-  s.append(base::Int64ToString(main_render_frame_id));
+  s.append(base::NumberToString(main_render_frame_id));
 
   char connector = kOptionStart;
   if (enable_auto_throttling) {
diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h
index 075425da..f2625e0 100644
--- a/content/public/common/common_param_traits_macros.h
+++ b/content/public/common/common_param_traits_macros.h
@@ -63,8 +63,8 @@
                           content::EDITING_BEHAVIOR_LAST)
 IPC_ENUM_TRAITS_MAX_VALUE(WindowOpenDisposition,
                           WindowOpenDisposition::MAX_VALUE)
-IPC_ENUM_TRAITS_MAX_VALUE(content::V8CacheOptions,
-                          content::V8_CACHE_OPTIONS_LAST)
+IPC_ENUM_TRAITS_MAX_VALUE(blink::mojom::V8CacheOptions,
+                          blink::mojom::V8CacheOptions::kMaxValue)
 IPC_ENUM_TRAITS_MIN_MAX_VALUE(ui::PointerType,
                               ui::POINTER_TYPE_FIRST,
                               ui::POINTER_TYPE_LAST)
diff --git a/content/public/common/content_features.cc b/content/public/common/content_features.cc
index 452842b58..bbe5a56 100644
--- a/content/public/common/content_features.cc
+++ b/content/public/common/content_features.cc
@@ -68,7 +68,7 @@
 
 // Enable unified garbage collection in Blink.
 const base::Feature kBlinkHeapUnifiedGarbageCollection{
-    "BlinkHeapUnifiedGarbageCollection", base::FEATURE_DISABLED_BY_DEFAULT};
+    "BlinkHeapUnifiedGarbageCollection", base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable bloated renderer detection.
 const base::Feature kBloatedRendererDetection{
@@ -258,13 +258,6 @@
 #endif
 };
 
-// Enables the memory coordinator.
-// WARNING:
-// The memory coordinator is not ready for use and enabling this may cause
-// unexpected memory regression at this point. Please do not enable this.
-const base::Feature kMemoryCoordinator{"MemoryCoordinator",
-                                       base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Instead of BrowserPlugin or GuestViews, MimeHandlerView will use a cross
 // process frame to render its handler.
 const base::Feature kMimeHandlerViewInCrossProcessFrame{
@@ -325,6 +318,10 @@
 const base::Feature kPassiveEventListenersDueToFling{
     "PassiveEventListenersDueToFling", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Whether PaymentRequest exposes hasEnrolledInstrument API.
+const base::Feature kPaymentRequestHasEnrolledInstrument = {
+    "PaymentRequestHasEnrolledInstrument", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Whether PDF files should be rendered in diffent processes based on origin.
 const base::Feature kPdfIsolation = {"PdfIsolation",
                                      base::FEATURE_DISABLED_BY_DEFAULT};
@@ -344,6 +341,12 @@
 #endif
 };
 
+// Whether cross-site frames should get their own SiteInstance even when
+// strict site isolation is disabled. These SiteInstances will still be
+// grouped into a shared default process based on BrowsingInstance.
+const base::Feature kProcessSharingWithStrictSiteInstances{
+    "ProcessSharingWithStrictSiteInstances", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enables Purge+Throttle on platforms except Android and MacOS.
 // (Android) Purge+Throttle depends on TabManager, but TabManager doesn't
 // support Android. Enable after Android is supported.
@@ -416,7 +419,7 @@
 // Origin-Signed HTTP Exchanges (for WebPackage Loading)
 // https://www.chromestatus.com/features/5745285984681984
 const base::Feature kSignedHTTPExchange{"SignedHTTPExchange",
-                                        base::FEATURE_DISABLED_BY_DEFAULT};
+                                        base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Send "Accept: application/signed-exchange" header to origins who opt-in.
 const base::Feature kSignedHTTPExchangeAcceptHeader{
@@ -674,14 +677,8 @@
 // Makes all WebUI that uses Polymer use 2.x version.
 // TODO(dpapad): Remove this once Polymer 2 migration is done,
 // https://crbug.com/738611.
-const base::Feature kWebUIPolymer2 {
-  "WebUIPolymer2",
-#if !defined(OS_CHROMEOS)
-      base::FEATURE_ENABLED_BY_DEFAULT
-#else
-      base::FEATURE_DISABLED_BY_DEFAULT
-#endif  // !defined(OS_CHROMEOS)
-};
+const base::Feature kWebUIPolymer2{"WebUIPolymer2",
+                                   base::FEATURE_ENABLED_BY_DEFAULT};
 #endif  // !defined(OS_ANDROID)
 
 #if defined(OS_CHROMEOS)
diff --git a/content/public/common/content_features.h b/content/public/common/content_features.h
index 2cfd88a9..b4e0453 100644
--- a/content/public/common/content_features.h
+++ b/content/public/common/content_features.h
@@ -66,7 +66,6 @@
 CONTENT_EXPORT extern const base::Feature kLazyInitializeMediaControls;
 CONTENT_EXPORT extern const base::Feature kLowPriorityIframes;
 CONTENT_EXPORT extern const base::Feature kMediaDevicesSystemMonitorCache;
-CONTENT_EXPORT extern const base::Feature kMemoryCoordinator;
 CONTENT_EXPORT extern const base::Feature kMimeHandlerViewInCrossProcessFrame;
 CONTENT_EXPORT extern const base::Feature kMojoVideoCapture;
 CONTENT_EXPORT extern const base::Feature kMojoVideoCaptureSecondary;
@@ -80,9 +79,12 @@
 CONTENT_EXPORT extern const base::Feature kPassiveDocumentEventListeners;
 CONTENT_EXPORT extern const base::Feature kPassiveDocumentWheelEventListeners;
 CONTENT_EXPORT extern const base::Feature kPassiveEventListenersDueToFling;
+CONTENT_EXPORT extern const base::Feature kPaymentRequestHasEnrolledInstrument;
 CONTENT_EXPORT extern const base::Feature kPdfIsolation;
 CONTENT_EXPORT extern const base::Feature kPerNavigationMojoInterface;
 CONTENT_EXPORT extern const base::Feature kPepper3DImageChromium;
+CONTENT_EXPORT extern const base::Feature
+    kProcessSharingWithStrictSiteInstances;
 CONTENT_EXPORT extern const base::Feature kPurgeAndSuspend;
 CONTENT_EXPORT extern const base::Feature kRasterInducingScroll;
 CONTENT_EXPORT extern const base::Feature kRenderingPipelineThrottling;
diff --git a/content/public/common/web_preferences.cc b/content/public/common/web_preferences.cc
index 8ce9ab7..bb5de85 100644
--- a/content/public/common/web_preferences.cc
+++ b/content/public/common/web_preferences.cc
@@ -26,15 +26,15 @@
 STATIC_ASSERT_ENUM(EDITING_BEHAVIOR_ANDROID,
                    WebSettings::EditingBehavior::kAndroid);
 
-STATIC_ASSERT_ENUM(V8_CACHE_OPTIONS_DEFAULT,
+STATIC_ASSERT_ENUM(blink::mojom::V8CacheOptions::kDefault,
                    WebSettings::V8CacheOptions::kDefault);
-STATIC_ASSERT_ENUM(V8_CACHE_OPTIONS_NONE, WebSettings::V8CacheOptions::kNone);
-STATIC_ASSERT_ENUM(V8_CACHE_OPTIONS_CODE, WebSettings::V8CacheOptions::kCode);
-STATIC_ASSERT_ENUM(V8_CACHE_OPTIONS_CODE_WITHOUT_HEAT_CHECK,
+STATIC_ASSERT_ENUM(blink::mojom::V8CacheOptions::kNone,
+                   WebSettings::V8CacheOptions::kNone);
+STATIC_ASSERT_ENUM(blink::mojom::V8CacheOptions::kCode,
+                   WebSettings::V8CacheOptions::kCode);
+STATIC_ASSERT_ENUM(blink::mojom::V8CacheOptions::kCodeWithoutHeatCheck,
                    WebSettings::V8CacheOptions::kCodeWithoutHeatCheck);
-STATIC_ASSERT_ENUM(V8_CACHE_OPTIONS_FULLCODE_WITHOUT_HEAT_CHECK,
-                   WebSettings::V8CacheOptions::kFullCodeWithoutHeatCheck);
-STATIC_ASSERT_ENUM(V8_CACHE_OPTIONS_LAST,
+STATIC_ASSERT_ENUM(blink::mojom::V8CacheOptions::kFullCodeWithoutHeatCheck,
                    WebSettings::V8CacheOptions::kFullCodeWithoutHeatCheck);
 
 STATIC_ASSERT_ENUM(IMAGE_ANIMATION_POLICY_ALLOWED,
@@ -168,7 +168,7 @@
       spatial_navigation_enabled(false),
       use_solid_color_scrollbars(false),
       navigate_on_drag_drop(true),
-      v8_cache_options(V8_CACHE_OPTIONS_DEFAULT),
+      v8_cache_options(blink::mojom::V8CacheOptions::kDefault),
       record_whole_document(false),
       cookie_enabled(true),
       accelerated_video_decode_enabled(false),
diff --git a/content/public/common/web_preferences.h b/content/public/common/web_preferences.h
index 52edd1a..231b5f3 100644
--- a/content/public/common/web_preferences.h
+++ b/content/public/common/web_preferences.h
@@ -14,6 +14,7 @@
 #include "build/build_config.h"
 #include "content/common/content_export.h"
 #include "net/nqe/effective_connection_type.h"
+#include "third_party/blink/public/mojom/v8_cache_options.mojom.h"
 #include "ui/base/pointer/pointer_device.h"
 #include "url/gurl.h"
 
@@ -35,16 +36,6 @@
   EDITING_BEHAVIOR_LAST = EDITING_BEHAVIOR_ANDROID
 };
 
-// Cache options for V8. See V8CacheOptions.h for information on the options.
-enum V8CacheOptions {
-  V8_CACHE_OPTIONS_DEFAULT,
-  V8_CACHE_OPTIONS_NONE,
-  V8_CACHE_OPTIONS_CODE,
-  V8_CACHE_OPTIONS_CODE_WITHOUT_HEAT_CHECK,
-  V8_CACHE_OPTIONS_FULLCODE_WITHOUT_HEAT_CHECK,
-  V8_CACHE_OPTIONS_LAST = V8_CACHE_OPTIONS_FULLCODE_WITHOUT_HEAT_CHECK
-};
-
 // ImageAnimationPolicy is used for controlling image animation
 // when image frame is rendered for animation.
 // See third_party/WebKit/Source/platform/graphics/ImageAnimationPolicy.h
@@ -192,7 +183,7 @@
   bool spatial_navigation_enabled;
   bool use_solid_color_scrollbars;
   bool navigate_on_drag_drop;
-  V8CacheOptions v8_cache_options;
+  blink::mojom::V8CacheOptions v8_cache_options;
   bool record_whole_document;
 
   // This flags corresponds to a Page's Settings' setCookieEnabled state. It
diff --git a/content/public/renderer/render_thread.h b/content/public/renderer/render_thread.h
index 5ed2164..d11233f 100644
--- a/content/public/renderer/render_thread.h
+++ b/content/public/renderer/render_thread.h
@@ -7,6 +7,7 @@
 
 #include <stddef.h>
 #include <stdint.h>
+#include <memory>
 
 #include "base/callback.h"
 #include "base/memory/shared_memory.h"
@@ -81,7 +82,7 @@
       size_t buffer_size) = 0;
 
   // Registers the given V8 extension with WebKit.
-  virtual void RegisterExtension(v8::Extension* extension) = 0;
+  virtual void RegisterExtension(std::unique_ptr<v8::Extension> extension) = 0;
 
   // Post task to all worker threads. Returns number of workers.
   virtual int PostTaskToAllWebWorkers(const base::Closure& closure) = 0;
diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc
index 7cde5af0..5bb0e93 100644
--- a/content/public/test/browser_test_base.cc
+++ b/content/public/test/browser_test_base.cc
@@ -193,7 +193,7 @@
   // when sharded.
   command_line->AppendSwitchASCII(
       switches::kIPCConnectionTimeout,
-      base::Int64ToString(TestTimeouts::action_max_timeout().InSeconds()));
+      base::NumberToString(TestTimeouts::action_max_timeout().InSeconds()));
 
   // The tests assume that file:// URIs can freely access other file:// URIs.
   if (AllowFileAccessFromFiles())
diff --git a/content/public/test/fake_service_worker_context.cc b/content/public/test/fake_service_worker_context.cc
index 68e4e29..5ff19dc 100644
--- a/content/public/test/fake_service_worker_context.cc
+++ b/content/public/test/fake_service_worker_context.cc
@@ -63,7 +63,6 @@
 }
 void FakeServiceWorkerContext::CheckHasServiceWorker(
     const GURL& url,
-    const GURL& other_url,
     CheckHasServiceWorkerCallback callback) {
   NOTREACHED();
 }
diff --git a/content/public/test/fake_service_worker_context.h b/content/public/test/fake_service_worker_context.h
index 81cf1fa..088583c 100644
--- a/content/public/test/fake_service_worker_context.h
+++ b/content/public/test/fake_service_worker_context.h
@@ -49,7 +49,6 @@
   void DeleteForOrigin(const GURL& origin, ResultCallback callback) override;
   void PerformStorageCleanup(base::OnceClosure callback) override;
   void CheckHasServiceWorker(const GURL& url,
-                             const GURL& other_url,
                              CheckHasServiceWorkerCallback callback) override;
   void ClearAllServiceWorkersForTest(base::OnceClosure) override;
   void StartWorkerForScope(
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h
index cd3fd88..56261b6 100644
--- a/content/public/test/mock_navigation_handle.h
+++ b/content/public/test/mock_navigation_handle.h
@@ -83,7 +83,9 @@
   bool WasResponseCached() override { return was_response_cached_; }
   const net::ProxyServer& GetProxyServer() override { return proxy_server_; }
   MOCK_METHOD0(GetHrefTranslate, const std::string&());
-  MOCK_METHOD0(GetInitiatorOrigin, const base::Optional<url::Origin>&());
+  const base::Optional<url::Origin>& GetInitiatorOrigin() override {
+    return initiator_origin_;
+  }
   MOCK_METHOD1(RegisterThrottleForTesting,
                void(std::unique_ptr<NavigationThrottle>));
   MOCK_METHOD0(IsDeferredForTesting, bool());
@@ -149,6 +151,7 @@
   bool is_form_submission_ = false;
   bool was_response_cached_ = false;
   net::ProxyServer proxy_server_;
+  base::Optional<url::Origin> initiator_origin_;
 };
 
 }  // namespace content
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index 3b94a8b7..fe409a7 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -4,6 +4,8 @@
 
 #include "content/public/test/mock_render_thread.h"
 
+#include <memory>
+
 #include "base/logging.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -197,8 +199,9 @@
   return std::unique_ptr<base::SharedMemory>(shared_buf.release());
 }
 
-void MockRenderThread::RegisterExtension(v8::Extension* extension) {
-  blink::WebScriptController::RegisterExtension(extension);
+void MockRenderThread::RegisterExtension(
+    std::unique_ptr<v8::Extension> extension) {
+  blink::WebScriptController::RegisterExtension(std::move(extension));
 }
 
 int MockRenderThread::PostTaskToAllWebWorkers(const base::Closure& closure) {
diff --git a/content/public/test/mock_render_thread.h b/content/public/test/mock_render_thread.h
index acec3326..7c03ae9 100644
--- a/content/public/test/mock_render_thread.h
+++ b/content/public/test/mock_render_thread.h
@@ -72,7 +72,7 @@
   void RecordComputedAction(const std::string& action) override;
   std::unique_ptr<base::SharedMemory> HostAllocateSharedMemoryBuffer(
       size_t buffer_size) override;
-  void RegisterExtension(v8::Extension* extension) override;
+  void RegisterExtension(std::unique_ptr<v8::Extension> extension) override;
   int PostTaskToAllWebWorkers(const base::Closure& closure) override;
   bool ResolveProxy(const GURL& url, std::string* proxy_list) override;
   base::WaitableEvent* GetShutdownEvent() override;
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index c557a57..66b68ae 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -431,8 +431,6 @@
     "navigation_state.h",
     "net_info_helper.cc",
     "net_info_helper.h",
-    "notifications/notification_data_conversions.cc",
-    "notifications/notification_data_conversions.h",
     "p2p/empty_network_manager.cc",
     "p2p/empty_network_manager.h",
     "p2p/filtering_network_manager.cc",
@@ -536,12 +534,6 @@
     "service_worker/web_service_worker_network_provider_impl_for_frame.h",
     "service_worker/web_service_worker_provider_impl.cc",
     "service_worker/web_service_worker_provider_impl.h",
-    "shared_worker/embedded_shared_worker_stub.cc",
-    "shared_worker/embedded_shared_worker_stub.h",
-    "shared_worker/shared_worker_factory_impl.cc",
-    "shared_worker/shared_worker_factory_impl.h",
-    "shared_worker/web_service_worker_network_provider_impl_for_worker.cc",
-    "shared_worker/web_service_worker_network_provider_impl_for_worker.h",
     "skia_benchmarking_extension.cc",
     "skia_benchmarking_extension.h",
     "stats_collection_controller.cc",
@@ -568,8 +560,14 @@
     "web_ui_extension_data.h",
     "webgraphicscontext3d_provider_impl.cc",
     "webgraphicscontext3d_provider_impl.h",
-    "worker_thread_registry.cc",
-    "worker_thread_registry.h",
+    "worker/embedded_shared_worker_stub.cc",
+    "worker/embedded_shared_worker_stub.h",
+    "worker/shared_worker_factory_impl.cc",
+    "worker/shared_worker_factory_impl.h",
+    "worker/web_service_worker_network_provider_impl_for_worker.cc",
+    "worker/web_service_worker_network_provider_impl_for_worker.h",
+    "worker/worker_thread_registry.cc",
+    "worker/worker_thread_registry.h",
   ]
 
   if (!is_component_build) {
diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc
index fc5ad8fa..deb05aa 100644
--- a/content/renderer/browser_plugin/browser_plugin.cc
+++ b/content/renderer/browser_plugin/browser_plugin.cc
@@ -455,7 +455,7 @@
   // by firing an event from there.
   UpdateDOMAttribute(
       "internalinstanceid",
-      base::UTF8ToUTF16(base::IntToString(browser_plugin_instance_id_)));
+      base::UTF8ToUTF16(base::NumberToString(browser_plugin_instance_id_)));
 }
 
 void BrowserPlugin::UpdateGuestFocusState(blink::WebFocusType focus_type) {
diff --git a/content/renderer/gpu_benchmarking_extension.cc b/content/renderer/gpu_benchmarking_extension.cc
index 16042eca..5d2a760 100644
--- a/content/renderer/gpu_benchmarking_extension.cc
+++ b/content/renderer/gpu_benchmarking_extension.cc
@@ -107,7 +107,8 @@
       // with
       // --no-sandbox command-line flag. Get rid of this limitation.
       // CRBUG: 139640.
-      std::string filename = "layer_" + base::IntToString(layer_id_++) + ".skp";
+      std::string filename =
+          "layer_" + base::NumberToString(layer_id_++) + ".skp";
       std::string filepath = dirpath_.AppendASCII(filename).MaybeAsASCII();
       DCHECK(!filepath.empty());
       SkFILEWStream file(filepath.c_str());
diff --git a/content/renderer/input/main_thread_event_queue.cc b/content/renderer/input/main_thread_event_queue.cc
index cc128fe..4a77a0e 100644
--- a/content/renderer/input/main_thread_event_queue.cc
+++ b/content/renderer/input/main_thread_event_queue.cc
@@ -366,7 +366,7 @@
   std::unique_ptr<QueuedClosure> item(new QueuedClosure(std::move(closure)));
   {
     base::AutoLock lock(shared_state_lock_);
-    shared_state_.events_.Queue(std::move(item));
+    shared_state_.events_.Enqueue(std::move(item));
     needs_post_task = !shared_state_.sent_post_task_;
     shared_state_.sent_post_task_ = true;
   }
@@ -533,13 +533,22 @@
   bool is_raf_aligned = IsRafAlignedEvent(event);
   bool needs_main_frame = false;
   bool needs_post_task = false;
+
+  // Record the input event's type prior to enqueueing so that the scheduler
+  // can be notified of its dispatch (if the event is not coalesced).
+  bool is_input_event = event->IsWebInputEvent();
+  WebInputEvent::Type input_event_type = WebInputEvent::kUndefined;
+  if (is_input_event) {
+    auto* queued_input_event =
+        static_cast<const QueuedWebInputEvent*>(event.get());
+    input_event_type = queued_input_event->event().GetType();
+  }
+
   {
     base::AutoLock lock(shared_state_lock_);
-    size_t size_before = shared_state_.events_.size();
-    shared_state_.events_.Queue(std::move(event));
-    size_t size_after = shared_state_.events_.size();
 
-    if (size_before != size_after) {
+    if (shared_state_.events_.Enqueue(std::move(event)) ==
+        MainThreadEventQueueTaskList::EnqueueResult::kEnqueued) {
       if (!is_raf_aligned) {
         needs_post_task = !shared_state_.sent_post_task_;
         shared_state_.sent_post_task_ = true;
@@ -547,6 +556,12 @@
         needs_main_frame = !shared_state_.sent_main_frame_request_;
         shared_state_.sent_main_frame_request_ = true;
       }
+
+      // Notify the scheduler that we'll enqueue a task to the main thread.
+      if (is_input_event && main_thread_scheduler_) {
+        main_thread_scheduler_->WillPostInputEventToMainThread(
+            input_event_type);
+      }
     }
   }
 
@@ -602,6 +617,10 @@
     const blink::WebCoalescedInputEvent& event,
     const ui::LatencyInfo& latency,
     HandledEventCallback handled_callback) {
+  // Notify the scheduler that the main thread is about to execute handlers.
+  if (auto* scheduler = main_thread_scheduler_)
+    scheduler->WillHandleInputEventOnMainThread(event.Event().GetType());
+
   bool handled = false;
   if (client_) {
     handled =
diff --git a/content/renderer/input/main_thread_event_queue_task_list.cc b/content/renderer/input/main_thread_event_queue_task_list.cc
index 32a5eaa..7a0ecd7 100644
--- a/content/renderer/input/main_thread_event_queue_task_list.cc
+++ b/content/renderer/input/main_thread_event_queue_task_list.cc
@@ -4,19 +4,22 @@
 
 #include "content/renderer/input/main_thread_event_queue_task_list.h"
 
+#include <utility>
+
 namespace content {
 
 MainThreadEventQueueTaskList::MainThreadEventQueueTaskList() {}
 
 MainThreadEventQueueTaskList::~MainThreadEventQueueTaskList() {}
 
-void MainThreadEventQueueTaskList::Queue(
+MainThreadEventQueueTaskList::EnqueueResult
+MainThreadEventQueueTaskList::Enqueue(
     std::unique_ptr<MainThreadEventQueueTask> event) {
   for (auto last_event_iter = queue_.rbegin(); last_event_iter != queue_.rend();
        ++last_event_iter) {
     switch ((*last_event_iter)->FilterNewEvent(event.get())) {
       case MainThreadEventQueueTask::FilterResult::CoalescedEvent:
-        return;
+        return EnqueueResult::kCoalesced;
       case MainThreadEventQueueTask::FilterResult::StopIterating:
         break;
       case MainThreadEventQueueTask::FilterResult::KeepIterating:
@@ -25,6 +28,7 @@
     break;
   }
   queue_.emplace_back(std::move(event));
+  return EnqueueResult::kEnqueued;
 }
 
 std::unique_ptr<MainThreadEventQueueTask> MainThreadEventQueueTaskList::Pop() {
diff --git a/content/renderer/input/main_thread_event_queue_task_list.h b/content/renderer/input/main_thread_event_queue_task_list.h
index 94c0162..097d714 100644
--- a/content/renderer/input/main_thread_event_queue_task_list.h
+++ b/content/renderer/input/main_thread_event_queue_task_list.h
@@ -16,12 +16,14 @@
 // This class supports coalescing upon queueing a task.
 class MainThreadEventQueueTaskList {
  public:
+  enum class EnqueueResult { kCoalesced, kEnqueued };
+
   MainThreadEventQueueTaskList();
   ~MainThreadEventQueueTaskList();
 
   // Adds an event to the queue. The event may be coalesced with previously
   // queued events.
-  void Queue(std::unique_ptr<MainThreadEventQueueTask> event);
+  EnqueueResult Enqueue(std::unique_ptr<MainThreadEventQueueTask> event);
   std::unique_ptr<MainThreadEventQueueTask> Pop();
 
   const std::unique_ptr<MainThreadEventQueueTask>& front() const {
diff --git a/content/renderer/loader/resource_dispatcher.cc b/content/renderer/loader/resource_dispatcher.cc
index f395301..aa381ae 100644
--- a/content/renderer/loader/resource_dispatcher.cc
+++ b/content/renderer/loader/resource_dispatcher.cc
@@ -406,6 +406,8 @@
   // TODO(yhirano): Consider using int64_t in
   // RequestPeer::OnTransferSizeUpdated.
   request_info->peer->OnTransferSizeUpdated(transfer_size_diff);
+  if (!GetPendingRequestInfo(request_id))
+    return;
 
   NotifyResourceTransferSizeUpdated(request_info->render_frame_id,
                                     request_info->resource_load_info.get(),
diff --git a/content/renderer/loader/url_loader_client_impl.cc b/content/renderer/loader/url_loader_client_impl.cc
index cd419ea0..85422ac5 100644
--- a/content/renderer/loader/url_loader_client_impl.cc
+++ b/content/renderer/loader/url_loader_client_impl.cc
@@ -379,7 +379,8 @@
 }
 
 bool URLLoaderClientImpl::NeedsStoringMessage() const {
-  return is_deferred_ || deferred_messages_.size() > 0;
+  return is_deferred_ || deferred_messages_.size() > 0 ||
+         accumulated_transfer_size_diff_during_deferred_ > 0;
 }
 
 void URLLoaderClientImpl::StoreAndDispatch(
@@ -387,7 +388,8 @@
   DCHECK(NeedsStoringMessage());
   if (is_deferred_) {
     deferred_messages_.push_back(std::move(message));
-  } else if (deferred_messages_.size() > 0) {
+  } else if (deferred_messages_.size() > 0 ||
+             accumulated_transfer_size_diff_during_deferred_ > 0) {
     deferred_messages_.push_back(std::move(message));
     FlushDeferredMessages();
   } else {
diff --git a/content/renderer/loader/web_url_loader_impl.cc b/content/renderer/loader/web_url_loader_impl.cc
index 99521d6e..2aa68534 100644
--- a/content/renderer/loader/web_url_loader_impl.cc
+++ b/content/renderer/loader/web_url_loader_impl.cc
@@ -920,7 +920,7 @@
         MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
         base::BindRepeating(&WebURLLoaderImpl::Context::OnBodyAvailable, this));
     if (defers_loading_ == NOT_DEFERRING)
-      body_watcher_.ArmOrNotify();
+      OnBodyAvailable(MOJO_RESULT_OK, {});
     return;
   }
 
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.cc b/content/renderer/loader/web_worker_fetch_context_impl.cc
index c8e78c1..bd2ef04 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.cc
+++ b/content/renderer/loader/web_worker_fetch_context_impl.cc
@@ -7,7 +7,6 @@
 #include <utility>
 
 #include "base/bind.h"
-#include "base/feature_list.h"
 #include "base/single_thread_task_runner.h"
 #include "base/task/post_task.h"
 #include "content/child/child_thread_impl.h"
@@ -15,7 +14,6 @@
 #include "content/common/content_constants_internal.h"
 #include "content/common/frame_messages.h"
 #include "content/public/common/content_client.h"
-#include "content/public/common/content_features.h"
 #include "content/public/common/origin_util.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/public/renderer/content_renderer_client.h"
@@ -30,11 +28,12 @@
 #include "content/renderer/service_worker/controller_service_worker_connector.h"
 #include "content/renderer/service_worker/service_worker_provider_context.h"
 #include "content/renderer/service_worker/service_worker_subresource_loader.h"
-#include "content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h"
+#include "content/renderer/worker/web_service_worker_network_provider_impl_for_worker.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
 #include "services/service_manager/public/cpp/connector.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
@@ -373,6 +372,12 @@
     extra_data->set_url_loader_throttles(throttle_provider_->CreateThrottles(
         ancestor_frame_id_, request, WebURLRequestToResourceType(request)));
   }
+  if (response_override_) {
+    DCHECK(blink::features::IsOffMainThreadSharedWorkerScriptFetchEnabled());
+    DCHECK_EQ(blink::mojom::RequestContextType::SHARED_WORKER,
+              request.GetRequestContext());
+    extra_data->set_navigation_response_override(std::move(response_override_));
+  }
   request.SetExtraData(std::move(extra_data));
   request.SetAppCacheHostID(appcache_host_id_);
 
@@ -482,6 +487,13 @@
   client_id_ = client_id;
 }
 
+void WebWorkerFetchContextImpl::SetResponseOverrideForMainScript(
+    std::unique_ptr<NavigationResponseOverrideParameters> response_override) {
+  DCHECK(blink::features::IsOffMainThreadSharedWorkerScriptFetchEnabled());
+  DCHECK(!response_override_);
+  response_override_ = std::move(response_override);
+}
+
 void WebWorkerFetchContextImpl::SetApplicationCacheHostID(int id) {
   appcache_host_id_ = id;
 }
diff --git a/content/renderer/loader/web_worker_fetch_context_impl.h b/content/renderer/loader/web_worker_fetch_context_impl.h
index 88b05dc..1aa4704b 100644
--- a/content/renderer/loader/web_worker_fetch_context_impl.h
+++ b/content/renderer/loader/web_worker_fetch_context_impl.h
@@ -40,6 +40,7 @@
 class ThreadSafeSender;
 class URLLoaderThrottleProvider;
 class WebSocketHandshakeThrottleProvider;
+struct NavigationResponseOverrideParameters;
 
 // This class is used for fetching resource requests from workers (dedicated
 // worker and shared worker). This class is created on the main thread and
@@ -124,6 +125,11 @@
   void set_origin_url(const GURL& origin_url);
   void set_client_id(const std::string& client_id);
 
+  // PlzWorker with off-the-main-thread worker script fetch:
+  // Sets the response for the worker main script loaded by the browser process.
+  void SetResponseOverrideForMainScript(
+      std::unique_ptr<NavigationResponseOverrideParameters> response_override);
+
   using RewriteURLFunction = blink::WebURL (*)(const std::string&, bool);
   static void InstallRewriteURLFunction(RewriteURLFunction rewrite_url);
 
@@ -265,6 +271,8 @@
 
   std::unique_ptr<service_manager::Connector> service_manager_connection_;
 
+  std::unique_ptr<NavigationResponseOverrideParameters> response_override_;
+
   blink::AcceptLanguagesWatcher* accept_languages_watcher_ = nullptr;
 };
 
diff --git a/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc b/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc
index face97b..af68f05 100644
--- a/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc
+++ b/content/renderer/media/stream/mock_mojo_media_stream_dispatcher_host.cc
@@ -32,18 +32,20 @@
 
   if (controls.audio.requested) {
     blink::MediaStreamDevice audio_device;
-    audio_device.id = controls.audio.device_id + base::IntToString(session_id_);
+    audio_device.id =
+        controls.audio.device_id + base::NumberToString(session_id_);
     audio_device.name = "microphone";
     audio_device.type = controls.audio.stream_type;
     audio_device.session_id = session_id_;
     audio_device.matched_output_device_id =
-        "associated_output_device_id" + base::IntToString(request_id_);
+        "associated_output_device_id" + base::NumberToString(request_id_);
     audio_devices_.push_back(audio_device);
   }
 
   if (controls.video.requested) {
     blink::MediaStreamDevice video_device;
-    video_device.id = controls.video.device_id + base::IntToString(session_id_);
+    video_device.id =
+        controls.video.device_id + base::NumberToString(session_id_);
     video_device.name = "usb video camera";
     video_device.type = controls.video.stream_type;
     video_device.video_facing = media::MEDIA_VIDEO_FACING_USER;
@@ -55,7 +57,7 @@
     generate_stream_cb_ = std::move(callback);
   } else {
     std::move(callback).Run(blink::MEDIA_DEVICE_OK,
-                            "dummy" + base::IntToString(request_id_),
+                            "dummy" + base::NumberToString(request_id_),
                             audio_devices_, video_devices_);
   }
 }
@@ -92,7 +94,7 @@
   device.type = type;
   device.session_id = session_id_;
   std::move(callback).Run(true /* success */,
-                          "dummy" + base::IntToString(request_id), device);
+                          "dummy" + base::NumberToString(request_id), device);
 }
 
 }  // namespace content
diff --git a/content/renderer/media/webrtc/peer_connection_tracker.cc b/content/renderer/media/webrtc/peer_connection_tracker.cc
index b8a467c..c4a23f2 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker.cc
@@ -686,7 +686,7 @@
     return;
   std::string value =
       "sdpMid: " + candidate->SdpMid().Utf8() + ", " +
-      "sdpMLineIndex: " + base::UintToString(candidate->SdpMLineIndex()) +
+      "sdpMLineIndex: " + base::NumberToString(candidate->SdpMLineIndex()) +
       ", " + "candidate: " + candidate->Candidate().Utf8();
 
   // OnIceCandidate always succeeds as it's a callback from the browser.
@@ -764,7 +764,7 @@
         blink::WebRTCRtpTransceiverImplementationType::kPlanBReceiverOnly);
     result += "getReceivers()";
   }
-  result += "[" + base::UintToString(transceiver_index) + "]:";
+  result += "[" + base::NumberToString(transceiver_index) + "]:";
   result += SerializeTransceiver(transceiver);
   SendPeerConnectionUpdate(id, callback_type, result);
 }
diff --git a/content/renderer/media_capture_from_element/canvas_capture_handler.cc b/content/renderer/media_capture_from_element/canvas_capture_handler.cc
index 547fd0a8..d1f6565 100644
--- a/content/renderer/media_capture_from_element/canvas_capture_handler.cc
+++ b/content/renderer/media_capture_from_element/canvas_capture_handler.cc
@@ -27,6 +27,7 @@
 #include "third_party/libyuv/include/libyuv.h"
 #include "third_party/skia/include/core/SkImage.h"
 #include "third_party/skia/include/gpu/GrBackendSurface.h"
+#include "ui/gfx/color_space.h"
 
 using media::VideoFrame;
 
@@ -175,7 +176,7 @@
                                 static_cast<const uint8_t*>(pixmap.addr(0, 0)),
                                 gfx::Size(pixmap.width(), pixmap.height()),
                                 pixmap.rowBytes(), pixmap.colorType()),
-              timestamp);
+              timestamp, image->refColorSpace());
     return;
   }
 
@@ -268,7 +269,7 @@
           is_opaque, false /* flip */,
           temp_argb_frame->visible_data(VideoFrame::kARGBPlane), image_size,
           temp_argb_frame->stride(VideoFrame::kARGBPlane), kN32_SkColorType),
-      timestamp);
+      timestamp, image->refColorSpace());
 }
 
 void CanvasCaptureHandler::ReadARGBPixelsAsync(
@@ -360,6 +361,7 @@
   }
   // Let |image| fall out of scope after we are done reading.
   const bool is_opaque = image->isOpaque();
+  const auto color_space = image->refColorSpace();
   image = nullptr;
 
   SendFrame(
@@ -368,11 +370,11 @@
                         temp_argb_frame->visible_rect().size(),
                         temp_argb_frame->stride(VideoFrame::kARGBPlane),
                         kN32_SkColorType),
-      this_frame_ticks);
+      this_frame_ticks, color_space);
 }
 
 void CanvasCaptureHandler::OnYUVPixelsReadAsync(
-    sk_sp<SkImage> /* image */,
+    sk_sp<SkImage> image,
     scoped_refptr<media::VideoFrame> yuv_frame,
     base::TimeTicks this_frame_ticks,
     bool success) {
@@ -381,7 +383,7 @@
     DLOG(ERROR) << "Couldn't read SkImage using async callback";
     return;
   }
-  SendFrame(yuv_frame, this_frame_ticks);
+  SendFrame(yuv_frame, this_frame_ticks, image->refColorSpace());
 }
 
 scoped_refptr<media::VideoFrame> CanvasCaptureHandler::ConvertToYUVFrame(
@@ -444,7 +446,8 @@
 }
 
 void CanvasCaptureHandler::SendFrame(scoped_refptr<VideoFrame> video_frame,
-                                     base::TimeTicks this_frame_ticks) {
+                                     base::TimeTicks this_frame_ticks,
+                                     sk_sp<SkColorSpace> color_space) {
   DCHECK(main_render_thread_checker_.CalledOnValidThread());
 
   // If this function is called asynchronously, |delegate_| might have been
@@ -455,6 +458,8 @@
   if (!first_frame_ticks_)
     first_frame_ticks_ = this_frame_ticks;
   video_frame->set_timestamp(this_frame_ticks - *first_frame_ticks_);
+  if (color_space)
+    video_frame->set_color_space(gfx::ColorSpace(*color_space));
 
   last_frame_ = video_frame;
   io_task_runner_->PostTask(
diff --git a/content/renderer/media_capture_from_element/canvas_capture_handler.h b/content/renderer/media_capture_from_element/canvas_capture_handler.h
index 8ff04562..9e40d165 100644
--- a/content/renderer/media_capture_from_element/canvas_capture_handler.h
+++ b/content/renderer/media_capture_from_element/canvas_capture_handler.h
@@ -100,7 +100,8 @@
       int stride,
       SkColorType source_color_type);
   void SendFrame(scoped_refptr<media::VideoFrame> video_frame,
-                 base::TimeTicks this_frame_ticks);
+                 base::TimeTicks this_frame_ticks,
+                 sk_sp<SkColorSpace> color_space);
 
   void AddVideoCapturerSourceToVideoTrack(
       std::unique_ptr<media::VideoCapturerSource> source,
diff --git a/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc b/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
index d8b087d..e09dbf6 100644
--- a/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
+++ b/content/renderer/media_capture_from_element/canvas_capture_handler_unittest.cc
@@ -83,8 +83,11 @@
 
   // Verify returned frames.
   static sk_sp<SkImage> GenerateTestImage(bool opaque, int width, int height) {
+    SkImageInfo info = SkImageInfo::MakeN32(
+        width, height, opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType,
+        SkColorSpace::MakeSRGB());
     SkBitmap testBitmap;
-    testBitmap.allocN32Pixels(width, height, opaque);
+    testBitmap.allocPixels(info);
     testBitmap.eraseARGB(opaque ? 255 : kTestAlphaValue, 30, 60, 200);
     return SkImage::MakeFromBitmap(testBitmap);
   }
@@ -117,6 +120,7 @@
           video_frame->visible_data(media::VideoFrame::kAPlane);
       EXPECT_EQ(kTestAlphaValue, a_plane[0]);
     }
+    EXPECT_TRUE(video_frame->ColorSpace().IsValid());
   }
 
   blink::WebMediaStreamTrack track_;
diff --git a/content/renderer/notifications/OWNERS b/content/renderer/notifications/OWNERS
deleted file mode 100644
index c7d29e3..0000000
--- a/content/renderer/notifications/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-file://content/browser/notifications/OWNERS
-
-# TEAM: platform-capabilities@chromium.org
-# COMPONENT: UI>Notifications
diff --git a/content/renderer/notifications/notification_data_conversions.cc b/content/renderer/notifications/notification_data_conversions.cc
deleted file mode 100644
index 790609b..0000000
--- a/content/renderer/notifications/notification_data_conversions.cc
+++ /dev/null
@@ -1,78 +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 "content/renderer/notifications/notification_data_conversions.h"
-
-#include <stddef.h>
-
-#include "base/strings/utf_string_conversions.h"
-#include "base/time/time.h"
-#include "third_party/blink/public/mojom/notifications/notification.mojom-shared.h"
-#include "third_party/blink/public/platform/modules/notifications/web_notification_action.h"
-#include "third_party/blink/public/platform/url_conversion.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/platform/web_url.h"
-#include "third_party/blink/public/platform/web_vector.h"
-
-using blink::WebNotificationData;
-using blink::WebString;
-
-namespace content {
-
-WebNotificationData ToWebNotificationData(
-    const blink::PlatformNotificationData& platform_data) {
-  WebNotificationData web_data;
-  web_data.title = WebString::FromUTF16(platform_data.title);
-  switch (platform_data.direction) {
-    case blink::PlatformNotificationData::DIRECTION_LEFT_TO_RIGHT:
-      web_data.direction = blink::mojom::NotificationDirection::LEFT_TO_RIGHT;
-      break;
-    case blink::PlatformNotificationData::DIRECTION_RIGHT_TO_LEFT:
-      web_data.direction = blink::mojom::NotificationDirection::RIGHT_TO_LEFT;
-      break;
-    case blink::PlatformNotificationData::DIRECTION_AUTO:
-      web_data.direction = blink::mojom::NotificationDirection::AUTO;
-      break;
-  }
-
-  web_data.lang = WebString::FromUTF8(platform_data.lang);
-  web_data.body = WebString::FromUTF16(platform_data.body);
-  web_data.tag = WebString::FromUTF8(platform_data.tag);
-  web_data.image = blink::WebURL(platform_data.image);
-  web_data.icon = blink::WebURL(platform_data.icon);
-  web_data.badge = blink::WebURL(platform_data.badge);
-  web_data.vibrate = platform_data.vibration_pattern;
-  web_data.timestamp = platform_data.timestamp.ToJsTime();
-  web_data.renotify = platform_data.renotify;
-  web_data.silent = platform_data.silent;
-  web_data.require_interaction = platform_data.require_interaction;
-  web_data.data = platform_data.data;
-  blink::WebVector<blink::WebNotificationAction> resized(
-      platform_data.actions.size());
-  web_data.actions.Swap(resized);
-  for (size_t i = 0; i < platform_data.actions.size(); ++i) {
-    switch (platform_data.actions[i].type) {
-      case blink::PLATFORM_NOTIFICATION_ACTION_TYPE_BUTTON:
-        web_data.actions[i].type = blink::WebNotificationAction::kButton;
-        break;
-      case blink::PLATFORM_NOTIFICATION_ACTION_TYPE_TEXT:
-        web_data.actions[i].type = blink::WebNotificationAction::kText;
-        break;
-      default:
-        NOTREACHED() << "Unknown platform data type: "
-                     << platform_data.actions[i].type;
-    }
-    web_data.actions[i].action =
-        WebString::FromUTF8(platform_data.actions[i].action);
-    web_data.actions[i].title =
-        WebString::FromUTF16(platform_data.actions[i].title);
-    web_data.actions[i].icon = blink::WebURL(platform_data.actions[i].icon);
-    web_data.actions[i].placeholder =
-        WebString::FromUTF16(platform_data.actions[i].placeholder);
-  }
-
-  return web_data;
-}
-
-}  // namespace content
diff --git a/content/renderer/notifications/notification_data_conversions.h b/content/renderer/notifications/notification_data_conversions.h
deleted file mode 100644
index 9162657..0000000
--- a/content/renderer/notifications/notification_data_conversions.h
+++ /dev/null
@@ -1,20 +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 CONTENT_RENDERER_NOTIFICATIONS_NOTIFICATION_DATA_CONVERSIONS_H_
-#define CONTENT_RENDERER_NOTIFICATIONS_NOTIFICATION_DATA_CONVERSIONS_H_
-
-#include "content/common/content_export.h"
-#include "third_party/blink/public/common/notifications/platform_notification_data.h"
-#include "third_party/blink/public/platform/modules/notifications/web_notification_data.h"
-
-namespace content {
-
-// Converts blink::PlatformNotificationData to Blink WebNotificationData.
-CONTENT_EXPORT blink::WebNotificationData ToWebNotificationData(
-    const blink::PlatformNotificationData& platform_data);
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_NOTIFICATIONS_NOTIFICATION_DATA_CONVERSIONS_H_
diff --git a/content/renderer/notifications/notification_data_conversions_unittest.cc b/content/renderer/notifications/notification_data_conversions_unittest.cc
deleted file mode 100644
index 527dd6b..0000000
--- a/content/renderer/notifications/notification_data_conversions_unittest.cc
+++ /dev/null
@@ -1,115 +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 "content/renderer/notifications/notification_data_conversions.h"
-
-#include <stddef.h>
-#include <stdint.h>
-
-#include "base/stl_util.h"
-#include "base/strings/utf_string_conversions.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/public/common/notifications/platform_notification_data.h"
-#include "third_party/blink/public/platform/modules/notifications/web_notification_data.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/platform/web_url.h"
-
-namespace content {
-
-const char kNotificationTitle[] = "My Notification";
-const char kNotificationLang[] = "nl";
-const char kNotificationBody[] = "Hello, world!";
-const char kNotificationTag[] = "my_tag";
-const char kNotificationImageUrl[] = "https://example.com/image.jpg";
-const char kNotificationIconUrl[] = "https://example.com/icon.png";
-const char kNotificationBadgeUrl[] = "https://example.com/badge.png";
-const int kNotificationVibrationPattern[] = {100, 200, 300};
-const double kNotificationTimestamp = 621046800.;
-const unsigned char kNotificationData[] = {0xdf, 0xff, 0x0, 0x0, 0xff, 0xdf};
-const char kAction1Name[] = "btn1";
-const char kAction1Title[] = "Button 1";
-const char kAction1IconUrl[] = "https://example.com/action_icon_1.png";
-const char kAction1Placeholder[] = "Run into the... friendliness pellets.";
-const char kAction2Name[] = "btn2";
-const char kAction2Title[] = "Button 2";
-const char kAction2IconUrl[] = "https://example.com/action_icon_2.png";
-
-TEST(NotificationDataConversionsTest, ToWebNotificationData) {
-  std::vector<int> vibration_pattern(
-      kNotificationVibrationPattern,
-      kNotificationVibrationPattern +
-          base::size(kNotificationVibrationPattern));
-
-  std::vector<char> developer_data(
-      kNotificationData, kNotificationData + base::size(kNotificationData));
-
-  blink::PlatformNotificationData platform_data;
-  platform_data.title = base::ASCIIToUTF16(kNotificationTitle);
-  platform_data.direction =
-      blink::PlatformNotificationData::DIRECTION_LEFT_TO_RIGHT;
-  platform_data.lang = kNotificationLang;
-  platform_data.body = base::ASCIIToUTF16(kNotificationBody);
-  platform_data.tag = kNotificationTag;
-  platform_data.image = GURL(kNotificationImageUrl);
-  platform_data.icon = GURL(kNotificationIconUrl);
-  platform_data.badge = GURL(kNotificationBadgeUrl);
-  platform_data.vibration_pattern = vibration_pattern;
-  platform_data.timestamp = base::Time::FromJsTime(kNotificationTimestamp);
-  platform_data.renotify = true;
-  platform_data.silent = true;
-  platform_data.require_interaction = true;
-  platform_data.data = developer_data;
-  platform_data.actions.resize(2);
-  platform_data.actions[0].type =
-      blink::PLATFORM_NOTIFICATION_ACTION_TYPE_BUTTON;
-  platform_data.actions[0].action = kAction1Name;
-  platform_data.actions[0].title = base::ASCIIToUTF16(kAction1Title);
-  platform_data.actions[0].icon = GURL(kAction1IconUrl);
-  platform_data.actions[0].placeholder =
-      base::NullableString16(base::ASCIIToUTF16(kAction1Placeholder), false);
-  platform_data.actions[1].type = blink::PLATFORM_NOTIFICATION_ACTION_TYPE_TEXT;
-  platform_data.actions[1].action = kAction2Name;
-  platform_data.actions[1].title = base::ASCIIToUTF16(kAction2Title);
-  platform_data.actions[1].icon = GURL(kAction2IconUrl);
-  platform_data.actions[1].placeholder = base::NullableString16();
-
-  blink::WebNotificationData web_data = ToWebNotificationData(platform_data);
-  EXPECT_EQ(kNotificationTitle, web_data.title);
-  EXPECT_EQ(blink::mojom::NotificationDirection::LEFT_TO_RIGHT,
-            web_data.direction);
-  EXPECT_EQ(kNotificationLang, web_data.lang);
-  EXPECT_EQ(kNotificationBody, web_data.body);
-  EXPECT_EQ(kNotificationTag, web_data.tag);
-  EXPECT_EQ(kNotificationImageUrl, web_data.image.GetString());
-  EXPECT_EQ(kNotificationIconUrl, web_data.icon.GetString());
-  EXPECT_EQ(kNotificationBadgeUrl, web_data.badge.GetString());
-
-  ASSERT_EQ(vibration_pattern.size(), web_data.vibrate.size());
-  for (size_t i = 0; i < vibration_pattern.size(); ++i)
-    EXPECT_EQ(vibration_pattern[i], web_data.vibrate[i]);
-
-  EXPECT_DOUBLE_EQ(kNotificationTimestamp, web_data.timestamp);
-  EXPECT_TRUE(web_data.renotify);
-  EXPECT_TRUE(web_data.silent);
-  EXPECT_TRUE(web_data.require_interaction);
-
-  ASSERT_EQ(developer_data.size(), web_data.data.size());
-  for (size_t i = 0; i < developer_data.size(); ++i)
-    EXPECT_EQ(developer_data[i], web_data.data[i]);
-
-  ASSERT_EQ(platform_data.actions.size(), web_data.actions.size());
-  EXPECT_EQ(blink::WebNotificationAction::kButton, web_data.actions[0].type);
-  EXPECT_EQ(kAction1Name, web_data.actions[0].action);
-  EXPECT_EQ(kAction1Title, web_data.actions[0].title);
-  EXPECT_EQ(kAction1IconUrl, web_data.actions[0].icon.GetString());
-  EXPECT_EQ(kAction1Placeholder, web_data.actions[0].placeholder);
-  EXPECT_EQ(blink::WebNotificationAction::kText, web_data.actions[1].type);
-  EXPECT_EQ(kAction2Name, web_data.actions[1].action);
-  EXPECT_EQ(kAction2Title, web_data.actions[1].title);
-  EXPECT_EQ(kAction2IconUrl, web_data.actions[1].icon.GetString());
-  EXPECT_TRUE(web_data.actions[1].placeholder.IsNull());
-}
-
-}  // namespace content
diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc
index cbcb0ed..19afe96 100644
--- a/content/renderer/pepper/pepper_plugin_instance_impl.cc
+++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc
@@ -3267,8 +3267,9 @@
 
   blink::WebScreenInfo info = render_frame_->render_view()->GetScreenInfo();
   screen_size_for_fullscreen_ = gfx::Size(info.rect.width, info.rect.height);
-  std::string width = base::IntToString(screen_size_for_fullscreen_.width());
-  std::string height = base::IntToString(screen_size_for_fullscreen_.height());
+  std::string width = base::NumberToString(screen_size_for_fullscreen_.width());
+  std::string height =
+      base::NumberToString(screen_size_for_fullscreen_.height());
 
   WebElement element = container_->GetElement();
   element.SetAttribute(WebString::FromUTF8(kWidth), WebString::FromUTF8(width));
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index c5a69ff..3884c0a7 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -184,6 +184,7 @@
 #include "third_party/blink/public/platform/web_media_player.h"
 #include "third_party/blink/public/platform/web_media_player_source.h"
 #include "third_party/blink/public/platform/web_point.h"
+#include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_scroll_into_view_params.h"
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/platform/web_url.h"
@@ -3293,6 +3294,16 @@
          !base::FeatureList::IsEnabled(network::features::kNetworkService) ||
          subresource_loader_factories);
 
+  // TODO(yoichio): This is temporary switch to have chrome WebUI
+  // use the old web APIs.
+  // After completion of the migration, we should remove this.
+  // See crbug.com/924871 for detail.
+  if (common_params.url.SchemeIs(content::kChromeUIScheme)) {
+    blink::WebRuntimeFeatures::EnableShadowDOMV0(true);
+    blink::WebRuntimeFeatures::EnableCustomElementsV0(true);
+    blink::WebRuntimeFeatures::EnableHTMLImports(true);
+  }
+
   SetupLoaderFactoryBundle(std::move(subresource_loader_factories),
                            std::move(subresource_overrides),
                            std::move(prefetch_loader_factory));
diff --git a/content/renderer/render_process_impl.cc b/content/renderer/render_process_impl.cc
index f1bfead6..f71fc7c 100644
--- a/content/renderer/render_process_impl.cc
+++ b/content/renderer/render_process_impl.cc
@@ -64,25 +64,18 @@
 
 std::unique_ptr<base::TaskScheduler::InitParams>
 GetDefaultTaskSchedulerInitParams() {
-
-  constexpr int kMaxNumThreadsInBackgroundPool = 1;
-  constexpr int kMaxNumThreadsInBackgroundBlockingPool = 1;
-  constexpr int kMaxNumThreadsInForegroundPoolLowerBound = 2;
-  constexpr int kMaxNumThreadsInForegroundBlockingPool = 1;
+  constexpr int kMaxNumThreadsInBackgroundPool = 2;
+  constexpr int kMaxNumThreadsInForegroundPoolLowerBound = 3;
   constexpr auto kSuggestedReclaimTime = base::TimeDelta::FromSeconds(30);
 
   return std::make_unique<base::TaskScheduler::InitParams>(
       base::SchedulerWorkerPoolParams(kMaxNumThreadsInBackgroundPool,
                                       kSuggestedReclaimTime),
-      base::SchedulerWorkerPoolParams(kMaxNumThreadsInBackgroundBlockingPool,
-                                      kSuggestedReclaimTime),
       base::SchedulerWorkerPoolParams(
           std::max(
               kMaxNumThreadsInForegroundPoolLowerBound,
               content::GetMinThreadsInRendererTaskSchedulerForegroundPool()),
-          kSuggestedReclaimTime),
-      base::SchedulerWorkerPoolParams(kMaxNumThreadsInForegroundBlockingPool,
-                                      kSuggestedReclaimTime));
+          kSuggestedReclaimTime));
 }
 
 #if defined(DCHECK_IS_CONFIGURABLE)
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 5174eb1..cd2ae0fe 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -110,10 +110,10 @@
 #include "content/renderer/renderer_blink_platform_impl.h"
 #include "content/renderer/service_worker/embedded_worker_instance_client_impl.h"
 #include "content/renderer/service_worker/service_worker_context_client.h"
-#include "content/renderer/shared_worker/embedded_shared_worker_stub.h"
-#include "content/renderer/shared_worker/shared_worker_factory_impl.h"
 #include "content/renderer/web_database_observer_impl.h"
-#include "content/renderer/worker_thread_registry.h"
+#include "content/renderer/worker/embedded_shared_worker_stub.h"
+#include "content/renderer/worker/shared_worker_factory_impl.h"
+#include "content/renderer/worker/worker_thread_registry.h"
 #include "device/gamepad/public/cpp/gamepads.h"
 #include "gin/public/debug.h"
 #include "gpu/GLES2/gl2extchromium.h"
@@ -149,7 +149,7 @@
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
 #include "third_party/blink/public/platform/web_cache.h"
 #include "third_party/blink/public/platform/web_image_generator.h"
-#include "third_party/blink/public/platform/web_memory_coordinator.h"
+#include "third_party/blink/public/platform/web_memory_pressure_listener.h"
 #include "third_party/blink/public/platform/web_network_state_notifier.h"
 #include "third_party/blink/public/platform/web_runtime_features.h"
 #include "third_party/blink/public/platform/web_string.h"
@@ -1276,8 +1276,9 @@
   return ChildThreadImpl::AllocateSharedMemory(size);
 }
 
-void RenderThreadImpl::RegisterExtension(v8::Extension* extension) {
-  WebScriptController::RegisterExtension(extension);
+void RenderThreadImpl::RegisterExtension(
+    std::unique_ptr<v8::Extension> extension) {
+  WebScriptController::RegisterExtension(std::move(extension));
 }
 
 int RenderThreadImpl::PostTaskToAllWebWorkers(const base::Closure& closure) {
@@ -2162,7 +2163,7 @@
 }
 
 void RenderThreadImpl::SetUpEmbeddedWorkerChannelForServiceWorker(
-    mojom::EmbeddedWorkerInstanceClientRequest client_request) {
+    blink::mojom::EmbeddedWorkerInstanceClientRequest client_request) {
   EmbeddedWorkerInstanceClientImpl::Create(std::move(client_request));
 }
 
@@ -2249,7 +2250,7 @@
     base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
   TRACE_EVENT0("memory", "RenderThreadImpl::OnMemoryPressure");
   if (blink_platform_impl_) {
-    blink::WebMemoryCoordinator::OnMemoryPressure(
+    blink::WebMemoryPressureListener::OnMemoryPressure(
         static_cast<blink::WebMemoryPressureLevel>(memory_pressure_level));
   }
   if (memory_pressure_level ==
@@ -2428,7 +2429,7 @@
   if (blink_platform_impl_) {
     // Purge Skia font cache, resource cache, and image filter.
     SkGraphics::PurgeAllCaches();
-    blink::WebMemoryCoordinator::OnPurgeMemory();
+    blink::WebMemoryPressureListener::OnPurgeMemory();
   }
 }
 
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 099a26df..8eaa1d61 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -204,7 +204,7 @@
       ResourceDispatcherDelegate* delegate) override;
   std::unique_ptr<base::SharedMemory> HostAllocateSharedMemoryBuffer(
       size_t buffer_size) override;
-  void RegisterExtension(v8::Extension* extension) override;
+  void RegisterExtension(std::unique_ptr<v8::Extension> extension) override;
   int PostTaskToAllWebWorkers(const base::Closure& closure) override;
   bool ResolveProxy(const GURL& url, std::string* proxy_list) override;
   base::WaitableEvent* GetShutdownEvent() override;
@@ -539,7 +539,8 @@
       const FrameReplicationState& replicated_state,
       const base::UnguessableToken& devtools_frame_token) override;
   void SetUpEmbeddedWorkerChannelForServiceWorker(
-      mojom::EmbeddedWorkerInstanceClientRequest client_request) override;
+      blink::mojom::EmbeddedWorkerInstanceClientRequest client_request)
+      override;
   void OnNetworkConnectionChanged(
       net::NetworkChangeNotifier::ConnectionType type,
       double max_bandwidth_mbps) override;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 234f5d0..01ef8d5 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -1839,20 +1839,12 @@
   // closing. Then we would not have to destroy this so carefully.
   screen_metrics_emulator_.reset();
 
-  // Informs the WebWidget that compositor is being destroyed, so it can remove
-  // references to it first.
-  //
   // When delegate() is present, the RenderWidget is for a main frame,
-  // and the GetWebWidget() is not the same as |webwidget_internal_|. However
-  // that widget is responsible for doing WillCloseLayerTreeView() on the
-  // |webwidget_internal_|, not us. Otherwise, they are the same and this is
-  // notifying |webwidget_internal_|.
-  GetWebWidget()->WillCloseLayerTreeView();
-
-  // While the wrapping WebWidget from an delegate() is responsible for
-  // doing WillCloseLayerTreeView() on the |webwidget_internal_|, this class is
-  // responsible for calling Close() on it. Notably, then, the wrapping
-  // WebWidget does not.
+  // and the GetWebWidget() will be a WebFrameWidget, which is not the same as
+  // |webwidget_internal_|. The WebFrameWidget will be closed when the main
+  // frame is detached, so we do not close it here. But it does not close the
+  // |webwidget_internal_| since this class takes responsibility for that here
+  // in all cases.
   webwidget_internal_->Close();
   webwidget_internal_ = nullptr;
 }
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index d69362ff..25f8b59 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -66,7 +66,7 @@
 #include "content/renderer/storage_util.h"
 #include "content/renderer/web_database_observer_impl.h"
 #include "content/renderer/webgraphicscontext3d_provider_impl.h"
-#include "content/renderer/worker_thread_registry.h"
+#include "content/renderer/worker/worker_thread_registry.h"
 #include "device/gamepad/public/cpp/gamepads.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
 #include "gpu/config/gpu_info.h"
diff --git a/content/renderer/renderer_main.cc b/content/renderer/renderer_main.cc
index 2fe53ee7..dc3f572 100644
--- a/content/renderer/renderer_main.cc
+++ b/content/renderer/renderer_main.cc
@@ -9,9 +9,7 @@
 #include "base/command_line.h"
 #include "base/debug/debugger.h"
 #include "base/debug/leak_annotations.h"
-#include "base/feature_list.h"
 #include "base/i18n/rtl.h"
-#include "base/message_loop/message_loop.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/pending_task.h"
 #include "base/run_loop.h"
@@ -68,9 +66,6 @@
 namespace content {
 namespace {
 
-const base::Feature kMainThreadUsesSequenceManager{
-    "BlinkMainThreadUsesSequenceManager", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // This function provides some ways to test crash and assertion handling
 // behavior of the renderer.
 static void HandleRendererErrorTestParameters(
@@ -163,19 +158,9 @@
     }
   }
 
-  std::unique_ptr<base::MessageLoop> main_message_loop;
-  std::unique_ptr<blink::scheduler::WebThreadScheduler> main_thread_scheduler;
-  if (!base::FeatureList::IsEnabled(kMainThreadUsesSequenceManager)) {
-    main_message_loop =
-        std::make_unique<base::MessageLoop>(CreateMainThreadMessagePump());
-    main_thread_scheduler =
-        blink::scheduler::WebThreadScheduler::CreateMainThreadScheduler(
-            /*message_pump=*/nullptr, initial_virtual_time);
-  } else {
-    main_thread_scheduler =
-        blink::scheduler::WebThreadScheduler::CreateMainThreadScheduler(
-            CreateMainThreadMessagePump(), initial_virtual_time);
-  }
+  std::unique_ptr<blink::scheduler::WebThreadScheduler> main_thread_scheduler =
+      blink::scheduler::WebThreadScheduler::CreateMainThreadScheduler(
+          CreateMainThreadMessagePump(), initial_virtual_time);
 
   platform.PlatformInitialize();
 
diff --git a/content/renderer/service_worker/controller_service_worker_impl.cc b/content/renderer/service_worker/controller_service_worker_impl.cc
index 2f7498c8..e674490 100644
--- a/content/renderer/service_worker/controller_service_worker_impl.cc
+++ b/content/renderer/service_worker/controller_service_worker_impl.cc
@@ -28,7 +28,7 @@
     blink::mojom::DispatchFetchEventParamsPtr params,
     blink::mojom::ServiceWorkerFetchResponseCallbackPtr response_callback,
     DispatchFetchEventCallback callback) {
-  DCHECK(context_client_);
+  CHECK(context_client_);
   context_client_->DispatchOrQueueFetchEvent(
       std::move(params), std::move(response_callback), std::move(callback));
 }
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
index 06bc6bf..f508156 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.cc
@@ -28,7 +28,7 @@
 
 // static
 void EmbeddedWorkerInstanceClientImpl::Create(
-    mojom::EmbeddedWorkerInstanceClientRequest request) {
+    blink::mojom::EmbeddedWorkerInstanceClientRequest request) {
   // This won't be leaked because the lifetime will be managed internally.
   // See the class documentation for detail.
   // We can't use MakeStrongBinding because must give the worker thread
@@ -45,12 +45,12 @@
 }
 
 void EmbeddedWorkerInstanceClientImpl::StartWorker(
-    mojom::EmbeddedWorkerStartParamsPtr params) {
+    blink::mojom::EmbeddedWorkerStartParamsPtr params) {
   DCHECK(ChildThreadImpl::current());
   DCHECK(!worker_);
   TRACE_EVENT0("ServiceWorker",
                "EmbeddedWorkerInstanceClientImpl::StartWorker");
-  auto start_timing = mojom::EmbeddedWorkerStartTiming::New();
+  auto start_timing = blink::mojom::EmbeddedWorkerStartTiming::New();
   start_timing->start_worker_received_time = base::TimeTicks::Now();
   DCHECK(!params->provider_info->cache_storage ||
          base::FeatureList::IsEnabled(
@@ -117,7 +117,7 @@
 }
 
 EmbeddedWorkerInstanceClientImpl::EmbeddedWorkerInstanceClientImpl(
-    mojom::EmbeddedWorkerInstanceClientRequest request)
+    blink::mojom::EmbeddedWorkerInstanceClientRequest request)
     : binding_(this, std::move(request)) {
   binding_.set_connection_error_handler(base::BindOnce(
       &EmbeddedWorkerInstanceClientImpl::OnError, base::Unretained(this)));
@@ -140,7 +140,7 @@
 
 std::unique_ptr<blink::WebEmbeddedWorker>
 EmbeddedWorkerInstanceClientImpl::StartWorkerContext(
-    mojom::EmbeddedWorkerStartParamsPtr params,
+    blink::mojom::EmbeddedWorkerStartParamsPtr params,
     std::unique_ptr<ServiceWorkerContextClient> context_client,
     blink::mojom::CacheStoragePtrInfo cache_storage,
     service_manager::mojom::InterfaceProviderPtrInfo interface_provider,
diff --git a/content/renderer/service_worker/embedded_worker_instance_client_impl.h b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
index 3304d28..6e2fe8a 100644
--- a/content/renderer/service_worker/embedded_worker_instance_client_impl.h
+++ b/content/renderer/service_worker/embedded_worker_instance_client_impl.h
@@ -9,9 +9,9 @@
 
 #include "content/child/child_thread_impl.h"
 #include "content/common/content_export.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "third_party/blink/public/common/privacy_preferences.h"
+#include "third_party/blink/public/mojom/service_worker/embedded_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom.h"
 #include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom.h"
 
@@ -33,7 +33,7 @@
 //
 // All methods are called on the main thread.
 class CONTENT_EXPORT EmbeddedWorkerInstanceClientImpl
-    : public mojom::EmbeddedWorkerInstanceClient {
+    : public blink::mojom::EmbeddedWorkerInstanceClient {
  public:
   // Enum for UMA to record when StartWorker is received.
   enum class StartWorkerHistogramEnum {
@@ -47,25 +47,25 @@
   // documentation.
   // TODO(shimazu): Create a service worker's execution context by this method
   // instead of just creating an instance of EmbeddedWorkerInstanceClient.
-  static void Create(mojom::EmbeddedWorkerInstanceClientRequest request);
+  static void Create(blink::mojom::EmbeddedWorkerInstanceClientRequest request);
 
   ~EmbeddedWorkerInstanceClientImpl() override;
 
   // Destroys |this|. Called from ServiceWorkerContextClient.
   void WorkerContextDestroyed();
 
-  // mojom::EmbeddedWorkerInstanceClient implementation (partially exposed to
-  // public)
+  // blink::mojom::EmbeddedWorkerInstanceClient implementation (partially
+  // exposed to public)
   void StopWorker() override;
 
  private:
   friend class ServiceWorkerContextClientTest;
 
   explicit EmbeddedWorkerInstanceClientImpl(
-      mojom::EmbeddedWorkerInstanceClientRequest request);
+      blink::mojom::EmbeddedWorkerInstanceClientRequest request);
 
-  // mojom::EmbeddedWorkerInstanceClient implementation
-  void StartWorker(mojom::EmbeddedWorkerStartParamsPtr params) override;
+  // blink::mojom::EmbeddedWorkerInstanceClient implementation
+  void StartWorker(blink::mojom::EmbeddedWorkerStartParamsPtr params) override;
   void ResumeAfterDownload() override;
   void AddMessageToConsole(blink::mojom::ConsoleMessageLevel level,
                            const std::string& message) override;
@@ -77,13 +77,13 @@
   void OnError();
 
   std::unique_ptr<blink::WebEmbeddedWorker> StartWorkerContext(
-      mojom::EmbeddedWorkerStartParamsPtr params,
+      blink::mojom::EmbeddedWorkerStartParamsPtr params,
       std::unique_ptr<ServiceWorkerContextClient> context_client,
       blink::mojom::CacheStoragePtrInfo cache_storage,
       service_manager::mojom::InterfaceProviderPtrInfo interface_provider,
       blink::PrivacyPreferences privacy_preferences);
 
-  mojo::Binding<mojom::EmbeddedWorkerInstanceClient> binding_;
+  mojo::Binding<blink::mojom::EmbeddedWorkerInstanceClient> binding_;
 
   // nullptr means the worker is not running.
   std::unique_ptr<blink::WebEmbeddedWorker> worker_;
diff --git a/content/renderer/service_worker/navigation_preload_request.cc b/content/renderer/service_worker/navigation_preload_request.cc
index 01373aa8b..2e48071e 100644
--- a/content/renderer/service_worker/navigation_preload_request.cc
+++ b/content/renderer/service_worker/navigation_preload_request.cc
@@ -15,10 +15,12 @@
 namespace content {
 
 NavigationPreloadRequest::NavigationPreloadRequest(
+    base::WeakPtr<ServiceWorkerContextClient> owner,
     int fetch_event_id,
     const GURL& url,
     blink::mojom::FetchEventPreloadHandlePtr preload_handle)
-    : fetch_event_id_(fetch_event_id),
+    : owner_(std::move(owner)),
+      fetch_event_id_(fetch_event_id),
       url_(url),
       url_loader_(std::move(preload_handle->url_loader)),
       binding_(this, std::move(preload_handle->url_loader_client_request)) {}
@@ -34,7 +36,7 @@
   WebURLLoaderImpl::PopulateURLResponse(url_, response_head, response_.get(),
                                         report_security_info,
                                         -1 /* request_id */);
-  MaybeReportResponseToClient();
+  MaybeReportResponseToOwner();
 }
 
 void NavigationPreloadRequest::OnReceiveRedirect(
@@ -44,18 +46,15 @@
   DCHECK(net::HttpResponseHeaders::IsRedirectResponseCode(
       response_head.headers->response_code()));
 
-  ServiceWorkerContextClient* client =
-      ServiceWorkerContextClient::ThreadSpecificInstance();
-  if (!client)
-    return;
+  CHECK(owner_);
   response_ = std::make_unique<blink::WebURLResponse>();
   WebURLLoaderImpl::PopulateURLResponse(url_, response_head, response_.get(),
                                         false /* report_security_info */,
                                         -1 /* request_id */);
-  client->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
+  owner_->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
                                       mojo::ScopedDataPipeConsumerHandle());
   // This will delete |this|.
-  client->OnNavigationPreloadComplete(
+  owner_->OnNavigationPreloadComplete(
       fetch_event_id_, response_head.response_start,
       response_head.encoded_data_length, 0 /* encoded_body_length */,
       0 /* decoded_body_length */);
@@ -78,7 +77,7 @@
     mojo::ScopedDataPipeConsumerHandle body) {
   DCHECK(!body_.is_valid());
   body_ = std::move(body);
-  MaybeReportResponseToClient();
+  MaybeReportResponseToOwner();
 }
 
 void NavigationPreloadRequest::OnComplete(
@@ -103,48 +102,38 @@
     }
 
     // This will delete |this|.
-    ReportErrorToClient(message, unsanitized_message);
+    ReportErrorToOwner(message, unsanitized_message);
     return;
   }
 
-  ServiceWorkerContextClient* client =
-      ServiceWorkerContextClient::ThreadSpecificInstance();
-  if (!client)
-    return;
+  CHECK(owner_);
   if (response_) {
     // When the response body from the server is empty, OnComplete() is called
     // without OnStartLoadingResponseBody().
     DCHECK(!body_.is_valid());
-    client->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
+    owner_->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
                                         mojo::ScopedDataPipeConsumerHandle());
   }
   // This will delete |this|.
-  client->OnNavigationPreloadComplete(
+  owner_->OnNavigationPreloadComplete(
       fetch_event_id_, status.completion_time, status.encoded_data_length,
       status.encoded_body_length, status.decoded_body_length);
 }
 
-void NavigationPreloadRequest::MaybeReportResponseToClient() {
+void NavigationPreloadRequest::MaybeReportResponseToOwner() {
   if (!response_ || !body_.is_valid())
     return;
-  ServiceWorkerContextClient* client =
-      ServiceWorkerContextClient::ThreadSpecificInstance();
-  if (!client)
-    return;
-
-  client->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
+  CHECK(owner_);
+  owner_->OnNavigationPreloadResponse(fetch_event_id_, std::move(response_),
                                       std::move(body_));
 }
 
-void NavigationPreloadRequest::ReportErrorToClient(
+void NavigationPreloadRequest::ReportErrorToOwner(
     const std::string& message,
     const std::string& unsanitized_message) {
-  ServiceWorkerContextClient* client =
-      ServiceWorkerContextClient::ThreadSpecificInstance();
-  if (!client)
-    return;
+  CHECK(owner_);
   // This will delete |this|.
-  client->OnNavigationPreloadError(
+  owner_->OnNavigationPreloadError(
       fetch_event_id_, std::make_unique<blink::WebServiceWorkerError>(
                            blink::mojom::ServiceWorkerErrorType::kNetwork,
                            blink::WebString::FromUTF8(message),
diff --git a/content/renderer/service_worker/navigation_preload_request.h b/content/renderer/service_worker/navigation_preload_request.h
index b662b0de..98dd5c6 100644
--- a/content/renderer/service_worker/navigation_preload_request.h
+++ b/content/renderer/service_worker/navigation_preload_request.h
@@ -9,6 +9,7 @@
 #include <string>
 #include <vector>
 
+#include "base/memory/weak_ptr.h"
 #include "mojo/public/cpp/bindings/binding.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "services/network/public/mojom/url_loader.mojom.h"
@@ -25,7 +26,9 @@
 // ServiceWorkerContextClient.
 class NavigationPreloadRequest final : public network::mojom::URLLoaderClient {
  public:
+  // |owner| must outlive |this|.
   NavigationPreloadRequest(
+      base::WeakPtr<ServiceWorkerContextClient> owner,
       int fetch_event_id,
       const GURL& url,
       blink::mojom::FetchEventPreloadHandlePtr preload_handle);
@@ -47,9 +50,14 @@
   void OnComplete(const network::URLLoaderCompletionStatus& status) override;
 
  private:
-  void MaybeReportResponseToClient();
-  void ReportErrorToClient(const std::string& message,
-                           const std::string& unsanitized_message);
+  void MaybeReportResponseToOwner();
+  void ReportErrorToOwner(const std::string& message,
+                          const std::string& unsanitized_message);
+
+  // TODO(crbug.com/907311): This is just a WeakPtr to do a CHECK that the owner
+  // really outlives this, as we've been getting related crashes. Change this to
+  // a raw pointer when the bug is fixed.
+  base::WeakPtr<ServiceWorkerContextClient> owner_;
 
   const int fetch_event_id_;
   const GURL url_;
diff --git a/content/renderer/service_worker/service_worker_context_client.cc b/content/renderer/service_worker/service_worker_context_client.cc
index bd8df1b..2ed05ab 100644
--- a/content/renderer/service_worker/service_worker_context_client.cc
+++ b/content/renderer/service_worker/service_worker_context_client.cc
@@ -13,13 +13,11 @@
 #include "base/bind_helpers.h"
 #include "base/debug/alias.h"
 #include "base/feature_list.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/single_thread_task_runner.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/threading/thread_checker.h"
-#include "base/threading/thread_local.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/time.h"
 #include "base/trace_event/trace_event.h"
@@ -35,7 +33,6 @@
 #include "content/renderer/loader/web_data_consumer_handle_impl.h"
 #include "content/renderer/loader/web_url_loader_impl.h"
 #include "content/renderer/loader/web_url_request_util.h"
-#include "content/renderer/notifications/notification_data_conversions.h"
 #include "content/renderer/render_thread_impl.h"
 #include "content/renderer/renderer_blink_platform_impl.h"
 #include "content/renderer/service_worker/controller_service_worker_impl.h"
@@ -71,6 +68,7 @@
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_error.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_request.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_response.h"
+#include "third_party/blink/public/platform/notification_data_conversions.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/url_conversion.h"
 #include "third_party/blink/public/platform/web_blob_registry.h"
@@ -92,10 +90,6 @@
 constexpr char kServiceWorkerContextClientScope[] =
     "ServiceWorkerContextClient";
 
-// For now client must be a per-thread instance.
-base::LazyInstance<base::ThreadLocalPointer<ServiceWorkerContextClient>>::
-    Leaky g_worker_client_tls = LAZY_INSTANCE_INITIALIZER;
-
 class StreamHandleListener
     : public blink::WebServiceWorkerStreamHandle::Listener {
  public:
@@ -210,7 +204,7 @@
 
 }  // namespace
 
-// Holding data that needs to be bound to the worker context on the
+// Holds data that needs to be bound to the worker context on the
 // worker thread.
 struct ServiceWorkerContextClient::WorkerContextData {
   explicit WorkerContextData(ServiceWorkerContextClient* owner)
@@ -218,9 +212,7 @@
         weak_factory(owner),
         proxy_weak_factory(owner->proxy_) {}
 
-  ~WorkerContextData() {
-    DCHECK(thread_checker.CalledOnValidThread());
-  }
+  ~WorkerContextData() { CHECK(thread_checker.CalledOnValidThread()); }
 
   mojo::Binding<blink::mojom::ServiceWorker> service_worker_binding;
 
@@ -286,11 +278,6 @@
   base::WeakPtrFactory<blink::WebServiceWorkerContextProxy> proxy_weak_factory;
 };
 
-ServiceWorkerContextClient*
-ServiceWorkerContextClient::ThreadSpecificInstance() {
-  return g_worker_client_tls.Pointer()->Get();
-}
-
 ServiceWorkerContextClient::ServiceWorkerContextClient(
     int64_t service_worker_version_id,
     const GURL& service_worker_scope,
@@ -299,10 +286,10 @@
     blink::mojom::RendererPreferencesPtr renderer_preferences,
     blink::mojom::ServiceWorkerRequest service_worker_request,
     blink::mojom::ControllerServiceWorkerRequest controller_request,
-    mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+    blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
     blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
     EmbeddedWorkerInstanceClientImpl* owner,
-    mojom::EmbeddedWorkerStartTimingPtr start_timing,
+    blink::mojom::EmbeddedWorkerStartTimingPtr start_timing,
     blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
     std::unique_ptr<blink::URLLoaderFactoryBundleInfo> subresource_loaders,
     scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner)
@@ -321,7 +308,7 @@
   DCHECK(main_thread_task_runner_->RunsTasksInCurrentSequence());
   DCHECK(owner_);
   instance_host_ =
-      mojom::ThreadSafeEmbeddedWorkerInstanceHostAssociatedPtr::Create(
+      blink::mojom::ThreadSafeEmbeddedWorkerInstanceHostAssociatedPtr::Create(
           std::move(instance_host), main_thread_task_runner_);
 
   if (subresource_loaders) {
@@ -436,17 +423,13 @@
   DCHECK_NE(0, WorkerThread::GetCurrentId());
   RecordDebugLog("WorkerContextStarted");
   worker_task_runner_ = base::ThreadTaskRunnerHandle::Get();
-  // g_worker_client_tls.Pointer()->Get() could return nullptr if this context
-  // gets deleted before workerContextStarted() is called.
-  DCHECK(g_worker_client_tls.Pointer()->Get() == nullptr);
   DCHECK(!proxy_);
-  g_worker_client_tls.Pointer()->Set(this);
   proxy_ = proxy;
 
   // Initialize pending callback maps. This needs to be freed on the
   // same thread before the worker context goes away in
   // willDestroyWorkerContext.
-  context_.reset(new WorkerContextData(this));
+  context_ = std::make_unique<WorkerContextData>(this);
 
   DCHECK(pending_service_worker_request_.is_pending());
   DCHECK(pending_controller_request_.is_pending());
@@ -456,8 +439,12 @@
       std::move(pending_service_worker_request_));
 
   if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
+    auto weak_ptr = GetWeakPtr();
+    // Intentionally dereferences the weak ptr in order to
+    // ensure the WeakPtrFactory is bound to this thread (the worker thread).
+    weak_ptr = weak_ptr->GetWeakPtr();
     context_->controller_impl = std::make_unique<ControllerServiceWorkerImpl>(
-        std::move(pending_controller_request_), GetWeakPtr());
+        std::move(pending_controller_request_), std::move(weak_ptr));
   }
 }
 
@@ -531,17 +518,12 @@
   // same thread.
   context_.reset();
 
-  // This also lets the message filter stop dispatching messages to
-  // this client.
-  g_worker_client_tls.Pointer()->Set(nullptr);
-
   GetContentClient()->renderer()->WillDestroyServiceWorkerContextOnWorkerThread(
       context, service_worker_version_id_, service_worker_scope_, script_url_);
 }
 
 void ServiceWorkerContextClient::WorkerContextDestroyed() {
   DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
-  DCHECK(g_worker_client_tls.Pointer()->Get() == nullptr);
   RecordDebugLog("WorkerContextDestroyed");
 
   (*instance_host_)->OnStopped();
@@ -1613,7 +1595,7 @@
     blink::mojom::FetchEventPreloadHandlePtr preload_handle) {
   DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
   auto preload_request = std::make_unique<NavigationPreloadRequest>(
-      fetch_event_id, url, std::move(preload_handle));
+      GetWeakPtr(), fetch_event_id, url, std::move(preload_handle));
   context_->preload_requests.AddWithID(std::move(preload_request),
                                        fetch_event_id);
 }
@@ -1659,16 +1641,11 @@
 
 base::WeakPtr<ServiceWorkerContextClient>
 ServiceWorkerContextClient::GetWeakPtr() {
-  DCHECK(worker_task_runner_->RunsTasksInCurrentSequence());
-  DCHECK(context_);
+  CHECK(worker_task_runner_->RunsTasksInCurrentSequence());
+  CHECK(context_);
   return context_->weak_factory.GetWeakPtr();
 }
 
-// static
-void ServiceWorkerContextClient::ResetThreadSpecificInstanceForTesting() {
-  g_worker_client_tls.Pointer()->Set(nullptr);
-}
-
 void ServiceWorkerContextClient::SetTimeoutTimerForTesting(
     std::unique_ptr<ServiceWorkerTimeoutTimer> timeout_timer) {
   DCHECK(context_);
diff --git a/content/renderer/service_worker/service_worker_context_client.h b/content/renderer/service_worker/service_worker_context_client.h
index 7d1b0c08..61ba66c 100644
--- a/content/renderer/service_worker/service_worker_context_client.h
+++ b/content/renderer/service_worker/service_worker_context_client.h
@@ -20,7 +20,6 @@
 #include "base/strings/string16.h"
 #include "base/synchronization/lock.h"
 #include "base/time/time.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "ipc/ipc_listener.h"
 #include "mojo/public/cpp/bindings/binding.h"
@@ -29,6 +28,7 @@
 #include "third_party/blink/public/mojom/blob/blob_registry.mojom.h"
 #include "third_party/blink/public/mojom/payments/payment_app.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"
 #include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom.h"
@@ -71,10 +71,6 @@
     : public blink::WebServiceWorkerContextClient,
       public blink::mojom::ServiceWorker {
  public:
-  // Returns a thread-specific client instance.  This does NOT create a
-  // new instance.
-  static ServiceWorkerContextClient* ThreadSpecificInstance();
-
   // Called on the main thread.
   // - |is_starting_installed_worker| is true if the script is already installed
   //   and will be streamed from the browser process.
@@ -90,10 +86,10 @@
       blink::mojom::RendererPreferencesPtr renderer_preferences,
       blink::mojom::ServiceWorkerRequest service_worker_request,
       blink::mojom::ControllerServiceWorkerRequest controller_request,
-      mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
+      blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtrInfo instance_host,
       blink::mojom::ServiceWorkerProviderInfoForStartWorkerPtr provider_info,
       EmbeddedWorkerInstanceClientImpl* owner,
-      mojom::EmbeddedWorkerStartTimingPtr start_timing,
+      blink::mojom::EmbeddedWorkerStartTimingPtr start_timing,
       blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
       std::unique_ptr<blink::URLLoaderFactoryBundleInfo> subresource_loaders,
       scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner);
@@ -367,7 +363,6 @@
 
   base::WeakPtr<ServiceWorkerContextClient> GetWeakPtr();
 
-  static void ResetThreadSpecificInstanceForTesting();
   void SetTimeoutTimerForTesting(
       std::unique_ptr<ServiceWorkerTimeoutTimer> timeout_timer);
 
@@ -396,7 +391,7 @@
   blink::mojom::ControllerServiceWorkerRequest pending_controller_request_;
 
   // This is bound on the main thread.
-  scoped_refptr<mojom::ThreadSafeEmbeddedWorkerInstanceHostAssociatedPtr>
+  scoped_refptr<blink::mojom::ThreadSafeEmbeddedWorkerInstanceHostAssociatedPtr>
       instance_host_;
 
   // This holds blink.mojom.ServiceWorkerContainer(Host) connections to the
@@ -418,7 +413,7 @@
 
   // Accessed on the worker thread. Passed to the browser process after worker
   // startup completes.
-  mojom::EmbeddedWorkerStartTimingPtr start_timing_;
+  blink::mojom::EmbeddedWorkerStartTimingPtr start_timing_;
 
   // S13nServiceWorker:
   // A URLLoaderFactory instance used for subresource loading.
diff --git a/content/renderer/service_worker/service_worker_context_client_unittest.cc b/content/renderer/service_worker/service_worker_context_client_unittest.cc
index 25a53acd..f8c28e54 100644
--- a/content/renderer/service_worker/service_worker_context_client_unittest.cc
+++ b/content/renderer/service_worker/service_worker_context_client_unittest.cc
@@ -15,14 +15,13 @@
 #include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "content/child/thread_safe_sender.h"
-#include "content/common/service_worker/embedded_worker.mojom.h"
 #include "content/common/service_worker/service_worker_types.h"
 #include "content/public/common/content_client.h"
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/renderer/service_worker/embedded_worker_instance_client_impl.h"
 #include "content/renderer/service_worker/service_worker_timeout_timer.h"
 #include "content/renderer/service_worker/service_worker_type_util.h"
-#include "content/renderer/worker_thread_registry.h"
+#include "content/renderer/worker/worker_thread_registry.h"
 #include "mojo/public/cpp/bindings/associated_binding_set.h"
 #include "mojo/public/cpp/bindings/associated_interface_ptr.h"
 #include "services/network/public/cpp/features.h"
@@ -32,6 +31,7 @@
 #include "third_party/blink/public/common/messaging/message_port_channel.h"
 #include "third_party/blink/public/common/service_worker/service_worker_utils.h"
 #include "third_party/blink/public/mojom/background_fetch/background_fetch.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/embedded_worker.mojom.h"
 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
 #include "third_party/blink/public/platform/modules/notifications/web_notification_data.h"
 #include "third_party/blink/public/platform/modules/payments/web_payment_request_event_data.h"
@@ -53,7 +53,7 @@
 // Pipes connected to the context client.
 struct ContextClientPipes {
   // From the browser to EmbeddedWorkerInstanceClientImpl.
-  mojom::EmbeddedWorkerInstanceClientPtr embedded_worker_instance_client;
+  blink::mojom::EmbeddedWorkerInstanceClientPtr embedded_worker_instance_client;
 
   // From the browser to ServiceWorkerContextClient.
   blink::mojom::ServiceWorkerPtr service_worker;
@@ -62,7 +62,7 @@
 
   // From ServiceWorkerContextClient to the browser.
   blink::mojom::ServiceWorkerHostAssociatedRequest service_worker_host_request;
-  mojom::EmbeddedWorkerInstanceHostAssociatedRequest
+  blink::mojom::EmbeddedWorkerInstanceHostAssociatedRequest
       embedded_worker_host_request;
   blink::mojom::ServiceWorkerRegistrationObjectHostAssociatedRequest
       registration_host_request;
@@ -272,7 +272,6 @@
   }
 
   void TearDown() override {
-    ServiceWorkerContextClient::ResetThreadSpecificInstanceForTesting();
     // Unregister this thread from worker threads.
     WorkerThreadRegistry::Instance()->WillStopCurrentWorkerThread();
     task_runner_->RunUntilIdle();
@@ -306,7 +305,8 @@
 
     auto service_worker_request = mojo::MakeRequest(&out_pipes->service_worker);
     auto controller_request = mojo::MakeRequest(&out_pipes->controller);
-    mojom::EmbeddedWorkerInstanceHostAssociatedPtr embedded_worker_host_ptr;
+    blink::mojom::EmbeddedWorkerInstanceHostAssociatedPtr
+        embedded_worker_host_ptr;
     out_pipes->embedded_worker_host_request =
         mojo::MakeRequestAssociatedWithDedicatedPipe(&embedded_worker_host_ptr);
     const GURL kScope("https://example.com");
@@ -318,7 +318,7 @@
         std::move(service_worker_request), std::move(controller_request),
         embedded_worker_host_ptr.PassInterface(), CreateProviderInfo(),
         embedded_worker_instance_client,
-        mojom::EmbeddedWorkerStartTiming::New(),
+        blink::mojom::EmbeddedWorkerStartTiming::New(),
         nullptr /* preference_watcher_request */,
         nullptr /* subresource_loaders */,
         blink::scheduler::GetSingleThreadTaskRunnerForTesting());
diff --git a/content/renderer/service_worker/service_worker_network_provider_for_service_worker.cc b/content/renderer/service_worker/service_worker_network_provider_for_service_worker.cc
index 89e6e44..008f3ee0 100644
--- a/content/renderer/service_worker/service_worker_network_provider_for_service_worker.cc
+++ b/content/renderer/service_worker/service_worker_network_provider_for_service_worker.cc
@@ -58,11 +58,16 @@
     const blink::WebURLRequest& request,
     std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>
         task_runner_handle) {
+  // We only get here for the main script request from the shadow page.
+  // importScripts() and other subresource fetches are handled on the worker
+  // thread by ServiceWorkerFetchContextImpl.
+  DCHECK_EQ(blink::mojom::RequestContextType::SERVICE_WORKER,
+            request.GetRequestContext());
+
   RenderThreadImpl* render_thread = RenderThreadImpl::current();
   // RenderThreadImpl may be null in some tests.
   if (render_thread && script_loader_factory() &&
-      blink::ServiceWorkerUtils::IsServicificationEnabled() &&
-      IsScriptRequest(request)) {
+      blink::ServiceWorkerUtils::IsServicificationEnabled()) {
     // TODO(crbug.com/796425): Temporarily wrap the raw
     // mojom::URLLoaderFactory pointer into SharedURLLoaderFactory.
     return std::make_unique<WebURLLoaderImpl>(
@@ -73,17 +78,4 @@
   return nullptr;
 }
 
-// TODO(falken): This class is only used for the shadow page so import
-// scripts shouldn't come here. Change the callsite to be a DCHECK that only
-// SERVICE_WORKER is used.
-//
-// static
-bool ServiceWorkerNetworkProviderForServiceWorker::IsScriptRequest(
-    const blink::WebURLRequest& request) {
-  auto request_context = request.GetRequestContext();
-  return request_context == blink::mojom::RequestContextType::SERVICE_WORKER ||
-         request_context == blink::mojom::RequestContextType::SCRIPT ||
-         request_context == blink::mojom::RequestContextType::IMPORT;
-}
-
 }  // namespace content
diff --git a/content/renderer/service_worker/service_worker_network_provider_for_service_worker.h b/content/renderer/service_worker/service_worker_network_provider_for_service_worker.h
index 86f1f22..1aa9eea 100644
--- a/content/renderer/service_worker/service_worker_network_provider_for_service_worker.h
+++ b/content/renderer/service_worker/service_worker_network_provider_for_service_worker.h
@@ -39,8 +39,6 @@
   }
 
  private:
-  static bool IsScriptRequest(const blink::WebURLRequest& request);
-
   const int provider_id_;
   // The URL loader factory for loading the service worker's scripts.
   network::mojom::URLLoaderFactoryAssociatedPtr script_loader_factory_;
diff --git a/content/renderer/service_worker/service_worker_provider_context.cc b/content/renderer/service_worker/service_worker_provider_context.cc
index c965488..6f166df 100644
--- a/content/renderer/service_worker/service_worker_provider_context.cc
+++ b/content/renderer/service_worker/service_worker_provider_context.cc
@@ -20,7 +20,7 @@
 #include "content/public/common/service_names.mojom.h"
 #include "content/renderer/service_worker/controller_service_worker_connector.h"
 #include "content/renderer/service_worker/service_worker_subresource_loader.h"
-#include "content/renderer/worker_thread_registry.h"
+#include "content/renderer/worker/worker_thread_registry.h"
 #include "mojo/public/cpp/bindings/strong_associated_binding.h"
 #include "mojo/public/cpp/bindings/strong_binding.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
diff --git a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
index 05341526..40e5a6ab 100644
--- a/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
+++ b/content/renderer/service_worker/service_worker_subresource_loader_unittest.cc
@@ -1255,7 +1255,7 @@
   int count = 1;
   std::string redirect_location =
       std::string("https://www.example.com/redirect_") +
-      base::IntToString(count);
+      base::NumberToString(count);
   fake_controller_.RespondWithRedirect(redirect_location);
   network::mojom::URLLoaderFactoryPtr factory =
       CreateSubresourceLoaderFactory();
@@ -1286,7 +1286,7 @@
 
     // Redirect more.
     redirect_location = std::string("https://www.example.com/redirect_") +
-                        base::IntToString(count);
+                        base::NumberToString(count);
     fake_controller_.RespondWithRedirect(redirect_location);
     loader->FollowRedirect({}, {}, base::nullopt);
   }
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.cc b/content/renderer/shared_worker/embedded_shared_worker_stub.cc
deleted file mode 100644
index d65555e..0000000
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.cc
+++ /dev/null
@@ -1,340 +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 "content/renderer/shared_worker/embedded_shared_worker_stub.h"
-
-#include <stdint.h>
-#include <utility>
-
-#include "base/bind.h"
-#include "base/feature_list.h"
-#include "base/single_thread_task_runner.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "content/common/possibly_associated_wrapper_shared_url_loader_factory.h"
-#include "content/public/common/content_features.h"
-#include "content/public/common/content_switches.h"
-#include "content/public/common/network_service_util.h"
-#include "content/public/common/origin_util.h"
-#include "content/renderer/appcache/appcache_frontend_impl.h"
-#include "content/renderer/appcache/web_application_cache_host_impl.h"
-#include "content/renderer/loader/child_url_loader_factory_bundle.h"
-#include "content/renderer/loader/navigation_response_override_parameters.h"
-#include "content/renderer/loader/tracked_child_url_loader_factory_bundle.h"
-#include "content/renderer/loader/web_worker_fetch_context_impl.h"
-#include "content/renderer/render_thread_impl.h"
-#include "content/renderer/renderer_blink_platform_impl.h"
-#include "content/renderer/service_worker/service_worker_provider_context.h"
-#include "content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h"
-#include "ipc/ipc_message_macros.h"
-#include "services/network/public/cpp/features.h"
-#include "services/network/public/cpp/shared_url_loader_factory.h"
-#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
-#include "third_party/blink/public/common/messaging/message_port_channel.h"
-#include "third_party/blink/public/common/privacy_preferences.h"
-#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
-#include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
-#include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
-#include "third_party/blink/public/platform/interface_provider.h"
-#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
-#include "third_party/blink/public/platform/task_type.h"
-#include "third_party/blink/public/platform/url_conversion.h"
-#include "third_party/blink/public/platform/web_security_origin.h"
-#include "third_party/blink/public/web/web_shared_worker.h"
-#include "third_party/blink/public/web/web_shared_worker_client.h"
-#include "url/origin.h"
-
-namespace content {
-
-namespace {
-
-class SharedWorkerWebApplicationCacheHostImpl
-    : public WebApplicationCacheHostImpl {
- public:
-  SharedWorkerWebApplicationCacheHostImpl(
-      blink::WebApplicationCacheHostClient* client,
-      int appcache_host_id)
-      : WebApplicationCacheHostImpl(client,
-                                    RenderThreadImpl::current()
-                                        ->appcache_frontend_impl()
-                                        ->backend_proxy(),
-                                    appcache_host_id) {}
-
-  // Main resource loading is different for workers. The main resource is
-  // loaded by the worker using WorkerClassicScriptLoader.
-  // These overrides are stubbed out.
-  void WillStartMainResourceRequest(
-      const blink::WebURL& url,
-      const blink::WebString& method,
-      const WebApplicationCacheHost* spawning_host) override {}
-  void DidReceiveResponseForMainResource(
-      const blink::WebURLResponse&) override {}
-
-  // Cache selection is also different for workers. We know at construction
-  // time what cache to select and do so then.
-  // These overrides are stubbed out.
-  void SelectCacheWithoutManifest() override {}
-  bool SelectCacheWithManifest(const blink::WebURL& manifestURL) override {
-    return true;
-  }
-};
-
-}  // namespace
-
-EmbeddedSharedWorkerStub::EmbeddedSharedWorkerStub(
-    blink::mojom::SharedWorkerInfoPtr info,
-    bool pause_on_start,
-    const base::UnguessableToken& devtools_worker_token,
-    const blink::mojom::RendererPreferences& renderer_preferences,
-    blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
-    blink::mojom::WorkerContentSettingsProxyPtr content_settings,
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
-        service_worker_provider_info,
-    int appcache_host_id,
-    network::mojom::URLLoaderFactoryAssociatedPtrInfo
-        main_script_loader_factory,
-    blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
-    std::unique_ptr<blink::URLLoaderFactoryBundleInfo> factory_bundle,
-    blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
-    blink::mojom::SharedWorkerHostPtr host,
-    blink::mojom::SharedWorkerRequest request,
-    service_manager::mojom::InterfaceProviderPtr interface_provider)
-    : binding_(this, std::move(request)),
-      host_(std::move(host)),
-      name_(info->name),
-      url_(info->url),
-      renderer_preferences_(renderer_preferences),
-      preference_watcher_request_(std::move(preference_watcher_request)),
-      appcache_host_id_(appcache_host_id) {
-  // The ID of the precreated AppCacheHost can be valid only when the
-  // NetworkService is enabled.
-  DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService) ||
-         appcache_host_id == blink::mojom::kAppCacheNoHostId);
-
-  if (main_script_load_params) {
-    response_override_ =
-        std::make_unique<NavigationResponseOverrideParameters>();
-    response_override_->url_loader_client_endpoints =
-        std::move(main_script_load_params->url_loader_client_endpoints);
-    response_override_->response = main_script_load_params->response_head;
-    response_override_->redirect_responses =
-        main_script_load_params->redirect_response_heads;
-    response_override_->redirect_infos =
-        main_script_load_params->redirect_infos;
-  }
-
-  impl_ = blink::WebSharedWorker::Create(this);
-  if (pause_on_start) {
-    // Pause worker context when it starts and wait until either DevTools client
-    // is attached or explicit resume notification is received.
-    impl_->PauseWorkerContextOnStart();
-  }
-
-  service_worker_provider_info_ = std::move(service_worker_provider_info);
-  main_script_loader_factory_ = std::move(main_script_loader_factory);
-  controller_info_ = std::move(controller_info);
-
-  // |factory_bundle| is provided in the
-  // ServiceWorkerServicification or NetworkService case.
-  DCHECK(factory_bundle ||
-         !blink::ServiceWorkerUtils::IsServicificationEnabled());
-
-  // Make the factory bundle.
-  subresource_loader_factories_ =
-      base::MakeRefCounted<HostChildURLLoaderFactoryBundle>(
-          impl_->GetTaskRunner(blink::TaskType::kInternalLoading));
-
-  // If NetworkService or S13nSW is enabled, the default factory must be
-  // given as a |factory_bundle|.
-  // In some tests |render_thread| could be null.
-  RenderThreadImpl* render_thread = RenderThreadImpl::current();
-  if (render_thread && !blink::ServiceWorkerUtils::IsServicificationEnabled()) {
-    subresource_loader_factories_->Update(
-        render_thread->blink_platform_impl()
-            ->CreateDefaultURLLoaderFactoryBundle()
-            ->PassInterface());
-  }
-
-  if (factory_bundle) {
-    // If the network service crashes, then self-destruct so clients don't get
-    // stuck with a worker with a broken loader. Self-destruction is effectively
-    // the same as the worker's process crashing.
-    if (IsOutOfProcessNetworkService()) {
-      default_factory_connection_error_handler_holder_.Bind(
-          std::move(factory_bundle->default_factory_info()));
-      default_factory_connection_error_handler_holder_->Clone(
-          mojo::MakeRequest(&factory_bundle->default_factory_info()));
-      default_factory_connection_error_handler_holder_
-          .set_connection_error_handler(base::BindOnce(
-              &EmbeddedSharedWorkerStub::Terminate, base::Unretained(this)));
-    }
-
-    subresource_loader_factories_->Update(
-        std::make_unique<ChildURLLoaderFactoryBundleInfo>(
-            std::move(factory_bundle)));
-  }
-
-  impl_->StartWorkerContext(
-      url_, blink::WebString::FromUTF8(name_),
-      blink::WebString::FromUTF8(info->content_security_policy),
-      info->content_security_policy_type, info->creation_address_space,
-      devtools_worker_token,
-      blink::PrivacyPreferences(renderer_preferences_.enable_do_not_track,
-                                renderer_preferences_.enable_referrers),
-      subresource_loader_factories_,
-      content_settings.PassInterface().PassHandle(),
-      interface_provider.PassInterface().PassHandle());
-
-  // If the host drops its connection, then self-destruct.
-  binding_.set_connection_error_handler(base::BindOnce(
-      &EmbeddedSharedWorkerStub::Terminate, base::Unretained(this)));
-}
-
-EmbeddedSharedWorkerStub::~EmbeddedSharedWorkerStub() {
-  // Destruction closes our connection to the host, triggering the host to
-  // cleanup and notify clients of this worker going away.
-}
-
-void EmbeddedSharedWorkerStub::WorkerReadyForInspection() {
-  host_->OnReadyForInspection();
-}
-
-void EmbeddedSharedWorkerStub::WorkerScriptLoaded() {
-  host_->OnScriptLoaded();
-}
-
-void EmbeddedSharedWorkerStub::WorkerScriptLoadFailed() {
-  host_->OnScriptLoadFailed();
-  pending_channels_.clear();
-}
-
-void EmbeddedSharedWorkerStub::WorkerScriptEvaluated(bool success) {
-  DCHECK(!running_);
-  running_ = true;
-  // Process any pending connections.
-  for (auto& item : pending_channels_)
-    ConnectToChannel(item.first, std::move(item.second));
-  pending_channels_.clear();
-}
-
-void EmbeddedSharedWorkerStub::CountFeature(blink::mojom::WebFeature feature) {
-  host_->OnFeatureUsed(feature);
-}
-
-void EmbeddedSharedWorkerStub::WorkerContextClosed() {
-  host_->OnContextClosed();
-}
-
-void EmbeddedSharedWorkerStub::WorkerContextDestroyed() {
-  delete this;
-}
-
-void EmbeddedSharedWorkerStub::SelectAppCacheID(long long app_cache_id) {
-  if (app_cache_host_) {
-    // app_cache_host_ could become stale as it's owned by blink's
-    // DocumentLoader. This method is assumed to be called while it's valid.
-    app_cache_host_->backend()->SelectCacheForSharedWorker(
-        app_cache_host_->host_id(), app_cache_id);
-  }
-}
-
-std::unique_ptr<blink::WebApplicationCacheHost>
-EmbeddedSharedWorkerStub::CreateApplicationCacheHost(
-    blink::WebApplicationCacheHostClient* client) {
-  std::unique_ptr<WebApplicationCacheHostImpl> host =
-      std::make_unique<SharedWorkerWebApplicationCacheHostImpl>(
-          client, appcache_host_id_);
-  app_cache_host_ = host.get();
-  return std::move(host);
-}
-
-std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
-EmbeddedSharedWorkerStub::CreateServiceWorkerNetworkProvider() {
-  return WebServiceWorkerNetworkProviderImplForWorker::Create(
-      std::move(service_worker_provider_info_),
-      std::move(main_script_loader_factory_), std::move(controller_info_),
-      subresource_loader_factories_, IsOriginSecure(url_),
-      std::move(response_override_));
-}
-
-void EmbeddedSharedWorkerStub::WaitForServiceWorkerControllerInfo(
-    blink::WebServiceWorkerNetworkProvider* web_network_provider,
-    base::OnceClosure callback) {
-  ServiceWorkerProviderContext* context =
-      static_cast<WebServiceWorkerNetworkProviderImplForWorker*>(
-          web_network_provider)
-          ->context();
-  context->PingContainerHost(std::move(callback));
-}
-
-scoped_refptr<blink::WebWorkerFetchContext>
-EmbeddedSharedWorkerStub::CreateWorkerFetchContext(
-    blink::WebServiceWorkerNetworkProvider* web_network_provider) {
-  DCHECK(web_network_provider);
-  WebServiceWorkerNetworkProviderImplForWorker* network_provider =
-      static_cast<WebServiceWorkerNetworkProviderImplForWorker*>(
-          web_network_provider);
-
-  // Make the factory used for service worker network fallback (that should
-  // skip AppCache if it is provided).
-  std::unique_ptr<network::SharedURLLoaderFactoryInfo> fallback_factory =
-      subresource_loader_factories_->CloneWithoutAppCacheFactory();
-
-  scoped_refptr<WebWorkerFetchContextImpl> worker_fetch_context =
-      WebWorkerFetchContextImpl::Create(
-          network_provider->context(), std::move(renderer_preferences_),
-          std::move(preference_watcher_request_),
-          subresource_loader_factories_->Clone(), std::move(fallback_factory));
-
-  // TODO(horo): To get the correct first_party_to_cookies for the shared
-  // worker, we need to check the all documents bounded by the shared worker.
-  // (crbug.com/723553)
-  // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-07#section-2.1.2
-  worker_fetch_context->set_site_for_cookies(url_);
-  // TODO(horo): Currently we treat the worker context as secure if the origin
-  // of the shared worker script url is secure. But according to the spec, if
-  // the creation context is not secure, we should treat the worker as
-  // non-secure. crbug.com/723575
-  // https://w3c.github.io/webappsec-secure-contexts/#examples-shared-workers
-  worker_fetch_context->set_is_secure_context(IsOriginSecure(url_));
-  worker_fetch_context->set_origin_url(url_.GetOrigin());
-
-  return worker_fetch_context;
-}
-
-void EmbeddedSharedWorkerStub::ConnectToChannel(
-    int connection_request_id,
-    blink::MessagePortChannel channel) {
-  impl_->Connect(std::move(channel));
-  host_->OnConnected(connection_request_id);
-}
-
-void EmbeddedSharedWorkerStub::Connect(int connection_request_id,
-                                       mojo::ScopedMessagePipeHandle port) {
-  blink::MessagePortChannel channel(std::move(port));
-  if (running_) {
-    ConnectToChannel(connection_request_id, std::move(channel));
-  } else {
-    // If two documents try to load a SharedWorker at the same time, the
-    // mojom::SharedWorker::Connect() for one of the documents can come in
-    // before the worker is started. Just queue up the connect and deliver it
-    // once the worker starts.
-    pending_channels_.emplace_back(connection_request_id, std::move(channel));
-  }
-}
-
-void EmbeddedSharedWorkerStub::Terminate() {
-  // After this we should ignore any IPC for this stub.
-  running_ = false;
-  impl_->TerminateWorkerContext();
-}
-
-void EmbeddedSharedWorkerStub::BindDevToolsAgent(
-    blink::mojom::DevToolsAgentHostAssociatedPtrInfo host,
-    blink::mojom::DevToolsAgentAssociatedRequest request) {
-  impl_->BindDevToolsAgent(host.PassHandle(), request.PassHandle());
-}
-
-}  // namespace content
diff --git a/content/renderer/shared_worker/embedded_shared_worker_stub.h b/content/renderer/shared_worker/embedded_shared_worker_stub.h
deleted file mode 100644
index 97eb16f..0000000
--- a/content/renderer/shared_worker/embedded_shared_worker_stub.h
+++ /dev/null
@@ -1,161 +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 CONTENT_RENDERER_SHARED_WORKER_EMBEDDED_SHARED_WORKER_STUB_H_
-#define CONTENT_RENDERER_SHARED_WORKER_EMBEDDED_SHARED_WORKER_STUB_H_
-
-#include <memory>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/unguessable_token.h"
-#include "ipc/ipc_listener.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
-#include "services/service_manager/public/mojom/interface_provider.mojom.h"
-#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
-#include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h"
-#include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
-#include "third_party/blink/public/mojom/worker/shared_worker.mojom.h"
-#include "third_party/blink/public/mojom/worker/shared_worker_host.mojom.h"
-#include "third_party/blink/public/mojom/worker/shared_worker_info.mojom.h"
-#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom.h"
-#include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"
-#include "third_party/blink/public/platform/web_content_security_policy.h"
-#include "third_party/blink/public/platform/web_content_settings_client.h"
-#include "third_party/blink/public/platform/web_string.h"
-#include "third_party/blink/public/web/web_shared_worker_client.h"
-#include "url/gurl.h"
-
-namespace blink {
-class WebApplicationCacheHost;
-class WebApplicationCacheHostClient;
-class WebSharedWorker;
-}
-
-namespace blink {
-class MessagePortChannel;
-class URLLoaderFactoryBundleInfo;
-}
-
-namespace content {
-
-class HostChildURLLoaderFactoryBundle;
-class WebApplicationCacheHostImpl;
-struct NavigationResponseOverrideParameters;
-
-// A stub class to receive IPC from browser process and talk to
-// blink::WebSharedWorker. Implements blink::WebSharedWorkerClient.
-// This class is self-destructed (no one explicitly owns this). It deletes
-// itself when WorkerContextDestroyed() is called by blink::WebSharedWorker.
-//
-// This class owns blink::WebSharedWorker.
-class EmbeddedSharedWorkerStub : public blink::WebSharedWorkerClient,
-                                 public blink::mojom::SharedWorker {
- public:
-  EmbeddedSharedWorkerStub(
-      blink::mojom::SharedWorkerInfoPtr info,
-      bool pause_on_start,
-      const base::UnguessableToken& devtools_worker_token,
-      const blink::mojom::RendererPreferences& renderer_preferences,
-      blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
-      blink::mojom::WorkerContentSettingsProxyPtr content_settings,
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
-          service_worker_provider_info,
-      int appcache_host_id,
-      network::mojom::URLLoaderFactoryAssociatedPtrInfo
-          main_script_loader_factory,
-      blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
-      std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
-          subresource_loader_factories,
-      blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
-      blink::mojom::SharedWorkerHostPtr host,
-      blink::mojom::SharedWorkerRequest request,
-      service_manager::mojom::InterfaceProviderPtr interface_provider);
-  ~EmbeddedSharedWorkerStub() override;
-
-  // blink::WebSharedWorkerClient implementation.
-  void CountFeature(blink::mojom::WebFeature feature) override;
-  void WorkerContextClosed() override;
-  void WorkerContextDestroyed() override;
-  void WorkerReadyForInspection() override;
-  void WorkerScriptLoaded() override;
-  void WorkerScriptLoadFailed() override;
-  void WorkerScriptEvaluated(bool success) override;
-  void SelectAppCacheID(long long) override;
-  std::unique_ptr<blink::WebApplicationCacheHost> CreateApplicationCacheHost(
-      blink::WebApplicationCacheHostClient*) override;
-  std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
-  CreateServiceWorkerNetworkProvider() override;
-  scoped_refptr<blink::WebWorkerFetchContext> CreateWorkerFetchContext(
-      blink::WebServiceWorkerNetworkProvider*) override;
-  void WaitForServiceWorkerControllerInfo(
-      blink::WebServiceWorkerNetworkProvider* web_network_provider,
-      base::OnceClosure callback) override;
-
- private:
-  // WebSharedWorker will own |channel|.
-  void ConnectToChannel(int connection_request_id,
-                        blink::MessagePortChannel channel);
-
-  // mojom::SharedWorker methods:
-  void Connect(int connection_request_id,
-               mojo::ScopedMessagePipeHandle port) override;
-  void Terminate() override;
-  void BindDevToolsAgent(
-      blink::mojom::DevToolsAgentHostAssociatedPtrInfo host,
-      blink::mojom::DevToolsAgentAssociatedRequest request) override;
-
-  mojo::Binding<blink::mojom::SharedWorker> binding_;
-  blink::mojom::SharedWorkerHostPtr host_;
-  const std::string name_;
-  bool running_ = false;
-  GURL url_;
-  blink::mojom::RendererPreferences renderer_preferences_;
-  // Set on ctor and passed to the fetch context created when
-  // CreateWorkerFetchContext() is called.
-  blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request_;
-  std::unique_ptr<blink::WebSharedWorker> impl_;
-
-  using PendingChannel =
-      std::pair<int /* connection_request_id */, blink::MessagePortChannel>;
-  std::vector<PendingChannel> pending_channels_;
-
-  const int appcache_host_id_;
-  WebApplicationCacheHostImpl* app_cache_host_ = nullptr;  // Not owned.
-
-  // S13nServiceWorker: The info needed to connect to the
-  // ServiceWorkerProviderHost on the browser.
-  blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
-      service_worker_provider_info_;
-
-  // NetworkService: The URLLoaderFactory used for loading the shared worker
-  // main script.
-  network::mojom::URLLoaderFactoryAssociatedPtrInfo main_script_loader_factory_;
-
-  // NetworkService:
-  blink::mojom::ControllerServiceWorkerInfoPtr controller_info_;
-
-  // S13nServiceWorker: The factory bundle used for loading subresources for
-  // this shared worker.
-  scoped_refptr<HostChildURLLoaderFactoryBundle> subresource_loader_factories_;
-
-  // NetworkService (PlzWorker): The response override parameters used for
-  // taking a resource pre-requested by the browser process.
-  std::unique_ptr<NavigationResponseOverrideParameters> response_override_;
-
-  // Out-of-process NetworkService:
-  // Detects disconnection from the default factory of the loader factory bundle
-  // used by this worker (typically the network service).
-  network::mojom::URLLoaderFactoryPtr
-      default_factory_connection_error_handler_holder_;
-
-  DISALLOW_COPY_AND_ASSIGN(EmbeddedSharedWorkerStub);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_SHARED_WORKER_EMBEDDED_SHARED_WORKER_STUB_H_
diff --git a/content/renderer/shared_worker/shared_worker_factory_impl.cc b/content/renderer/shared_worker/shared_worker_factory_impl.cc
deleted file mode 100644
index 308d364..0000000
--- a/content/renderer/shared_worker/shared_worker_factory_impl.cc
+++ /dev/null
@@ -1,54 +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/renderer/shared_worker/shared_worker_factory_impl.h"
-
-#include "base/memory/ptr_util.h"
-#include "content/renderer/shared_worker/embedded_shared_worker_stub.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
-#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
-
-namespace content {
-
-// static
-void SharedWorkerFactoryImpl::Create(
-    blink::mojom::SharedWorkerFactoryRequest request) {
-  mojo::MakeStrongBinding<blink::mojom::SharedWorkerFactory>(
-      base::WrapUnique(new SharedWorkerFactoryImpl()), std::move(request));
-}
-
-SharedWorkerFactoryImpl::SharedWorkerFactoryImpl() {}
-
-void SharedWorkerFactoryImpl::CreateSharedWorker(
-    blink::mojom::SharedWorkerInfoPtr info,
-    bool pause_on_start,
-    const base::UnguessableToken& devtools_worker_token,
-    blink::mojom::RendererPreferencesPtr renderer_preferences,
-    blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
-    blink::mojom::WorkerContentSettingsProxyPtr content_settings,
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
-        service_worker_provider_info,
-    int appcache_host_id,
-    network::mojom::URLLoaderFactoryAssociatedPtrInfo
-        main_script_loader_factory,
-    blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
-    std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
-        subresource_loader_factories,
-    blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
-    blink::mojom::SharedWorkerHostPtr host,
-    blink::mojom::SharedWorkerRequest request,
-    service_manager::mojom::InterfaceProviderPtr interface_provider) {
-  // Bound to the lifetime of the underlying blink::WebSharedWorker instance.
-  new EmbeddedSharedWorkerStub(
-      std::move(info), pause_on_start, devtools_worker_token,
-      *renderer_preferences, std::move(preference_watcher_request),
-      std::move(content_settings), std::move(service_worker_provider_info),
-      appcache_host_id, std::move(main_script_loader_factory),
-      std::move(main_script_load_params),
-      std::move(subresource_loader_factories), std::move(controller_info),
-      std::move(host), std::move(request), std::move(interface_provider));
-}
-
-}  // namespace content
diff --git a/content/renderer/shared_worker/shared_worker_factory_impl.h b/content/renderer/shared_worker/shared_worker_factory_impl.h
deleted file mode 100644
index 8428de1..0000000
--- a/content/renderer/shared_worker/shared_worker_factory_impl.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_RENDERER_SHARED_WORKER_SHARED_WORKER_FACTORY_IMPL_H_
-#define CONTENT_RENDERER_SHARED_WORKER_SHARED_WORKER_FACTORY_IMPL_H_
-
-#include "base/macros.h"
-#include "services/network/public/mojom/url_loader_factory.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
-#include "third_party/blink/public/mojom/worker/shared_worker_factory.mojom.h"
-
-namespace blink {
-class URLLoaderFactoryBundleInfo;
-}  // namespace blink
-
-namespace content {
-
-class SharedWorkerFactoryImpl : public blink::mojom::SharedWorkerFactory {
- public:
-  static void Create(blink::mojom::SharedWorkerFactoryRequest request);
-
- private:
-  SharedWorkerFactoryImpl();
-
-  // mojom::SharedWorkerFactory methods:
-  void CreateSharedWorker(
-      blink::mojom::SharedWorkerInfoPtr info,
-      bool pause_on_start,
-      const base::UnguessableToken& devtools_worker_token,
-      blink::mojom::RendererPreferencesPtr renderer_preferences,
-      blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
-      blink::mojom::WorkerContentSettingsProxyPtr content_settings,
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr
-          service_worker_provider_info,
-      int appcache_host_id,
-      network::mojom::URLLoaderFactoryAssociatedPtrInfo
-          main_script_loader_factory,
-      blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
-      std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
-          subresource_loader_factories,
-      blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
-      blink::mojom::SharedWorkerHostPtr host,
-      blink::mojom::SharedWorkerRequest request,
-      service_manager::mojom::InterfaceProviderPtr interface_provider) override;
-
-  DISALLOW_COPY_AND_ASSIGN(SharedWorkerFactoryImpl);
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_SHARED_WORKER_SHARED_WORKER_FACTORY_IMPL_H_
diff --git a/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.cc b/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.cc
deleted file mode 100644
index f483fc5..0000000
--- a/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.cc
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h"
-
-#include <utility>
-
-#include "base/feature_list.h"
-#include "content/public/common/origin_util.h"
-#include "content/renderer/loader/navigation_response_override_parameters.h"
-#include "content/renderer/loader/request_extra_data.h"
-#include "content/renderer/loader/web_url_loader_impl.h"
-#include "content/renderer/render_thread_impl.h"
-#include "content/renderer/service_worker/service_worker_provider_context.h"
-#include "services/network/public/cpp/features.h"
-#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
-#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
-
-namespace content {
-
-std::unique_ptr<WebServiceWorkerNetworkProviderImplForWorker>
-WebServiceWorkerNetworkProviderImplForWorker::Create(
-    blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr info,
-    network::mojom::URLLoaderFactoryAssociatedPtrInfo
-        script_loader_factory_info,
-    blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
-    scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory,
-    bool is_secure_context,
-    std::unique_ptr<NavigationResponseOverrideParameters> response_override) {
-  auto provider =
-      base::WrapUnique(new WebServiceWorkerNetworkProviderImplForWorker(
-          is_secure_context, std::move(response_override)));
-  if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
-    DCHECK(info);
-    provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>(
-        info->provider_id,
-        blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
-        std::move(info->client_request), std::move(info->host_ptr_info),
-        std::move(controller_info), std::move(fallback_loader_factory));
-    if (script_loader_factory_info.is_valid()) {
-      provider->script_loader_factory_.Bind(
-          std::move(script_loader_factory_info));
-    }
-  } else {
-    DCHECK(!info);
-    int provider_id = ServiceWorkerProviderContext::GetNextId();
-    auto host_info = blink::mojom::ServiceWorkerProviderHostInfo::New(
-        provider_id, MSG_ROUTING_NONE,
-        blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
-        true /* is_parent_frame_secure */, nullptr /* host_request */,
-        nullptr /* client_ptr_info */);
-    blink::mojom::ServiceWorkerContainerAssociatedRequest client_request =
-        mojo::MakeRequest(&host_info->client_ptr_info);
-    blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info;
-    host_info->host_request = mojo::MakeRequest(&host_ptr_info);
-
-    provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>(
-        provider_id, blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
-        std::move(client_request), std::move(host_ptr_info),
-        std::move(controller_info), std::move(fallback_loader_factory));
-    if (ChildThreadImpl::current()) {
-      ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface(
-          &provider->dispatcher_host_);
-      provider->dispatcher_host_->OnProviderCreated(std::move(host_info));
-    } else {
-      // current() may be null in tests. Silently drop messages sent over
-      // ServiceWorkerContainerHost since we couldn't set it up correctly due
-      // to this test limitation. This way we don't crash when the associated
-      // interface ptr is used.
-      //
-      // TODO(falken): Just give ServiceWorkerProviderContext a null interface
-      // ptr and make the callsites deal with it. They are supposed to anyway
-      // because OnNetworkProviderDestroyed() can reset the ptr to null at any
-      // time.
-      mojo::AssociateWithDisconnectedPipe(host_info->host_request.PassHandle());
-    }
-  }
-  return provider;
-}
-
-WebServiceWorkerNetworkProviderImplForWorker::
-    ~WebServiceWorkerNetworkProviderImplForWorker() {
-  if (context())
-    context()->OnNetworkProviderDestroyed();
-}
-
-void WebServiceWorkerNetworkProviderImplForWorker::WillSendRequest(
-    blink::WebURLRequest& request) {
-  auto extra_data = std::make_unique<RequestExtraData>();
-  extra_data->set_service_worker_provider_id(provider_id());
-  extra_data->set_initiated_in_secure_context(is_secure_context_);
-  if (response_override_) {
-    DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
-    DCHECK_EQ(blink::mojom::RequestContextType::SHARED_WORKER,
-              request.GetRequestContext());
-    extra_data->set_navigation_response_override(std::move(response_override_));
-  }
-  request.SetExtraData(std::move(extra_data));
-
-  // If the provider does not have a controller at this point, the renderer
-  // expects subresource requests to never be handled by a controlling service
-  // worker, so set |skip_service_worker| to skip service workers here.
-  // Otherwise, a service worker that is in the process of becoming the
-  // controller (i.e., via claim()) on the browser-side could handle the
-  // request and break the assumptions of the renderer.
-  if (request.GetRequestContext() !=
-          blink::mojom::RequestContextType::SHARED_WORKER &&
-      IsControlledByServiceWorker() ==
-          blink::mojom::ControllerServiceWorkerMode::kNoController) {
-    request.SetSkipServiceWorker(true);
-  }
-}
-
-blink::mojom::ControllerServiceWorkerMode
-WebServiceWorkerNetworkProviderImplForWorker::IsControlledByServiceWorker() {
-  if (!context())
-    return blink::mojom::ControllerServiceWorkerMode::kNoController;
-  return context()->IsControlledByServiceWorker();
-}
-
-int64_t
-WebServiceWorkerNetworkProviderImplForWorker::ControllerServiceWorkerID() {
-  if (!context())
-    return blink::mojom::kInvalidServiceWorkerVersionId;
-  return context()->GetControllerVersionId();
-}
-
-std::unique_ptr<blink::WebURLLoader>
-WebServiceWorkerNetworkProviderImplForWorker::CreateURLLoader(
-    const blink::WebURLRequest& request,
-    std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>
-        task_runner_handle) {
-  // S13nServiceWorker:
-  // We only install our own URLLoader if Servicification is enabled.
-  if (!blink::ServiceWorkerUtils::IsServicificationEnabled())
-    return nullptr;
-
-  RenderThreadImpl* render_thread = RenderThreadImpl::current();
-  // RenderThreadImpl is nullptr in some tests.
-  if (!render_thread) {
-    return nullptr;
-  }
-  // If the request is for the main script, use the script_loader_factory.
-  if (script_loader_factory_ &&
-      request.GetRequestContext() ==
-          blink::mojom::RequestContextType::SHARED_WORKER) {
-    // TODO(crbug.com/796425): Temporarily wrap the raw
-    // mojom::URLLoaderFactory pointer into SharedURLLoaderFactory.
-    return std::make_unique<WebURLLoaderImpl>(
-        render_thread->resource_dispatcher(), std::move(task_runner_handle),
-        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-            script_loader_factory_.get()));
-  }
-
-  // Otherwise, it's an importScript. Use the subresource loader factory if it
-  // exists (we are controlled by a service worker).
-  if (!context() || !context()->GetSubresourceLoaderFactory()) {
-    return nullptr;
-  }
-
-  // If the URL is not http(s) or otherwise whitelisted, do not intercept the
-  // request. Schemes like 'blob' and 'file' are not eligible to be
-  // intercepted by service workers.
-  // TODO(falken): Let ServiceWorkerSubresourceLoaderFactory handle the
-  // request and move this check there (i.e., for such URLs, it should use
-  // its fallback factory).
-  if (!GURL(request.Url()).SchemeIsHTTPOrHTTPS() &&
-      !OriginCanAccessServiceWorkers(request.Url())) {
-    return nullptr;
-  }
-
-  // If GetSkipServiceWorker() returns true, do not intercept the request.
-  if (request.GetSkipServiceWorker())
-    return nullptr;
-
-  // Create our own SubresourceLoader to route the request to the controller
-  // ServiceWorker.
-  // TODO(crbug.com/796425): Temporarily wrap the raw mojom::URLLoaderFactory
-  // pointer into SharedURLLoaderFactory.
-  return std::make_unique<WebURLLoaderImpl>(
-      RenderThreadImpl::current()->resource_dispatcher(),
-      std::move(task_runner_handle),
-      base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
-          context()->GetSubresourceLoaderFactory()));
-}
-
-int WebServiceWorkerNetworkProviderImplForWorker::provider_id() const {
-  if (!context_)
-    return kInvalidServiceWorkerProviderId;
-  return context_->provider_id();
-}
-
-WebServiceWorkerNetworkProviderImplForWorker::
-    WebServiceWorkerNetworkProviderImplForWorker(
-        bool is_secure_context,
-        std::unique_ptr<NavigationResponseOverrideParameters> response_override)
-    : is_secure_context_(is_secure_context),
-      response_override_(std::move(response_override)) {}
-
-}  // namespace content
diff --git a/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h b/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h
deleted file mode 100644
index 1c31ae2..0000000
--- a/content/renderer/shared_worker/web_service_worker_network_provider_impl_for_worker.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef CONTENT_RENDERER_SHARED_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_WORKER_H_
-#define CONTENT_RENDERER_SHARED_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_WORKER_H_
-
-#include <memory>
-
-#include "base/memory/ref_counted.h"
-#include "content/renderer/service_worker/service_worker_provider_context.h"
-#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
-#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
-#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
-
-namespace content {
-
-struct NavigationResponseOverrideParameters;
-
-// The WebServiceWorkerNetworkProvider implementation used for shared
-// workers.
-class WebServiceWorkerNetworkProviderImplForWorker final
-    : public blink::WebServiceWorkerNetworkProvider {
- public:
-  // Creates a new instance. Some params might only be used in S13nServiceWorker
-  // or PlzSharedWorker.
-  // - |info|: provider info from the browser
-  // - |script_loader_factory_info|: the factory for loading the worker's
-  //   scripts
-  // - |controller_info|: info about controller service worker
-  // - |fallback_loader_factory|: the factory to use when a service worker falls
-  //   back to network (unlike the default factory of this renderer, it skips
-  //   AppCache)
-  // - |is_secure_context|: whether this context is secure
-  // - |response_override|: the main script response
-  static std::unique_ptr<WebServiceWorkerNetworkProviderImplForWorker> Create(
-      blink::mojom::ServiceWorkerProviderInfoForSharedWorkerPtr info,
-      network::mojom::URLLoaderFactoryAssociatedPtrInfo
-          script_loader_factory_info,
-      blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
-      scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory,
-      bool is_secure_context,
-      std::unique_ptr<NavigationResponseOverrideParameters> response_override);
-
-  WebServiceWorkerNetworkProviderImplForWorker(
-      bool is_secure_context,
-      std::unique_ptr<NavigationResponseOverrideParameters> response_override);
-  ~WebServiceWorkerNetworkProviderImplForWorker() override;
-
-  // Implements WebServiceWorkerNetworkProvider.
-  // Blink calls this method for each request starting with the main script,
-  // we tag them with the provider id.
-  void WillSendRequest(blink::WebURLRequest& request) override;
-  blink::mojom::ControllerServiceWorkerMode IsControlledByServiceWorker()
-      override;
-  int64_t ControllerServiceWorkerID() override;
-  std::unique_ptr<blink::WebURLLoader> CreateURLLoader(
-      const blink::WebURLRequest& request,
-      std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>
-          task_runner_handle) override;
-
-  int provider_id() const;
-  ServiceWorkerProviderContext* context() { return context_.get(); }
-
- private:
-  const bool is_secure_context_;
-  std::unique_ptr<NavigationResponseOverrideParameters> response_override_;
-
-  // |context_| is null if |this| is an invalid instance, in which case there is
-  // no connection to the browser process.
-  scoped_refptr<ServiceWorkerProviderContext> context_;
-
-  // Used in non-s13nsw.
-  blink::mojom::ServiceWorkerDispatcherHostAssociatedPtr dispatcher_host_;
-
-  // The URL loader factory for loading the worker's scripts.
-  network::mojom::URLLoaderFactoryAssociatedPtr script_loader_factory_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_SHARED_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_WORKER_H_
diff --git a/content/renderer/shared_worker/OWNERS b/content/renderer/worker/OWNERS
similarity index 100%
rename from content/renderer/shared_worker/OWNERS
rename to content/renderer/worker/OWNERS
diff --git a/content/renderer/worker/README.md b/content/renderer/worker/README.md
new file mode 100644
index 0000000..c7e17ed
--- /dev/null
+++ b/content/renderer/worker/README.md
@@ -0,0 +1,8 @@
+# Web Worker in Renderer
+
+[content/renderer/worker] implements the renderer side of web workers (dedicated
+workers and shared workers). The browser side implementations are in
+[content/browser/worker_host].
+
+[content/browser/worker_host]: /content/browser/worker_host
+[content/renderer/worker]: /content/renderer/worker
diff --git a/content/renderer/worker/embedded_shared_worker_stub.cc b/content/renderer/worker/embedded_shared_worker_stub.cc
new file mode 100644
index 0000000..4ddb67c
--- /dev/null
+++ b/content/renderer/worker/embedded_shared_worker_stub.cc
@@ -0,0 +1,373 @@
+// 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 "content/renderer/worker/embedded_shared_worker_stub.h"
+
+#include <stdint.h>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/feature_list.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/common/possibly_associated_wrapper_shared_url_loader_factory.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/network_service_util.h"
+#include "content/public/common/origin_util.h"
+#include "content/renderer/appcache/appcache_frontend_impl.h"
+#include "content/renderer/appcache/web_application_cache_host_impl.h"
+#include "content/renderer/loader/child_url_loader_factory_bundle.h"
+#include "content/renderer/loader/navigation_response_override_parameters.h"
+#include "content/renderer/loader/tracked_child_url_loader_factory_bundle.h"
+#include "content/renderer/loader/web_worker_fetch_context_impl.h"
+#include "content/renderer/render_thread_impl.h"
+#include "content/renderer/renderer_blink_platform_impl.h"
+#include "content/renderer/service_worker/service_worker_provider_context.h"
+#include "content/renderer/worker/web_service_worker_network_provider_impl_for_worker.h"
+#include "ipc/ipc_message_macros.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "third_party/blink/public/common/features.h"
+#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
+#include "third_party/blink/public/common/messaging/message_port_channel.h"
+#include "third_party/blink/public/common/privacy_preferences.h"
+#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
+#include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
+#include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h"
+#include "third_party/blink/public/platform/interface_provider.h"
+#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
+#include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/public/platform/url_conversion.h"
+#include "third_party/blink/public/platform/web_security_origin.h"
+#include "third_party/blink/public/web/web_shared_worker.h"
+#include "third_party/blink/public/web/web_shared_worker_client.h"
+#include "url/origin.h"
+
+namespace content {
+
+namespace {
+
+class SharedWorkerWebApplicationCacheHostImpl
+    : public WebApplicationCacheHostImpl {
+ public:
+  SharedWorkerWebApplicationCacheHostImpl(
+      blink::WebApplicationCacheHostClient* client,
+      int appcache_host_id)
+      : WebApplicationCacheHostImpl(client,
+                                    RenderThreadImpl::current()
+                                        ->appcache_frontend_impl()
+                                        ->backend_proxy(),
+                                    appcache_host_id) {}
+
+  // Main resource loading is different for workers. The main resource is
+  // loaded by the worker using WorkerClassicScriptLoader.
+  // These overrides are stubbed out.
+  void WillStartMainResourceRequest(
+      const blink::WebURL& url,
+      const blink::WebString& method,
+      const WebApplicationCacheHost* spawning_host) override {}
+  void DidReceiveResponseForMainResource(
+      const blink::WebURLResponse&) override {}
+
+  // Cache selection is also different for workers. We know at construction
+  // time what cache to select and do so then.
+  // These overrides are stubbed out.
+  void SelectCacheWithoutManifest() override {}
+  bool SelectCacheWithManifest(const blink::WebURL& manifestURL) override {
+    return true;
+  }
+};
+
+}  // namespace
+
+EmbeddedSharedWorkerStub::EmbeddedSharedWorkerStub(
+    blink::mojom::SharedWorkerInfoPtr info,
+    bool pause_on_start,
+    const base::UnguessableToken& devtools_worker_token,
+    const blink::mojom::RendererPreferences& renderer_preferences,
+    blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
+    blink::mojom::WorkerContentSettingsProxyPtr content_settings,
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
+        service_worker_provider_info,
+    int appcache_host_id,
+    network::mojom::URLLoaderFactoryAssociatedPtrInfo
+        main_script_loader_factory,
+    blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
+    std::unique_ptr<blink::URLLoaderFactoryBundleInfo> factory_bundle,
+    blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
+    blink::mojom::SharedWorkerHostPtr host,
+    blink::mojom::SharedWorkerRequest request,
+    service_manager::mojom::InterfaceProviderPtr interface_provider)
+    : binding_(this, std::move(request)),
+      host_(std::move(host)),
+      name_(info->name),
+      url_(info->url),
+      renderer_preferences_(renderer_preferences),
+      preference_watcher_request_(std::move(preference_watcher_request)),
+      appcache_host_id_(appcache_host_id) {
+  // The ID of the precreated AppCacheHost can be valid only when the
+  // NetworkService is enabled.
+  DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService) ||
+         appcache_host_id == blink::mojom::kAppCacheNoHostId);
+
+  if (main_script_load_params) {
+    response_override_ =
+        std::make_unique<NavigationResponseOverrideParameters>();
+    response_override_->url_loader_client_endpoints =
+        std::move(main_script_load_params->url_loader_client_endpoints);
+    response_override_->response = main_script_load_params->response_head;
+    response_override_->redirect_responses =
+        main_script_load_params->redirect_response_heads;
+    response_override_->redirect_infos =
+        main_script_load_params->redirect_infos;
+  }
+
+  impl_ = blink::WebSharedWorker::Create(this);
+  if (pause_on_start) {
+    // Pause worker context when it starts and wait until either DevTools client
+    // is attached or explicit resume notification is received.
+    impl_->PauseWorkerContextOnStart();
+  }
+
+  service_worker_provider_info_ = std::move(service_worker_provider_info);
+  main_script_loader_factory_ = std::move(main_script_loader_factory);
+  controller_info_ = std::move(controller_info);
+
+  // |factory_bundle| is provided in the
+  // ServiceWorkerServicification or NetworkService case.
+  DCHECK(factory_bundle ||
+         !blink::ServiceWorkerUtils::IsServicificationEnabled());
+
+  // Make the factory bundle.
+  subresource_loader_factories_ =
+      base::MakeRefCounted<HostChildURLLoaderFactoryBundle>(
+          impl_->GetTaskRunner(blink::TaskType::kInternalLoading));
+
+  // If NetworkService or S13nSW is enabled, the default factory must be
+  // given as a |factory_bundle|.
+  // In some tests |render_thread| could be null.
+  RenderThreadImpl* render_thread = RenderThreadImpl::current();
+  if (render_thread && !blink::ServiceWorkerUtils::IsServicificationEnabled()) {
+    subresource_loader_factories_->Update(
+        render_thread->blink_platform_impl()
+            ->CreateDefaultURLLoaderFactoryBundle()
+            ->PassInterface());
+  }
+
+  if (factory_bundle) {
+    // If the network service crashes, then self-destruct so clients don't get
+    // stuck with a worker with a broken loader. Self-destruction is effectively
+    // the same as the worker's process crashing.
+    if (IsOutOfProcessNetworkService()) {
+      default_factory_connection_error_handler_holder_.Bind(
+          std::move(factory_bundle->default_factory_info()));
+      default_factory_connection_error_handler_holder_->Clone(
+          mojo::MakeRequest(&factory_bundle->default_factory_info()));
+      default_factory_connection_error_handler_holder_
+          .set_connection_error_handler(base::BindOnce(
+              &EmbeddedSharedWorkerStub::Terminate, base::Unretained(this)));
+    }
+
+    subresource_loader_factories_->Update(
+        std::make_unique<ChildURLLoaderFactoryBundleInfo>(
+            std::move(factory_bundle)));
+  }
+
+  impl_->StartWorkerContext(
+      url_, blink::WebString::FromUTF8(name_),
+      blink::WebString::FromUTF8(info->content_security_policy),
+      info->content_security_policy_type, info->creation_address_space,
+      devtools_worker_token,
+      blink::PrivacyPreferences(renderer_preferences_.enable_do_not_track,
+                                renderer_preferences_.enable_referrers),
+      subresource_loader_factories_,
+      content_settings.PassInterface().PassHandle(),
+      interface_provider.PassInterface().PassHandle());
+
+  // If the host drops its connection, then self-destruct.
+  binding_.set_connection_error_handler(base::BindOnce(
+      &EmbeddedSharedWorkerStub::Terminate, base::Unretained(this)));
+}
+
+EmbeddedSharedWorkerStub::~EmbeddedSharedWorkerStub() {
+  // Destruction closes our connection to the host, triggering the host to
+  // cleanup and notify clients of this worker going away.
+}
+
+void EmbeddedSharedWorkerStub::WorkerReadyForInspection() {
+  host_->OnReadyForInspection();
+}
+
+void EmbeddedSharedWorkerStub::WorkerScriptLoaded() {
+  host_->OnScriptLoaded();
+}
+
+void EmbeddedSharedWorkerStub::WorkerScriptLoadFailed() {
+  host_->OnScriptLoadFailed();
+  pending_channels_.clear();
+}
+
+void EmbeddedSharedWorkerStub::WorkerScriptEvaluated(bool success) {
+  DCHECK(!running_);
+  running_ = true;
+  // Process any pending connections.
+  for (auto& item : pending_channels_)
+    ConnectToChannel(item.first, std::move(item.second));
+  pending_channels_.clear();
+}
+
+void EmbeddedSharedWorkerStub::CountFeature(blink::mojom::WebFeature feature) {
+  host_->OnFeatureUsed(feature);
+}
+
+void EmbeddedSharedWorkerStub::WorkerContextClosed() {
+  host_->OnContextClosed();
+}
+
+void EmbeddedSharedWorkerStub::WorkerContextDestroyed() {
+  delete this;
+}
+
+void EmbeddedSharedWorkerStub::SelectAppCacheID(long long app_cache_id) {
+  if (app_cache_host_) {
+    // app_cache_host_ could become stale as it's owned by blink's
+    // DocumentLoader. This method is assumed to be called while it's valid.
+    app_cache_host_->backend()->SelectCacheForSharedWorker(
+        app_cache_host_->host_id(), app_cache_id);
+  }
+}
+
+std::unique_ptr<blink::WebApplicationCacheHost>
+EmbeddedSharedWorkerStub::CreateApplicationCacheHost(
+    blink::WebApplicationCacheHostClient* client) {
+  std::unique_ptr<WebApplicationCacheHostImpl> host =
+      std::make_unique<SharedWorkerWebApplicationCacheHostImpl>(
+          client, appcache_host_id_);
+  app_cache_host_ = host.get();
+  return std::move(host);
+}
+
+std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
+EmbeddedSharedWorkerStub::CreateServiceWorkerNetworkProvider() {
+  if (blink::features::IsOffMainThreadSharedWorkerScriptFetchEnabled()) {
+    // PlzSharedWorker w/ off-the-main-thread shared worker script fetch:
+    // |response_override_| will be passed to WebWorkerFetchContextImpl in
+    // CreateWorkerFetchContext() and consumed during off-the-main-thread
+    // shared worker script fetch.
+    DCHECK(response_override_);
+    return WebServiceWorkerNetworkProviderImplForWorker::Create(
+        std::move(service_worker_provider_info_),
+        std::move(main_script_loader_factory_), std::move(controller_info_),
+        subresource_loader_factories_, IsOriginSecure(url_),
+        nullptr /* response_override */);
+  }
+
+#if DCHECK_IS_ON()
+  if (base::FeatureList::IsEnabled(network::features::kNetworkService)) {
+    // PlzSharedWorker:
+    // |response_override_| is passed to DocumentLoader and consumed during
+    // on-the-main-thread shared worker script fetch.
+    DCHECK(response_override_);
+  } else {
+    // Legacy loading path:
+    // This path will be removed once PlzSharedWorker and off-the-main-thread
+    // shared worker script fetch are enabled by default.
+    DCHECK(!response_override_);
+  }
+#endif  // DCHECK_IS_ON()
+
+  return WebServiceWorkerNetworkProviderImplForWorker::Create(
+      std::move(service_worker_provider_info_),
+      std::move(main_script_loader_factory_), std::move(controller_info_),
+      subresource_loader_factories_, IsOriginSecure(url_),
+      std::move(response_override_));
+}
+
+void EmbeddedSharedWorkerStub::WaitForServiceWorkerControllerInfo(
+    blink::WebServiceWorkerNetworkProvider* web_network_provider,
+    base::OnceClosure callback) {
+  ServiceWorkerProviderContext* context =
+      static_cast<WebServiceWorkerNetworkProviderImplForWorker*>(
+          web_network_provider)
+          ->context();
+  context->PingContainerHost(std::move(callback));
+}
+
+scoped_refptr<blink::WebWorkerFetchContext>
+EmbeddedSharedWorkerStub::CreateWorkerFetchContext(
+    blink::WebServiceWorkerNetworkProvider* web_network_provider) {
+  DCHECK(web_network_provider);
+  WebServiceWorkerNetworkProviderImplForWorker* network_provider =
+      static_cast<WebServiceWorkerNetworkProviderImplForWorker*>(
+          web_network_provider);
+
+  // Make the factory used for service worker network fallback (that should
+  // skip AppCache if it is provided).
+  std::unique_ptr<network::SharedURLLoaderFactoryInfo> fallback_factory =
+      subresource_loader_factories_->CloneWithoutAppCacheFactory();
+
+  scoped_refptr<WebWorkerFetchContextImpl> worker_fetch_context =
+      WebWorkerFetchContextImpl::Create(
+          network_provider->context(), std::move(renderer_preferences_),
+          std::move(preference_watcher_request_),
+          subresource_loader_factories_->Clone(), std::move(fallback_factory));
+
+  // TODO(horo): To get the correct first_party_to_cookies for the shared
+  // worker, we need to check the all documents bounded by the shared worker.
+  // (crbug.com/723553)
+  // https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-07#section-2.1.2
+  worker_fetch_context->set_site_for_cookies(url_);
+  // TODO(horo): Currently we treat the worker context as secure if the origin
+  // of the shared worker script url is secure. But according to the spec, if
+  // the creation context is not secure, we should treat the worker as
+  // non-secure. crbug.com/723575
+  // https://w3c.github.io/webappsec-secure-contexts/#examples-shared-workers
+  worker_fetch_context->set_is_secure_context(IsOriginSecure(url_));
+  worker_fetch_context->set_origin_url(url_.GetOrigin());
+
+  if (response_override_) {
+    DCHECK(blink::features::IsOffMainThreadSharedWorkerScriptFetchEnabled());
+    worker_fetch_context->SetResponseOverrideForMainScript(
+        std::move(response_override_));
+  }
+
+  return worker_fetch_context;
+}
+
+void EmbeddedSharedWorkerStub::ConnectToChannel(
+    int connection_request_id,
+    blink::MessagePortChannel channel) {
+  impl_->Connect(std::move(channel));
+  host_->OnConnected(connection_request_id);
+}
+
+void EmbeddedSharedWorkerStub::Connect(int connection_request_id,
+                                       mojo::ScopedMessagePipeHandle port) {
+  blink::MessagePortChannel channel(std::move(port));
+  if (running_) {
+    ConnectToChannel(connection_request_id, std::move(channel));
+  } else {
+    // If two documents try to load a SharedWorker at the same time, the
+    // mojom::SharedWorker::Connect() for one of the documents can come in
+    // before the worker is started. Just queue up the connect and deliver it
+    // once the worker starts.
+    pending_channels_.emplace_back(connection_request_id, std::move(channel));
+  }
+}
+
+void EmbeddedSharedWorkerStub::Terminate() {
+  // After this we should ignore any IPC for this stub.
+  running_ = false;
+  impl_->TerminateWorkerContext();
+}
+
+void EmbeddedSharedWorkerStub::BindDevToolsAgent(
+    blink::mojom::DevToolsAgentHostAssociatedPtrInfo host,
+    blink::mojom::DevToolsAgentAssociatedRequest request) {
+  impl_->BindDevToolsAgent(host.PassHandle(), request.PassHandle());
+}
+
+}  // namespace content
diff --git a/content/renderer/worker/embedded_shared_worker_stub.h b/content/renderer/worker/embedded_shared_worker_stub.h
new file mode 100644
index 0000000..d5d248e
--- /dev/null
+++ b/content/renderer/worker/embedded_shared_worker_stub.h
@@ -0,0 +1,161 @@
+// 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 CONTENT_RENDERER_WORKER_EMBEDDED_SHARED_WORKER_STUB_H_
+#define CONTENT_RENDERER_WORKER_EMBEDDED_SHARED_WORKER_STUB_H_
+
+#include <memory>
+#include <vector>
+
+#include "base/macros.h"
+#include "base/unguessable_token.h"
+#include "ipc/ipc_listener.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "services/service_manager/public/mojom/interface_provider.mojom.h"
+#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom.h"
+#include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h"
+#include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
+#include "third_party/blink/public/mojom/worker/shared_worker.mojom.h"
+#include "third_party/blink/public/mojom/worker/shared_worker_host.mojom.h"
+#include "third_party/blink/public/mojom/worker/shared_worker_info.mojom.h"
+#include "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom.h"
+#include "third_party/blink/public/mojom/worker/worker_main_script_load_params.mojom.h"
+#include "third_party/blink/public/platform/web_content_security_policy.h"
+#include "third_party/blink/public/platform/web_content_settings_client.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/web_shared_worker_client.h"
+#include "url/gurl.h"
+
+namespace blink {
+class WebApplicationCacheHost;
+class WebApplicationCacheHostClient;
+class WebSharedWorker;
+}  // namespace blink
+
+namespace blink {
+class MessagePortChannel;
+class URLLoaderFactoryBundleInfo;
+}  // namespace blink
+
+namespace content {
+
+class HostChildURLLoaderFactoryBundle;
+class WebApplicationCacheHostImpl;
+struct NavigationResponseOverrideParameters;
+
+// A stub class to receive IPC from browser process and talk to
+// blink::WebSharedWorker. Implements blink::WebSharedWorkerClient.
+// This class is self-destructed (no one explicitly owns this). It deletes
+// itself when WorkerContextDestroyed() is called by blink::WebSharedWorker.
+//
+// This class owns blink::WebSharedWorker.
+class EmbeddedSharedWorkerStub : public blink::WebSharedWorkerClient,
+                                 public blink::mojom::SharedWorker {
+ public:
+  EmbeddedSharedWorkerStub(
+      blink::mojom::SharedWorkerInfoPtr info,
+      bool pause_on_start,
+      const base::UnguessableToken& devtools_worker_token,
+      const blink::mojom::RendererPreferences& renderer_preferences,
+      blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
+      blink::mojom::WorkerContentSettingsProxyPtr content_settings,
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
+          service_worker_provider_info,
+      int appcache_host_id,
+      network::mojom::URLLoaderFactoryAssociatedPtrInfo
+          main_script_loader_factory,
+      blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
+      std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
+          subresource_loader_factories,
+      blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
+      blink::mojom::SharedWorkerHostPtr host,
+      blink::mojom::SharedWorkerRequest request,
+      service_manager::mojom::InterfaceProviderPtr interface_provider);
+  ~EmbeddedSharedWorkerStub() override;
+
+  // blink::WebSharedWorkerClient implementation.
+  void CountFeature(blink::mojom::WebFeature feature) override;
+  void WorkerContextClosed() override;
+  void WorkerContextDestroyed() override;
+  void WorkerReadyForInspection() override;
+  void WorkerScriptLoaded() override;
+  void WorkerScriptLoadFailed() override;
+  void WorkerScriptEvaluated(bool success) override;
+  void SelectAppCacheID(long long) override;
+  std::unique_ptr<blink::WebApplicationCacheHost> CreateApplicationCacheHost(
+      blink::WebApplicationCacheHostClient*) override;
+  std::unique_ptr<blink::WebServiceWorkerNetworkProvider>
+  CreateServiceWorkerNetworkProvider() override;
+  scoped_refptr<blink::WebWorkerFetchContext> CreateWorkerFetchContext(
+      blink::WebServiceWorkerNetworkProvider*) override;
+  void WaitForServiceWorkerControllerInfo(
+      blink::WebServiceWorkerNetworkProvider* web_network_provider,
+      base::OnceClosure callback) override;
+
+ private:
+  // WebSharedWorker will own |channel|.
+  void ConnectToChannel(int connection_request_id,
+                        blink::MessagePortChannel channel);
+
+  // mojom::SharedWorker methods:
+  void Connect(int connection_request_id,
+               mojo::ScopedMessagePipeHandle port) override;
+  void Terminate() override;
+  void BindDevToolsAgent(
+      blink::mojom::DevToolsAgentHostAssociatedPtrInfo host,
+      blink::mojom::DevToolsAgentAssociatedRequest request) override;
+
+  mojo::Binding<blink::mojom::SharedWorker> binding_;
+  blink::mojom::SharedWorkerHostPtr host_;
+  const std::string name_;
+  bool running_ = false;
+  GURL url_;
+  blink::mojom::RendererPreferences renderer_preferences_;
+  // Set on ctor and passed to the fetch context created when
+  // CreateWorkerFetchContext() is called.
+  blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request_;
+  std::unique_ptr<blink::WebSharedWorker> impl_;
+
+  using PendingChannel =
+      std::pair<int /* connection_request_id */, blink::MessagePortChannel>;
+  std::vector<PendingChannel> pending_channels_;
+
+  const int appcache_host_id_;
+  WebApplicationCacheHostImpl* app_cache_host_ = nullptr;  // Not owned.
+
+  // S13nServiceWorker: The info needed to connect to the
+  // ServiceWorkerProviderHost on the browser.
+  blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
+      service_worker_provider_info_;
+
+  // NetworkService: The URLLoaderFactory used for loading the shared worker
+  // main script.
+  network::mojom::URLLoaderFactoryAssociatedPtrInfo main_script_loader_factory_;
+
+  // NetworkService:
+  blink::mojom::ControllerServiceWorkerInfoPtr controller_info_;
+
+  // S13nServiceWorker: The factory bundle used for loading subresources for
+  // this shared worker.
+  scoped_refptr<HostChildURLLoaderFactoryBundle> subresource_loader_factories_;
+
+  // NetworkService (PlzWorker): The response override parameters used for
+  // taking a resource pre-requested by the browser process.
+  std::unique_ptr<NavigationResponseOverrideParameters> response_override_;
+
+  // Out-of-process NetworkService:
+  // Detects disconnection from the default factory of the loader factory bundle
+  // used by this worker (typically the network service).
+  network::mojom::URLLoaderFactoryPtr
+      default_factory_connection_error_handler_holder_;
+
+  DISALLOW_COPY_AND_ASSIGN(EmbeddedSharedWorkerStub);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_WORKER_EMBEDDED_SHARED_WORKER_STUB_H_
diff --git a/content/renderer/worker/shared_worker_factory_impl.cc b/content/renderer/worker/shared_worker_factory_impl.cc
new file mode 100644
index 0000000..87fd36e
--- /dev/null
+++ b/content/renderer/worker/shared_worker_factory_impl.cc
@@ -0,0 +1,54 @@
+// 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/renderer/worker/shared_worker_factory_impl.h"
+
+#include "base/memory/ptr_util.h"
+#include "content/renderer/worker/embedded_shared_worker_stub.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
+#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
+
+namespace content {
+
+// static
+void SharedWorkerFactoryImpl::Create(
+    blink::mojom::SharedWorkerFactoryRequest request) {
+  mojo::MakeStrongBinding<blink::mojom::SharedWorkerFactory>(
+      base::WrapUnique(new SharedWorkerFactoryImpl()), std::move(request));
+}
+
+SharedWorkerFactoryImpl::SharedWorkerFactoryImpl() {}
+
+void SharedWorkerFactoryImpl::CreateSharedWorker(
+    blink::mojom::SharedWorkerInfoPtr info,
+    bool pause_on_start,
+    const base::UnguessableToken& devtools_worker_token,
+    blink::mojom::RendererPreferencesPtr renderer_preferences,
+    blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
+    blink::mojom::WorkerContentSettingsProxyPtr content_settings,
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
+        service_worker_provider_info,
+    int appcache_host_id,
+    network::mojom::URLLoaderFactoryAssociatedPtrInfo
+        main_script_loader_factory,
+    blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
+    std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
+        subresource_loader_factories,
+    blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
+    blink::mojom::SharedWorkerHostPtr host,
+    blink::mojom::SharedWorkerRequest request,
+    service_manager::mojom::InterfaceProviderPtr interface_provider) {
+  // Bound to the lifetime of the underlying blink::WebSharedWorker instance.
+  new EmbeddedSharedWorkerStub(
+      std::move(info), pause_on_start, devtools_worker_token,
+      *renderer_preferences, std::move(preference_watcher_request),
+      std::move(content_settings), std::move(service_worker_provider_info),
+      appcache_host_id, std::move(main_script_loader_factory),
+      std::move(main_script_load_params),
+      std::move(subresource_loader_factories), std::move(controller_info),
+      std::move(host), std::move(request), std::move(interface_provider));
+}
+
+}  // namespace content
diff --git a/content/renderer/worker/shared_worker_factory_impl.h b/content/renderer/worker/shared_worker_factory_impl.h
new file mode 100644
index 0000000..41760c7
--- /dev/null
+++ b/content/renderer/worker/shared_worker_factory_impl.h
@@ -0,0 +1,52 @@
+// 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_RENDERER_WORKER_SHARED_WORKER_FACTORY_IMPL_H_
+#define CONTENT_RENDERER_WORKER_SHARED_WORKER_FACTORY_IMPL_H_
+
+#include "base/macros.h"
+#include "services/network/public/mojom/url_loader_factory.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
+#include "third_party/blink/public/mojom/worker/shared_worker_factory.mojom.h"
+
+namespace blink {
+class URLLoaderFactoryBundleInfo;
+}  // namespace blink
+
+namespace content {
+
+class SharedWorkerFactoryImpl : public blink::mojom::SharedWorkerFactory {
+ public:
+  static void Create(blink::mojom::SharedWorkerFactoryRequest request);
+
+ private:
+  SharedWorkerFactoryImpl();
+
+  // mojom::SharedWorkerFactory methods:
+  void CreateSharedWorker(
+      blink::mojom::SharedWorkerInfoPtr info,
+      bool pause_on_start,
+      const base::UnguessableToken& devtools_worker_token,
+      blink::mojom::RendererPreferencesPtr renderer_preferences,
+      blink::mojom::RendererPreferenceWatcherRequest preference_watcher_request,
+      blink::mojom::WorkerContentSettingsProxyPtr content_settings,
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr
+          service_worker_provider_info,
+      int appcache_host_id,
+      network::mojom::URLLoaderFactoryAssociatedPtrInfo
+          main_script_loader_factory,
+      blink::mojom::WorkerMainScriptLoadParamsPtr main_script_load_params,
+      std::unique_ptr<blink::URLLoaderFactoryBundleInfo>
+          subresource_loader_factories,
+      blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
+      blink::mojom::SharedWorkerHostPtr host,
+      blink::mojom::SharedWorkerRequest request,
+      service_manager::mojom::InterfaceProviderPtr interface_provider) override;
+
+  DISALLOW_COPY_AND_ASSIGN(SharedWorkerFactoryImpl);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_WORKER_SHARED_WORKER_FACTORY_IMPL_H_
diff --git a/content/renderer/worker/web_service_worker_network_provider_impl_for_worker.cc b/content/renderer/worker/web_service_worker_network_provider_impl_for_worker.cc
new file mode 100644
index 0000000..539de29
--- /dev/null
+++ b/content/renderer/worker/web_service_worker_network_provider_impl_for_worker.cc
@@ -0,0 +1,165 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/worker/web_service_worker_network_provider_impl_for_worker.h"
+
+#include <utility>
+
+#include "base/feature_list.h"
+#include "content/public/common/origin_util.h"
+#include "content/renderer/loader/navigation_response_override_parameters.h"
+#include "content/renderer/loader/request_extra_data.h"
+#include "content/renderer/loader/web_url_loader_impl.h"
+#include "content/renderer/render_thread_impl.h"
+#include "content/renderer/service_worker/service_worker_provider_context.h"
+#include "services/network/public/cpp/features.h"
+#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
+#include "third_party/blink/public/common/service_worker/service_worker_utils.h"
+
+namespace content {
+
+std::unique_ptr<WebServiceWorkerNetworkProviderImplForWorker>
+WebServiceWorkerNetworkProviderImplForWorker::Create(
+    blink::mojom::ServiceWorkerProviderInfoForWorkerPtr info,
+    network::mojom::URLLoaderFactoryAssociatedPtrInfo
+        script_loader_factory_info,
+    blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
+    scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory,
+    bool is_secure_context,
+    std::unique_ptr<NavigationResponseOverrideParameters> response_override) {
+  auto provider =
+      base::WrapUnique(new WebServiceWorkerNetworkProviderImplForWorker(
+          is_secure_context, std::move(response_override)));
+  if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
+    DCHECK(info);
+    provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>(
+        info->provider_id,
+        blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+        std::move(info->client_request), std::move(info->host_ptr_info),
+        std::move(controller_info), std::move(fallback_loader_factory));
+    if (script_loader_factory_info.is_valid()) {
+      provider->script_loader_factory_.Bind(
+          std::move(script_loader_factory_info));
+    }
+  } else {
+    DCHECK(!info);
+    int provider_id = ServiceWorkerProviderContext::GetNextId();
+    auto host_info = blink::mojom::ServiceWorkerProviderHostInfo::New(
+        provider_id, MSG_ROUTING_NONE,
+        blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+        true /* is_parent_frame_secure */, nullptr /* host_request */,
+        nullptr /* client_ptr_info */);
+    blink::mojom::ServiceWorkerContainerAssociatedRequest client_request =
+        mojo::MakeRequest(&host_info->client_ptr_info);
+    blink::mojom::ServiceWorkerContainerHostAssociatedPtrInfo host_ptr_info;
+    host_info->host_request = mojo::MakeRequest(&host_ptr_info);
+
+    provider->context_ = base::MakeRefCounted<ServiceWorkerProviderContext>(
+        provider_id, blink::mojom::ServiceWorkerProviderType::kForSharedWorker,
+        std::move(client_request), std::move(host_ptr_info),
+        std::move(controller_info), std::move(fallback_loader_factory));
+    if (ChildThreadImpl::current()) {
+      ChildThreadImpl::current()->channel()->GetRemoteAssociatedInterface(
+          &provider->dispatcher_host_);
+      provider->dispatcher_host_->OnProviderCreated(std::move(host_info));
+    } else {
+      // current() may be null in tests. Silently drop messages sent over
+      // ServiceWorkerContainerHost since we couldn't set it up correctly due
+      // to this test limitation. This way we don't crash when the associated
+      // interface ptr is used.
+      //
+      // TODO(falken): Just give ServiceWorkerProviderContext a null interface
+      // ptr and make the callsites deal with it. They are supposed to anyway
+      // because OnNetworkProviderDestroyed() can reset the ptr to null at any
+      // time.
+      mojo::AssociateWithDisconnectedPipe(host_info->host_request.PassHandle());
+    }
+  }
+  return provider;
+}
+
+WebServiceWorkerNetworkProviderImplForWorker::
+    ~WebServiceWorkerNetworkProviderImplForWorker() {
+  if (context())
+    context()->OnNetworkProviderDestroyed();
+}
+
+void WebServiceWorkerNetworkProviderImplForWorker::WillSendRequest(
+    blink::WebURLRequest& request) {
+  DCHECK_EQ(blink::mojom::RequestContextType::SHARED_WORKER,
+            request.GetRequestContext());
+  auto extra_data = std::make_unique<RequestExtraData>();
+  extra_data->set_service_worker_provider_id(provider_id());
+  extra_data->set_initiated_in_secure_context(is_secure_context_);
+  if (response_override_) {
+    DCHECK(base::FeatureList::IsEnabled(network::features::kNetworkService));
+    extra_data->set_navigation_response_override(std::move(response_override_));
+  }
+  request.SetExtraData(std::move(extra_data));
+}
+
+blink::mojom::ControllerServiceWorkerMode
+WebServiceWorkerNetworkProviderImplForWorker::IsControlledByServiceWorker() {
+  if (!context())
+    return blink::mojom::ControllerServiceWorkerMode::kNoController;
+  return context()->IsControlledByServiceWorker();
+}
+
+int64_t
+WebServiceWorkerNetworkProviderImplForWorker::ControllerServiceWorkerID() {
+  if (!context())
+    return blink::mojom::kInvalidServiceWorkerVersionId;
+  return context()->GetControllerVersionId();
+}
+
+std::unique_ptr<blink::WebURLLoader>
+WebServiceWorkerNetworkProviderImplForWorker::CreateURLLoader(
+    const blink::WebURLRequest& request,
+    std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>
+        task_runner_handle) {
+  // We only get here for the main script request from the shadow page.
+  // importScripts() and other subresource fetches are handled on the worker
+  // thread by WebWorkerFetchContextImpl.
+  DCHECK_EQ(blink::mojom::RequestContextType::SHARED_WORKER,
+            request.GetRequestContext());
+
+  // S13nServiceWorker:
+  // We only install our own URLLoader if Servicification is enabled.
+  if (!blink::ServiceWorkerUtils::IsServicificationEnabled())
+    return nullptr;
+
+  // If the |script_loader_factory_| exists, use it.
+  if (script_loader_factory_) {
+    RenderThreadImpl* render_thread = RenderThreadImpl::current();
+    if (!render_thread) {
+      // RenderThreadImpl is nullptr in some tests.
+      return nullptr;
+    }
+
+    // TODO(crbug.com/796425): Temporarily wrap the raw
+    // mojom::URLLoaderFactory pointer into SharedURLLoaderFactory.
+    return std::make_unique<WebURLLoaderImpl>(
+        render_thread->resource_dispatcher(), std::move(task_runner_handle),
+        base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
+            script_loader_factory_.get()));
+  }
+
+  // Otherwise go to default resource loading.
+  return nullptr;
+}
+
+int WebServiceWorkerNetworkProviderImplForWorker::provider_id() const {
+  if (!context_)
+    return kInvalidServiceWorkerProviderId;
+  return context_->provider_id();
+}
+
+WebServiceWorkerNetworkProviderImplForWorker::
+    WebServiceWorkerNetworkProviderImplForWorker(
+        bool is_secure_context,
+        std::unique_ptr<NavigationResponseOverrideParameters> response_override)
+    : is_secure_context_(is_secure_context),
+      response_override_(std::move(response_override)) {}
+
+}  // namespace content
diff --git a/content/renderer/worker/web_service_worker_network_provider_impl_for_worker.h b/content/renderer/worker/web_service_worker_network_provider_impl_for_worker.h
new file mode 100644
index 0000000..b055c91
--- /dev/null
+++ b/content/renderer/worker/web_service_worker_network_provider_impl_for_worker.h
@@ -0,0 +1,84 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CONTENT_RENDERER_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_WORKER_H_
+#define CONTENT_RENDERER_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_WORKER_H_
+
+#include <memory>
+
+#include "base/memory/ref_counted.h"
+#include "content/renderer/service_worker/service_worker_provider_context.h"
+#include "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom.h"
+#include "third_party/blink/public/mojom/service_worker/service_worker_provider_type.mojom.h"
+#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
+
+namespace content {
+
+struct NavigationResponseOverrideParameters;
+
+// The WebServiceWorkerNetworkProvider implementation used for shared
+// workers.
+//
+// This class is only used for the main script request from the shadow page.
+// Remove it when the shadow page is removed (https://crbug.com/538751).
+class WebServiceWorkerNetworkProviderImplForWorker final
+    : public blink::WebServiceWorkerNetworkProvider {
+ public:
+  // Creates a new instance. Some params might only be used in S13nServiceWorker
+  // or PlzSharedWorker.
+  // - |info|: provider info from the browser
+  // - |script_loader_factory_info|: the factory for loading the worker's
+  //   scripts
+  // - |controller_info|: info about controller service worker
+  // - |fallback_loader_factory|: the factory to use when a service worker falls
+  //   back to network (unlike the default factory of this renderer, it skips
+  //   AppCache)
+  // - |is_secure_context|: whether this context is secure
+  // - |response_override|: the main script response
+  static std::unique_ptr<WebServiceWorkerNetworkProviderImplForWorker> Create(
+      blink::mojom::ServiceWorkerProviderInfoForWorkerPtr info,
+      network::mojom::URLLoaderFactoryAssociatedPtrInfo
+          script_loader_factory_info,
+      blink::mojom::ControllerServiceWorkerInfoPtr controller_info,
+      scoped_refptr<network::SharedURLLoaderFactory> fallback_loader_factory,
+      bool is_secure_context,
+      std::unique_ptr<NavigationResponseOverrideParameters> response_override);
+
+  WebServiceWorkerNetworkProviderImplForWorker(
+      bool is_secure_context,
+      std::unique_ptr<NavigationResponseOverrideParameters> response_override);
+  ~WebServiceWorkerNetworkProviderImplForWorker() override;
+
+  // Implements WebServiceWorkerNetworkProvider.
+  void WillSendRequest(blink::WebURLRequest& request) override;
+  blink::mojom::ControllerServiceWorkerMode IsControlledByServiceWorker()
+      override;
+  int64_t ControllerServiceWorkerID() override;
+  std::unique_ptr<blink::WebURLLoader> CreateURLLoader(
+      const blink::WebURLRequest& request,
+      std::unique_ptr<blink::scheduler::WebResourceLoadingTaskRunnerHandle>
+          task_runner_handle) override;
+
+  int provider_id() const;
+  ServiceWorkerProviderContext* context() { return context_.get(); }
+
+ private:
+  const bool is_secure_context_;
+  std::unique_ptr<NavigationResponseOverrideParameters> response_override_;
+
+  // |context_| is null if |this| is an invalid instance, in which case there is
+  // no connection to the browser process.
+  scoped_refptr<ServiceWorkerProviderContext> context_;
+
+  // Used in non-s13nsw.
+  blink::mojom::ServiceWorkerDispatcherHostAssociatedPtr dispatcher_host_;
+
+  // The URL loader factory for loading the worker's scripts.
+  network::mojom::URLLoaderFactoryAssociatedPtr script_loader_factory_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_WORKER_WEB_SERVICE_WORKER_NETWORK_PROVIDER_IMPL_FOR_WORKER_H_
diff --git a/content/renderer/worker/worker_thread_registry.cc b/content/renderer/worker/worker_thread_registry.cc
new file mode 100644
index 0000000..fcf2bee
--- /dev/null
+++ b/content/renderer/worker/worker_thread_registry.cc
@@ -0,0 +1,138 @@
+// 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 "content/renderer/worker/worker_thread_registry.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/lazy_instance.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/observer_list.h"
+#include "base/single_thread_task_runner.h"
+#include "base/stl_util.h"
+#include "base/threading/thread_local.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "content/public/renderer/worker_thread.h"
+
+namespace content {
+
+namespace {
+
+using WorkerThreadObservers =
+    base::ObserverList<WorkerThread::Observer>::Unchecked;
+using ThreadLocalWorkerThreadObservers =
+    base::ThreadLocalPointer<WorkerThreadObservers>;
+
+// Stores a WorkerThreadObservers instance per thread.
+base::LazyInstance<ThreadLocalWorkerThreadObservers>::DestructorAtExit
+    g_observers_tls = LAZY_INSTANCE_INITIALIZER;
+
+// A task-runner that refuses to run any tasks.
+class DoNothingTaskRunner : public base::TaskRunner {
+ public:
+  DoNothingTaskRunner() {}
+
+ private:
+  ~DoNothingTaskRunner() override {}
+
+  bool PostDelayedTask(const base::Location& from_here,
+                       base::OnceClosure task,
+                       base::TimeDelta delay) override {
+    return false;
+  }
+
+  bool RunsTasksInCurrentSequence() const override { return false; }
+};
+
+}  // namespace
+
+// WorkerThread implementation:
+
+int WorkerThread::GetCurrentId() {
+  if (!g_observers_tls.Pointer()->Get())
+    return 0;
+  return base::PlatformThread::CurrentId();
+}
+
+void WorkerThread::PostTask(int id, base::OnceClosure task) {
+  WorkerThreadRegistry::Instance()->PostTask(id, std::move(task));
+}
+
+void WorkerThread::AddObserver(Observer* observer) {
+  DCHECK(GetCurrentId() > 0);
+  WorkerThreadObservers* observers = g_observers_tls.Pointer()->Get();
+  DCHECK(observers);
+  observers->AddObserver(observer);
+}
+
+void WorkerThread::RemoveObserver(Observer* observer) {
+  DCHECK(GetCurrentId() > 0);
+  WorkerThreadObservers* observers = g_observers_tls.Pointer()->Get();
+  DCHECK(observers);
+  observers->RemoveObserver(observer);
+}
+
+// WorkerThreadRegistry implementation:
+
+WorkerThreadRegistry::WorkerThreadRegistry()
+    : task_runner_for_dead_worker_(new DoNothingTaskRunner()) {}
+
+int WorkerThreadRegistry::PostTaskToAllThreads(base::Closure closure) {
+  base::AutoLock locker(task_runner_map_lock_);
+  for (const auto& it : task_runner_map_)
+    it.second->PostTask(FROM_HERE, closure);
+  return static_cast<int>(task_runner_map_.size());
+}
+
+WorkerThreadRegistry* WorkerThreadRegistry::Instance() {
+  static base::LazyInstance<WorkerThreadRegistry>::Leaky worker_task_runner =
+      LAZY_INSTANCE_INITIALIZER;
+  return worker_task_runner.Pointer();
+}
+
+WorkerThreadRegistry::~WorkerThreadRegistry() {}
+
+void WorkerThreadRegistry::DidStartCurrentWorkerThread() {
+  DCHECK(!g_observers_tls.Pointer()->Get());
+  DCHECK(!base::PlatformThread::CurrentRef().is_null());
+  g_observers_tls.Pointer()->Set(new WorkerThreadObservers());
+  int id = base::PlatformThread::CurrentId();
+  base::AutoLock locker_(task_runner_map_lock_);
+  task_runner_map_[id] = base::ThreadTaskRunnerHandle::Get().get();
+  CHECK(task_runner_map_[id]);
+}
+
+void WorkerThreadRegistry::WillStopCurrentWorkerThread() {
+  WorkerThreadObservers* observers = g_observers_tls.Pointer()->Get();
+  DCHECK(observers);
+  for (auto& observer : *observers)
+    observer.WillStopCurrentWorkerThread();
+
+  {
+    base::AutoLock locker(task_runner_map_lock_);
+    task_runner_map_.erase(WorkerThread::GetCurrentId());
+  }
+  delete observers;
+  g_observers_tls.Pointer()->Set(nullptr);
+}
+
+base::TaskRunner* WorkerThreadRegistry::GetTaskRunnerFor(int worker_id) {
+  base::AutoLock locker(task_runner_map_lock_);
+  return base::ContainsKey(task_runner_map_, worker_id)
+             ? task_runner_map_[worker_id]
+             : task_runner_for_dead_worker_.get();
+}
+
+bool WorkerThreadRegistry::PostTask(int id, base::OnceClosure closure) {
+  DCHECK(id > 0);
+  base::AutoLock locker(task_runner_map_lock_);
+  auto found = task_runner_map_.find(id);
+  if (found == task_runner_map_.end())
+    return false;
+  return found->second->PostTask(FROM_HERE, std::move(closure));
+}
+
+}  // namespace content
diff --git a/content/renderer/worker/worker_thread_registry.h b/content/renderer/worker/worker_thread_registry.h
new file mode 100644
index 0000000..4500691b
--- /dev/null
+++ b/content/renderer/worker/worker_thread_registry.h
@@ -0,0 +1,62 @@
+// 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 CONTENT_RENDERER_WORKER_WORKER_THREAD_REGISTRY_H_
+#define CONTENT_RENDERER_WORKER_WORKER_THREAD_REGISTRY_H_
+
+#include <map>
+
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/platform_thread.h"
+#include "content/common/content_export.h"
+#include "content/public/renderer/worker_thread.h"
+
+namespace base {
+class TaskRunner;
+}
+
+namespace content {
+
+class CONTENT_EXPORT WorkerThreadRegistry {
+ public:
+  WorkerThreadRegistry();
+
+  int PostTaskToAllThreads(base::Closure task);
+  static WorkerThreadRegistry* Instance();
+
+  void DidStartCurrentWorkerThread();
+  void WillStopCurrentWorkerThread();
+
+  // Always returns a non-null task runner regardless of whether the
+  // corresponding worker thread is gone or not. If the thread is already gone
+  // the tasks posted onto the task runner will be silently discarded.
+  base::TaskRunner* GetTaskRunnerFor(int worker_id);
+
+ private:
+  friend class WorkerThread;
+  friend class WorkerThreadRegistryTest;
+
+  bool PostTask(int id, base::OnceClosure task);
+
+  using IDToTaskRunnerMap = std::map<base::PlatformThreadId, base::TaskRunner*>;
+
+  ~WorkerThreadRegistry();
+
+  // It is possible for an IPC message to arrive for a worker thread that has
+  // already gone away. In such cases, it is still necessary to provide a
+  // task-runner for that particular thread, because otherwise the message will
+  // end up being handled as per usual in the main-thread, causing incorrect
+  // results. |task_runner_for_dead_worker_| is used to handle such messages,
+  // which silently discards all the tasks it receives.
+  scoped_refptr<base::TaskRunner> task_runner_for_dead_worker_;
+
+  IDToTaskRunnerMap task_runner_map_;
+  base::Lock task_runner_map_lock_;
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_WORKER_WORKER_THREAD_REGISTRY_H_
diff --git a/content/renderer/worker/worker_thread_registry_unittest.cc b/content/renderer/worker/worker_thread_registry_unittest.cc
new file mode 100644
index 0000000..299261fd8
--- /dev/null
+++ b/content/renderer/worker/worker_thread_registry_unittest.cc
@@ -0,0 +1,75 @@
+// 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 "content/renderer/worker/worker_thread_registry.h"
+
+#include "base/logging.h"
+#include "base/test/scoped_task_environment.h"
+#include "content/public/renderer/worker_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace content {
+
+class WorkerThreadRegistryTest : public testing::Test {
+ public:
+  void FakeStart() { task_runner_.DidStartCurrentWorkerThread(); }
+  void FakeStop() { task_runner_.WillStopCurrentWorkerThread(); }
+  WorkerThreadRegistry task_runner_;
+
+ private:
+  base::test::ScopedTaskEnvironment task_environment_;
+};
+
+class MockObserver : public WorkerThread::Observer {
+ public:
+  MOCK_METHOD0(WillStopCurrentWorkerThread, void());
+  void RemoveSelfOnNotify() {
+    ON_CALL(*this, WillStopCurrentWorkerThread())
+        .WillByDefault(testing::Invoke(this, &MockObserver::RemoveSelf));
+  }
+  void RemoveSelf() { WorkerThread::RemoveObserver(this); }
+  WorkerThreadRegistry* runner_;
+};
+
+TEST_F(WorkerThreadRegistryTest, BasicObservingAndWorkerId) {
+  ASSERT_EQ(0, WorkerThread::GetCurrentId());
+  MockObserver o;
+  EXPECT_CALL(o, WillStopCurrentWorkerThread()).Times(1);
+  FakeStart();
+  WorkerThread::AddObserver(&o);
+  ASSERT_LT(0, WorkerThread::GetCurrentId());
+  FakeStop();
+}
+
+TEST_F(WorkerThreadRegistryTest, CanRemoveSelfDuringNotification) {
+  MockObserver o;
+  o.RemoveSelfOnNotify();
+  o.runner_ = &task_runner_;
+  EXPECT_CALL(o, WillStopCurrentWorkerThread()).Times(1);
+  FakeStart();
+  WorkerThread::AddObserver(&o);
+  FakeStop();
+}
+
+TEST_F(WorkerThreadRegistryTest, TaskRunnerRemovedCorrectly) {
+  ASSERT_EQ(0, WorkerThread::GetCurrentId());
+  MockObserver o;
+  FakeStart();
+  WorkerThread::AddObserver(&o);
+  EXPECT_CALL(o, WillStopCurrentWorkerThread()).Times(1);
+
+  // Get the thread id and the task runner for the live thread.
+  int thread_id = WorkerThread::GetCurrentId();
+  base::TaskRunner* task_runner = task_runner_.GetTaskRunnerFor(thread_id);
+  ASSERT_LT(0, thread_id);
+  ASSERT_NE(nullptr, task_runner);
+
+  // Check that the task runner is no longer used after the thread is
+  // terminated.
+  FakeStop();
+  ASSERT_NE(task_runner, task_runner_.GetTaskRunnerFor(thread_id));
+}
+
+}  // namespace content
diff --git a/content/renderer/worker_thread_registry.cc b/content/renderer/worker_thread_registry.cc
deleted file mode 100644
index 1984cd2..0000000
--- a/content/renderer/worker_thread_registry.cc
+++ /dev/null
@@ -1,138 +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 "content/renderer/worker_thread_registry.h"
-
-#include <memory>
-#include <utility>
-
-#include "base/lazy_instance.h"
-#include "base/location.h"
-#include "base/logging.h"
-#include "base/observer_list.h"
-#include "base/single_thread_task_runner.h"
-#include "base/stl_util.h"
-#include "base/threading/thread_local.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "content/public/renderer/worker_thread.h"
-
-namespace content {
-
-namespace {
-
-using WorkerThreadObservers =
-    base::ObserverList<WorkerThread::Observer>::Unchecked;
-using ThreadLocalWorkerThreadObservers =
-    base::ThreadLocalPointer<WorkerThreadObservers>;
-
-// Stores a WorkerThreadObservers instance per thread.
-base::LazyInstance<ThreadLocalWorkerThreadObservers>::DestructorAtExit
-    g_observers_tls = LAZY_INSTANCE_INITIALIZER;
-
-// A task-runner that refuses to run any tasks.
-class DoNothingTaskRunner : public base::TaskRunner {
- public:
-  DoNothingTaskRunner() {}
-
- private:
-  ~DoNothingTaskRunner() override {}
-
-  bool PostDelayedTask(const base::Location& from_here,
-                       base::OnceClosure task,
-                       base::TimeDelta delay) override {
-    return false;
-  }
-
-  bool RunsTasksInCurrentSequence() const override { return false; }
-};
-
-}  // namespace
-
-// WorkerThread implementation:
-
-int WorkerThread::GetCurrentId() {
-  if (!g_observers_tls.Pointer()->Get())
-    return 0;
-  return base::PlatformThread::CurrentId();
-}
-
-void WorkerThread::PostTask(int id, base::OnceClosure task) {
-  WorkerThreadRegistry::Instance()->PostTask(id, std::move(task));
-}
-
-void WorkerThread::AddObserver(Observer* observer) {
-  DCHECK(GetCurrentId() > 0);
-  WorkerThreadObservers* observers = g_observers_tls.Pointer()->Get();
-  DCHECK(observers);
-  observers->AddObserver(observer);
-}
-
-void WorkerThread::RemoveObserver(Observer* observer) {
-  DCHECK(GetCurrentId() > 0);
-  WorkerThreadObservers* observers = g_observers_tls.Pointer()->Get();
-  DCHECK(observers);
-  observers->RemoveObserver(observer);
-}
-
-// WorkerThreadRegistry implementation:
-
-WorkerThreadRegistry::WorkerThreadRegistry()
-    : task_runner_for_dead_worker_(new DoNothingTaskRunner()) {}
-
-int WorkerThreadRegistry::PostTaskToAllThreads(base::Closure closure) {
-  base::AutoLock locker(task_runner_map_lock_);
-  for (const auto& it : task_runner_map_)
-    it.second->PostTask(FROM_HERE, closure);
-  return static_cast<int>(task_runner_map_.size());
-}
-
-WorkerThreadRegistry* WorkerThreadRegistry::Instance() {
-  static base::LazyInstance<WorkerThreadRegistry>::Leaky worker_task_runner =
-      LAZY_INSTANCE_INITIALIZER;
-  return worker_task_runner.Pointer();
-}
-
-WorkerThreadRegistry::~WorkerThreadRegistry() {}
-
-void WorkerThreadRegistry::DidStartCurrentWorkerThread() {
-  DCHECK(!g_observers_tls.Pointer()->Get());
-  DCHECK(!base::PlatformThread::CurrentRef().is_null());
-  g_observers_tls.Pointer()->Set(new WorkerThreadObservers());
-  int id = base::PlatformThread::CurrentId();
-  base::AutoLock locker_(task_runner_map_lock_);
-  task_runner_map_[id] = base::ThreadTaskRunnerHandle::Get().get();
-  CHECK(task_runner_map_[id]);
-}
-
-void WorkerThreadRegistry::WillStopCurrentWorkerThread() {
-  WorkerThreadObservers* observers = g_observers_tls.Pointer()->Get();
-  DCHECK(observers);
-  for (auto& observer : *observers)
-    observer.WillStopCurrentWorkerThread();
-
-  {
-    base::AutoLock locker(task_runner_map_lock_);
-    task_runner_map_.erase(WorkerThread::GetCurrentId());
-  }
-  delete observers;
-  g_observers_tls.Pointer()->Set(nullptr);
-}
-
-base::TaskRunner* WorkerThreadRegistry::GetTaskRunnerFor(int worker_id) {
-  base::AutoLock locker(task_runner_map_lock_);
-  return base::ContainsKey(task_runner_map_, worker_id)
-             ? task_runner_map_[worker_id]
-             : task_runner_for_dead_worker_.get();
-}
-
-bool WorkerThreadRegistry::PostTask(int id, base::OnceClosure closure) {
-  DCHECK(id > 0);
-  base::AutoLock locker(task_runner_map_lock_);
-  auto found = task_runner_map_.find(id);
-  if (found == task_runner_map_.end())
-    return false;
-  return found->second->PostTask(FROM_HERE, std::move(closure));
-}
-
-}  // namespace content
diff --git a/content/renderer/worker_thread_registry.h b/content/renderer/worker_thread_registry.h
deleted file mode 100644
index 7c7d184e..0000000
--- a/content/renderer/worker_thread_registry.h
+++ /dev/null
@@ -1,62 +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 CONTENT_RENDERER_WORKER_THREAD_REGISTRY_H_
-#define CONTENT_RENDERER_WORKER_THREAD_REGISTRY_H_
-
-#include <map>
-
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/platform_thread.h"
-#include "content/common/content_export.h"
-#include "content/public/renderer/worker_thread.h"
-
-namespace base {
-class TaskRunner;
-}
-
-namespace content {
-
-class CONTENT_EXPORT WorkerThreadRegistry {
- public:
-  WorkerThreadRegistry();
-
-  int PostTaskToAllThreads(base::Closure task);
-  static WorkerThreadRegistry* Instance();
-
-  void DidStartCurrentWorkerThread();
-  void WillStopCurrentWorkerThread();
-
-  // Always returns a non-null task runner regardless of whether the
-  // corresponding worker thread is gone or not. If the thread is already gone
-  // the tasks posted onto the task runner will be silently discarded.
-  base::TaskRunner* GetTaskRunnerFor(int worker_id);
-
- private:
-  friend class WorkerThread;
-  friend class WorkerThreadRegistryTest;
-
-  bool PostTask(int id, base::OnceClosure task);
-
-  using IDToTaskRunnerMap = std::map<base::PlatformThreadId, base::TaskRunner*>;
-
-  ~WorkerThreadRegistry();
-
-  // It is possible for an IPC message to arrive for a worker thread that has
-  // already gone away. In such cases, it is still necessary to provide a
-  // task-runner for that particular thread, because otherwise the message will
-  // end up being handled as per usual in the main-thread, causing incorrect
-  // results. |task_runner_for_dead_worker_| is used to handle such messages,
-  // which silently discards all the tasks it receives.
-  scoped_refptr<base::TaskRunner> task_runner_for_dead_worker_;
-
-  IDToTaskRunnerMap task_runner_map_;
-  base::Lock task_runner_map_lock_;
-};
-
-}  // namespace content
-
-#endif  // CONTENT_RENDERER_WORKER_THREAD_REGISTRY_H_
diff --git a/content/renderer/worker_thread_registry_unittest.cc b/content/renderer/worker_thread_registry_unittest.cc
deleted file mode 100644
index 275630a..0000000
--- a/content/renderer/worker_thread_registry_unittest.cc
+++ /dev/null
@@ -1,75 +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 "content/renderer/worker_thread_registry.h"
-
-#include "base/logging.h"
-#include "base/test/scoped_task_environment.h"
-#include "content/public/renderer/worker_thread.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace content {
-
-class WorkerThreadRegistryTest : public testing::Test {
- public:
-  void FakeStart() { task_runner_.DidStartCurrentWorkerThread(); }
-  void FakeStop() { task_runner_.WillStopCurrentWorkerThread(); }
-  WorkerThreadRegistry task_runner_;
-
- private:
-  base::test::ScopedTaskEnvironment task_environment_;
-};
-
-class MockObserver : public WorkerThread::Observer {
- public:
-  MOCK_METHOD0(WillStopCurrentWorkerThread, void());
-  void RemoveSelfOnNotify() {
-    ON_CALL(*this, WillStopCurrentWorkerThread())
-        .WillByDefault(testing::Invoke(this, &MockObserver::RemoveSelf));
-  }
-  void RemoveSelf() { WorkerThread::RemoveObserver(this); }
-  WorkerThreadRegistry* runner_;
-};
-
-TEST_F(WorkerThreadRegistryTest, BasicObservingAndWorkerId) {
-  ASSERT_EQ(0, WorkerThread::GetCurrentId());
-  MockObserver o;
-  EXPECT_CALL(o, WillStopCurrentWorkerThread()).Times(1);
-  FakeStart();
-  WorkerThread::AddObserver(&o);
-  ASSERT_LT(0, WorkerThread::GetCurrentId());
-  FakeStop();
-}
-
-TEST_F(WorkerThreadRegistryTest, CanRemoveSelfDuringNotification) {
-  MockObserver o;
-  o.RemoveSelfOnNotify();
-  o.runner_ = &task_runner_;
-  EXPECT_CALL(o, WillStopCurrentWorkerThread()).Times(1);
-  FakeStart();
-  WorkerThread::AddObserver(&o);
-  FakeStop();
-}
-
-TEST_F(WorkerThreadRegistryTest, TaskRunnerRemovedCorrectly) {
-  ASSERT_EQ(0, WorkerThread::GetCurrentId());
-  MockObserver o;
-  FakeStart();
-  WorkerThread::AddObserver(&o);
-  EXPECT_CALL(o, WillStopCurrentWorkerThread()).Times(1);
-
-  // Get the thread id and the task runner for the live thread.
-  int thread_id = WorkerThread::GetCurrentId();
-  base::TaskRunner* task_runner = task_runner_.GetTaskRunnerFor(thread_id);
-  ASSERT_LT(0, thread_id);
-  ASSERT_NE(nullptr, task_runner);
-
-  // Check that the task runner is no longer used after the thread is
-  // terminated.
-  FakeStop();
-  ASSERT_NE(task_runner, task_runner_.GetTaskRunnerFor(thread_id));
-}
-
-}  // namespace content
diff --git a/content/shell/app/blink_test_platform_support_android.cc b/content/shell/app/blink_test_platform_support_android.cc
index 3029ce6..f0ebb71 100644
--- a/content/shell/app/blink_test_platform_support_android.cc
+++ b/content/shell/app/blink_test_platform_support_android.cc
@@ -4,7 +4,7 @@
 
 #include "content/shell/app/blink_test_platform_support.h"
 
-#include "skia/ext/fontmgr_default_android.h"
+#include "skia/ext/fontmgr_default.h"
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/ports/SkFontMgr_android.h"
 
@@ -42,7 +42,7 @@
   custom.fFallbackFontsXml = kFallbackFontConfig;
   custom.fIsolated = false;
 
-  SetDefaultSkiaFactory(SkFontMgr_New_Android(&custom));
+  skia::OverrideDefaultSkFontMgr(SkFontMgr_New_Android(&custom));
   return true;
 }
 
diff --git a/content/shell/app/blink_test_platform_support_fuchsia.cc b/content/shell/app/blink_test_platform_support_fuchsia.cc
index 3ac315c..c58f70b 100644
--- a/content/shell/app/blink_test_platform_support_fuchsia.cc
+++ b/content/shell/app/blink_test_platform_support_fuchsia.cc
@@ -4,7 +4,7 @@
 
 #include "content/shell/app/blink_test_platform_support.h"
 
-#include "skia/ext/fontmgr_default_android.h"
+#include "skia/ext/fontmgr_default.h"
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/ports/SkFontMgr_android.h"
 
@@ -23,7 +23,7 @@
   custom.fFallbackFontsXml = "/pkg/test_fonts/android_fallback_fonts.xml";
   custom.fIsolated = false;
 
-  SetDefaultSkiaFactory(SkFontMgr_New_Android(&custom));
+  skia::OverrideDefaultSkFontMgr(SkFontMgr_New_Android(&custom));
 
   return true;
 }
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc
index f9b341330..8c3ebba 100644
--- a/content/shell/app/shell_main_delegate.cc
+++ b/content/shell/app/shell_main_delegate.cc
@@ -87,6 +87,7 @@
 
 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
 #include "components/crash/content/app/breakpad_linux.h"
+#include "v8/include/v8-wasm-trap-handler-posix.h"
 #endif
 
 #if defined(OS_FUCHSIA)
@@ -167,7 +168,7 @@
   v8_crashpad_support::SetUp();
 #endif
 #if defined(OS_LINUX)
-  breakpad::SetFirstChanceExceptionHandler(v8::V8::TryHandleSignal);
+  breakpad::SetFirstChanceExceptionHandler(v8::TryHandleWebAssemblyTrapPosix);
 #endif
 #if defined(OS_MACOSX)
   // Needs to happen before InitializeResourceBundle() and before
diff --git a/content/shell/browser/web_test/blink_test_controller.cc b/content/shell/browser/web_test/blink_test_controller.cc
index 6d6d12c8..718be489 100644
--- a/content/shell/browser/web_test/blink_test_controller.cc
+++ b/content/shell/browser/web_test/blink_test_controller.cc
@@ -893,7 +893,7 @@
       const base::Process& process = render_process_host->GetProcess();
       if (process.IsValid()) {
         printer_->AddErrorMessage(std::string("#CRASHED - renderer (pid ") +
-                                  base::IntToString(process.Pid()) + ")");
+                                  base::NumberToString(process.Pid()) + ")");
       } else {
         printer_->AddErrorMessage("#CRASHED - renderer");
       }
diff --git a/content/shell/renderer/shell_content_renderer_client.cc b/content/shell/renderer/shell_content_renderer_client.cc
index c3aa914f..a4aab9c 100644
--- a/content/shell/renderer/shell_content_renderer_client.cc
+++ b/content/shell/renderer/shell_content_renderer_client.cc
@@ -143,7 +143,7 @@
     *error_html =
         "<head><title>Error</title></head><body>Could not load the requested "
         "resource.<br/>Error code: " +
-        base::IntToString(error.reason()) +
+        base::NumberToString(error.reason()) +
         (error.reason() < 0 ? " (" + net::ErrorToString(error.reason()) + ")"
                             : "") +
         "</body>";
@@ -160,7 +160,7 @@
   if (error_html) {
     *error_html =
         "<head><title>Error</title></head><body>Server returned HTTP status " +
-        base::IntToString(http_status) + "</body>";
+        base::NumberToString(http_status) + "</body>";
   }
 }
 
diff --git a/content/shell/test_runner/test_preferences.cc b/content/shell/test_runner/test_preferences.cc
index c7ddc53..5a999fc 100644
--- a/content/shell/test_runner/test_preferences.cc
+++ b/content/shell/test_runner/test_preferences.cc
@@ -29,10 +29,7 @@
   loads_images_automatically = true;
   plugins_enabled = true;
   caret_browsing_enabled = false;
-
-  // Allow those web tests running as local files, i.e. under
-  // web_tests/http/tests/local, to access http server.
-  allow_universal_access_from_file_urls = true;
+  allow_universal_access_from_file_urls = false;
 
 #if defined(OS_MACOSX)
   editing_behavior = WebSettings::EditingBehavior::kMac;
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 1a0934fa..fa4d9f17 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1816,7 +1816,6 @@
     "../renderer/media_recorder/audio_track_recorder_unittest.cc",
     "../renderer/media_recorder/media_recorder_handler_unittest.cc",
     "../renderer/media_recorder/video_track_recorder_unittest.cc",
-    "../renderer/notifications/notification_data_conversions_unittest.cc",
     "../renderer/p2p/filtering_network_manager_unittest.cc",
     "../renderer/p2p/ipc_network_manager_unittest.cc",
     "../renderer/peripheral_content_heuristic_unittest.cc",
@@ -1830,7 +1829,7 @@
     "../renderer/service_worker/service_worker_timeout_timer_unittest.cc",
     "../renderer/skia_benchmarking_extension_unittest.cc",
     "../renderer/v8_value_converter_impl_unittest.cc",
-    "../renderer/worker_thread_registry_unittest.cc",
+    "../renderer/worker/worker_thread_registry_unittest.cc",
     "../test/renderer_audio_output_stream_factory_context_impl_unittest.cc",
     "image_decoder_test.cc",
     "image_decoder_test.h",
diff --git a/content/test/data/accessibility/aria/aria-set-counts-with-hidden-items-expected-blink.txt b/content/test/data/accessibility/aria/aria-set-counts-with-hidden-items-expected-blink.txt
new file mode 100644
index 0000000..a0eefff
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-set-counts-with-hidden-items-expected-blink.txt
@@ -0,0 +1,14 @@
+rootWebArea
+++listBox setSize=4
+++++listBoxOption name='Item 1' setSize=4 posInSet=1 selected=false
+++++listBoxOption name='Item 2' setSize=4 posInSet=2 selected=false
+++++listBoxOption invisible name='Hidden' selected=false
+++++listBoxOption name='Item 3' setSize=4 posInSet=3 selected=false
+++++listBoxOption name='Item 4' setSize=4 posInSet=4 selected=false
+++listBox setSize=5
+++++listBoxOption name='Item 1' setSize=5 posInSet=1 selected=false
+++++listBoxOption name='Item 2' setSize=5 posInSet=2 selected=false
+++++listBoxOption invisible name='Hidden' selected=false
+++++listBoxOption name='Item 3' setSize=5 posInSet=3 selected=false
+++++listBoxOption name='Item 4' setSize=5 posInSet=4 selected=false
+++++listBoxOption name='Item 5' setSize=5 posInSet=5 selected=false
diff --git a/content/test/data/accessibility/aria/aria-set-counts-with-hidden-items.html b/content/test/data/accessibility/aria/aria-set-counts-with-hidden-items.html
new file mode 100644
index 0000000..b87343a
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-set-counts-with-hidden-items.html
@@ -0,0 +1,25 @@
+<!--
+@BLINK-ALLOW:setSize*
+@BLINK-ALLOW:posInSet*
+@BLINK-DENY:setSize=0
+@BLINK-DENY:posInSet=0
+-->
+<html>
+<body>
+<div role="listbox">
+  <div tabIndex="0" aria-setsize="4" aria-posinset="1" role="option">Item 1</div>
+  <div tabIndex="0" aria-setsize="4" aria-posinset="2" role="option">Item 2</div>
+  <div tabIndex="-1" aria-hidden="true" aria-setsize="4" aria-posinset="4" role="option">Hidden</div>
+  <div tabIndex="0" aria-setsize="4" aria-posinset="3" role="option">Item 3</div>
+  <div tabIndex="0" aria-setsize="4" aria-posinset="4" role="option">Item 4</div>
+</div>
+<div role="listbox">
+  <div tabIndex="0" role="option">Item 1</div>
+  <div tabIndex="0" role="option">Item 2</div>
+  <div tabIndex="-1" aria-hidden="true" role="option">Hidden</div>
+  <div tabIndex="0" role="option">Item 3</div>
+  <div tabIndex="0" role="option">Item 4</div>
+  <div tabIndex="0" role="option">Item 5</div>
+</div>
+</body>
+</html>
diff --git a/content/test/data/accessibility/event/tbody-focus-expected-auralinux.txt b/content/test/data/accessibility/event/tbody-focus-expected-auralinux.txt
new file mode 100644
index 0000000..702454d
--- /dev/null
+++ b/content/test/data/accessibility/event/tbody-focus-expected-auralinux.txt
@@ -0,0 +1,4 @@
+FOCUS-EVENT role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+FOCUS-EVENT role=ROLE_PANEL name='tbody' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:FOCUSED:FALSE role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:FOCUSED:TRUE role=ROLE_PANEL name='tbody' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/tbody-focus-expected-mac.txt b/content/test/data/accessibility/event/tbody-focus-expected-mac.txt
new file mode 100644
index 0000000..2b1debd
--- /dev/null
+++ b/content/test/data/accessibility/event/tbody-focus-expected-mac.txt
@@ -0,0 +1 @@
+AXFocusedUIElementChanged on AXGroup AXDescription="tbody"
diff --git a/content/test/data/accessibility/event/tbody-focus-expected-win.txt b/content/test/data/accessibility/event/tbody-focus-expected-win.txt
new file mode 100644
index 0000000..18fdee3
--- /dev/null
+++ b/content/test/data/accessibility/event/tbody-focus-expected-win.txt
@@ -0,0 +1 @@
+EVENT_OBJECT_FOCUS on <tbody#tbody> role=ROLE_SYSTEM_GROUPING name="tbody" FOCUSED,FOCUSABLE
diff --git a/content/test/data/accessibility/event/tbody-focus.html b/content/test/data/accessibility/event/tbody-focus.html
new file mode 100644
index 0000000..1577c91
--- /dev/null
+++ b/content/test/data/accessibility/event/tbody-focus.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<body>
+<table border=1>
+<thead id="thead" tabindex="0" aria-label="thead">
+  <tr>
+    <th>Sum</th>
+    <th>Subtraction</th>
+  </tr>
+</thead>
+<tfoot id="tfoot" tabindex="0" aria-label="tfoot">
+  <tr>
+    <td>12</td>
+     <td>3</td>
+  </tr>
+</tfoot>
+<tbody id="tbody" tabindex="0" aria-label="tbody">
+  <tr>
+    <td>10</td>
+    <td>7</td>
+  </tr>
+  <tr>
+    <td>2</td>
+    <td>4</td>
+  </tr>
+</tbody>
+</table>
+<script>
+  function go() {
+    document.querySelector('tbody').focus();
+  }
+</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/event/tfoot-focus-expected-auralinux.txt b/content/test/data/accessibility/event/tfoot-focus-expected-auralinux.txt
new file mode 100644
index 0000000..c23e086
--- /dev/null
+++ b/content/test/data/accessibility/event/tfoot-focus-expected-auralinux.txt
@@ -0,0 +1,4 @@
+FOCUS-EVENT role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+FOCUS-EVENT role=ROLE_PANEL name='tfoot' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:FOCUSED:FALSE role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:FOCUSED:TRUE role=ROLE_PANEL name='tfoot' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/tfoot-focus-expected-mac.txt b/content/test/data/accessibility/event/tfoot-focus-expected-mac.txt
new file mode 100644
index 0000000..1117641
--- /dev/null
+++ b/content/test/data/accessibility/event/tfoot-focus-expected-mac.txt
@@ -0,0 +1 @@
+AXFocusedUIElementChanged on AXGroup AXDescription="tfoot"
diff --git a/content/test/data/accessibility/event/tfoot-focus-expected-win.txt b/content/test/data/accessibility/event/tfoot-focus-expected-win.txt
new file mode 100644
index 0000000..28e49b9
--- /dev/null
+++ b/content/test/data/accessibility/event/tfoot-focus-expected-win.txt
@@ -0,0 +1 @@
+EVENT_OBJECT_FOCUS on <tfoot#tfoot> role=ROLE_SYSTEM_GROUPING name="tfoot" FOCUSED,FOCUSABLE
diff --git a/content/test/data/accessibility/event/tfoot-focus.html b/content/test/data/accessibility/event/tfoot-focus.html
new file mode 100644
index 0000000..3a03eae
--- /dev/null
+++ b/content/test/data/accessibility/event/tfoot-focus.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<body>
+<table border=1>
+<thead id="thead" tabindex="0" aria-label="thead">
+  <tr>
+    <th>Sum</th>
+    <th>Subtraction</th>
+  </tr>
+</thead>
+<tfoot id="tfoot" tabindex="0" aria-label="tfoot">
+  <tr>
+    <td>12</td>
+     <td>3</td>
+  </tr>
+</tfoot>
+<tbody id="tbody" tabindex="0" aria-label="tbody">
+  <tr>
+    <td>10</td>
+    <td>7</td>
+  </tr>
+  <tr>
+    <td>2</td>
+    <td>4</td>
+  </tr>
+</tbody>
+</table>
+<script>
+  function go() {
+    document.querySelector('tfoot').focus();
+  }
+</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/event/thead-focus-expected-auralinux.txt b/content/test/data/accessibility/event/thead-focus-expected-auralinux.txt
new file mode 100644
index 0000000..4e2777a8
--- /dev/null
+++ b/content/test/data/accessibility/event/thead-focus-expected-auralinux.txt
@@ -0,0 +1,4 @@
+FOCUS-EVENT role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+FOCUS-EVENT role=ROLE_PANEL name='thead' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:FOCUSED:FALSE role=ROLE_DOCUMENT_WEB name='(null)' ENABLED,FOCUSABLE,SENSITIVE,SHOWING,VISIBLE
+STATE-CHANGE:FOCUSED:TRUE role=ROLE_PANEL name='thead' ENABLED,FOCUSABLE,FOCUSED,SENSITIVE,SHOWING,VISIBLE
diff --git a/content/test/data/accessibility/event/thead-focus-expected-mac.txt b/content/test/data/accessibility/event/thead-focus-expected-mac.txt
new file mode 100644
index 0000000..f37da7f
--- /dev/null
+++ b/content/test/data/accessibility/event/thead-focus-expected-mac.txt
@@ -0,0 +1 @@
+AXFocusedUIElementChanged on AXGroup AXDescription="thead"
diff --git a/content/test/data/accessibility/event/thead-focus-expected-win.txt b/content/test/data/accessibility/event/thead-focus-expected-win.txt
new file mode 100644
index 0000000..0bae015f
--- /dev/null
+++ b/content/test/data/accessibility/event/thead-focus-expected-win.txt
@@ -0,0 +1 @@
+EVENT_OBJECT_FOCUS on <thead#thead> role=ROLE_SYSTEM_GROUPING name="thead" FOCUSED,FOCUSABLE
diff --git a/content/test/data/accessibility/event/thead-focus.html b/content/test/data/accessibility/event/thead-focus.html
new file mode 100644
index 0000000..a0d4b34
--- /dev/null
+++ b/content/test/data/accessibility/event/thead-focus.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<body>
+<table border=1>
+<thead id="thead" tabindex="0" aria-label="thead">
+  <tr>
+    <th>Sum</th>
+    <th>Subtraction</th>
+  </tr>
+</thead>
+<tfoot id="tfoot" tabindex="0" aria-label="tfoot">
+  <tr>
+    <td>12</td>
+     <td>3</td>
+  </tr>
+</tfoot>
+<tbody id="tbody" tabindex="0" aria-label="tbody">
+  <tr>
+    <td>10</td>
+    <td>7</td>
+  </tr>
+  <tr>
+    <td>2</td>
+    <td>4</td>
+  </tr>
+</tbody>
+</table>
+<script>
+  function go() {
+    document.querySelector('thead').focus();
+  }
+</script>
+</body>
+</html>
diff --git a/content/test/data/accessibility/html/table-focusable-sections-expected-android.txt b/content/test/data/accessibility/html/table-focusable-sections-expected-android.txt
new file mode 100644
index 0000000..f3ad78a
--- /dev/null
+++ b/content/test/data/accessibility/html/table-focusable-sections-expected-android.txt
@@ -0,0 +1,17 @@
+android.webkit.WebView focusable focused scrollable name='Table example - focusable thead, tbody, tfoot'
+++android.widget.GridView role_description='table' collection row_count=4 column_count=2
+++++android.view.View clickable focusable
+++++++android.view.View
+++++++++android.view.View role_description='column header' collection_item heading name='Sum' row_span=1 column_span=1
+++++++++android.view.View role_description='column header' collection_item heading name='Subtraction' row_span=1 column_index=1 column_span=1
+++++android.view.View clickable focusable
+++++++android.view.View
+++++++++android.view.View collection_item name='10' row_index=1 row_span=1 column_span=1
+++++++++android.view.View collection_item name='7' row_index=1 row_span=1 column_index=1 column_span=1
+++++++android.view.View
+++++++++android.view.View collection_item name='2' row_index=2 row_span=1 column_span=1
+++++++++android.view.View collection_item name='4' row_index=2 row_span=1 column_index=1 column_span=1
+++++android.view.View clickable focusable
+++++++android.view.View
+++++++++android.view.View collection_item name='12' row_index=3 row_span=1 column_span=1
+++++++++android.view.View collection_item name='3' row_index=3 row_span=1 column_index=1 column_span=1
diff --git a/content/test/data/accessibility/html/table-focusable-sections-expected-auralinux.txt b/content/test/data/accessibility/html/table-focusable-sections-expected-auralinux.txt
new file mode 100644
index 0000000..514a10a
--- /dev/null
+++ b/content/test/data/accessibility/html/table-focusable-sections-expected-auralinux.txt
@@ -0,0 +1,25 @@
+[document web] name='Table example - focusable thead, tbody, tfoot'
+++[table]
+++++[panel]
+++++++[table row]
+++++++++[column header] name='Sum'
+++++++++++[text] name='Sum'
+++++++++[column header] name='Subtraction'
+++++++++++[text] name='Subtraction'
+++++[panel]
+++++++[table row]
+++++++++[table cell] name='10'
+++++++++++[text] name='10'
+++++++++[table cell] name='7'
+++++++++++[text] name='7'
+++++++[table row]
+++++++++[table cell] name='2'
+++++++++++[text] name='2'
+++++++++[table cell] name='4'
+++++++++++[text] name='4'
+++++[panel]
+++++++[table row]
+++++++++[table cell] name='12'
+++++++++++[text] name='12'
+++++++++[table cell] name='3'
+++++++++++[text] name='3'
diff --git a/content/test/data/accessibility/html/table-focusable-sections-expected-blink.txt b/content/test/data/accessibility/html/table-focusable-sections-expected-blink.txt
new file mode 100644
index 0000000..4f01368
--- /dev/null
+++ b/content/test/data/accessibility/html/table-focusable-sections-expected-blink.txt
@@ -0,0 +1,33 @@
+rootWebArea name='Table example - focusable thead, tbody, tfoot'
+++table
+++++group
+++++++row
+++++++++columnHeader name='Sum'
+++++++++++staticText name='Sum'
+++++++++++++inlineTextBox name='Sum'
+++++++++columnHeader name='Subtraction'
+++++++++++staticText name='Subtraction'
+++++++++++++inlineTextBox name='Subtraction'
+++++group
+++++++row
+++++++++cell name='10'
+++++++++++staticText name='10'
+++++++++++++inlineTextBox name='10'
+++++++++cell name='7'
+++++++++++staticText name='7'
+++++++++++++inlineTextBox name='7'
+++++++row
+++++++++cell name='2'
+++++++++++staticText name='2'
+++++++++++++inlineTextBox name='2'
+++++++++cell name='4'
+++++++++++staticText name='4'
+++++++++++++inlineTextBox name='4'
+++++group
+++++++row
+++++++++cell name='12'
+++++++++++staticText name='12'
+++++++++++++inlineTextBox name='12'
+++++++++cell name='3'
+++++++++++staticText name='3'
+++++++++++++inlineTextBox name='3'
diff --git a/content/test/data/accessibility/html/table-focusable-sections-expected-mac.txt b/content/test/data/accessibility/html/table-focusable-sections-expected-mac.txt
new file mode 100644
index 0000000..c4f1d1d
--- /dev/null
+++ b/content/test/data/accessibility/html/table-focusable-sections-expected-mac.txt
@@ -0,0 +1,28 @@
+AXWebArea AXTitle='Table example - focusable thead, tbody, tfoot'
+++AXTable
+++++AXGroup AXDescription='Sum Subtraction'
+++++++AXRow AXIndex='0'
+++++++++AXCell AXColumnIndexRange={"len":1,"loc":0} AXRowIndexRange={"len":1,"loc":0}
+++++++++++AXStaticText AXValue='Sum'
+++++++++AXCell AXColumnIndexRange={"len":1,"loc":1} AXRowIndexRange={"len":1,"loc":0}
+++++++++++AXStaticText AXValue='Subtraction'
+++++AXGroup AXDescription='10 7 2 4'
+++++++AXRow AXIndex='1'
+++++++++AXCell AXColumnIndexRange={"len":1,"loc":0} AXRowIndexRange={"len":1,"loc":1}
+++++++++++AXStaticText AXValue='10'
+++++++++AXCell AXColumnIndexRange={"len":1,"loc":1} AXRowIndexRange={"len":1,"loc":1}
+++++++++++AXStaticText AXValue='7'
+++++++AXRow AXIndex='2'
+++++++++AXCell AXColumnIndexRange={"len":1,"loc":0} AXRowIndexRange={"len":1,"loc":2}
+++++++++++AXStaticText AXValue='2'
+++++++++AXCell AXColumnIndexRange={"len":1,"loc":1} AXRowIndexRange={"len":1,"loc":2}
+++++++++++AXStaticText AXValue='4'
+++++AXGroup AXDescription='12 3'
+++++++AXRow AXIndex='3'
+++++++++AXCell AXColumnIndexRange={"len":1,"loc":0} AXRowIndexRange={"len":1,"loc":3}
+++++++++++AXStaticText AXValue='12'
+++++++++AXCell AXColumnIndexRange={"len":1,"loc":1} AXRowIndexRange={"len":1,"loc":3}
+++++++++++AXStaticText AXValue='3'
+++++AXColumn AXIndex='0'
+++++AXColumn AXIndex='1'
+++++AXGroup
diff --git a/content/test/data/accessibility/html/table-focusable-sections-expected-win.txt b/content/test/data/accessibility/html/table-focusable-sections-expected-win.txt
new file mode 100644
index 0000000..9f9a759
--- /dev/null
+++ b/content/test/data/accessibility/html/table-focusable-sections-expected-win.txt
@@ -0,0 +1,25 @@
+ROLE_SYSTEM_DOCUMENT name='Table example - focusable thead, tbody, tfoot' READONLY FOCUSABLE
+++ROLE_SYSTEM_TABLE
+++++ROLE_SYSTEM_GROUPING FOCUSABLE
+++++++ROLE_SYSTEM_ROW
+++++++++ROLE_SYSTEM_COLUMNHEADER name='Sum'
+++++++++++ROLE_SYSTEM_STATICTEXT name='Sum'
+++++++++ROLE_SYSTEM_COLUMNHEADER name='Subtraction'
+++++++++++ROLE_SYSTEM_STATICTEXT name='Subtraction'
+++++ROLE_SYSTEM_GROUPING FOCUSABLE
+++++++ROLE_SYSTEM_ROW
+++++++++ROLE_SYSTEM_CELL name='10'
+++++++++++ROLE_SYSTEM_STATICTEXT name='10'
+++++++++ROLE_SYSTEM_CELL name='7'
+++++++++++ROLE_SYSTEM_STATICTEXT name='7'
+++++++ROLE_SYSTEM_ROW
+++++++++ROLE_SYSTEM_CELL name='2'
+++++++++++ROLE_SYSTEM_STATICTEXT name='2'
+++++++++ROLE_SYSTEM_CELL name='4'
+++++++++++ROLE_SYSTEM_STATICTEXT name='4'
+++++ROLE_SYSTEM_GROUPING FOCUSABLE
+++++++ROLE_SYSTEM_ROW
+++++++++ROLE_SYSTEM_CELL name='12'
+++++++++++ROLE_SYSTEM_STATICTEXT name='12'
+++++++++ROLE_SYSTEM_CELL name='3'
+++++++++++ROLE_SYSTEM_STATICTEXT name='3'
diff --git a/content/test/data/accessibility/html/table-focusable-sections.html b/content/test/data/accessibility/html/table-focusable-sections.html
new file mode 100644
index 0000000..8c20fce
--- /dev/null
+++ b/content/test/data/accessibility/html/table-focusable-sections.html
@@ -0,0 +1,37 @@
+<!--
+@MAC-ALLOW:AXIndex=*
+@MAC-ALLOW:AXColumnIndexRange=*
+@MAC-ALLOW:AXRowIndexRange=*
+-->
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Table example - focusable thead, tbody, tfoot</title>
+</head>
+<body>
+<table border=1>
+<thead tabindex="0">
+  <tr>
+    <th>Sum</th>
+    <th>Subtraction</th>
+  </tr>
+</thead>
+<tfoot tabindex="0">
+  <tr>
+    <td>12</td>
+     <td>3</td>
+  </tr>
+</tfoot>
+<tbody tabindex="0">
+  <tr>
+    <td>10</td>
+    <td>7</td>
+  </tr>
+  <tr>
+    <td>2</td>
+    <td>4</td>
+  </tr>
+</tbody>
+</table>
+</body>
+</html>
diff --git a/content/test/data/fuzzer_corpus/http_structured_header_data/4.txt b/content/test/data/fuzzer_corpus/http_structured_header_data/4.txt
new file mode 100644
index 0000000..9d0aa44
--- /dev/null
+++ b/content/test/data/fuzzer_corpus/http_structured_header_data/4.txt
@@ -0,0 +1 @@
+Accept;text/plain;text/html, Accept-Encoding;gzip;br, Accept-Language;en;fr
diff --git a/content/test/data/fuzzer_corpus/http_structured_header_data/5.txt b/content/test/data/fuzzer_corpus/http_structured_header_data/5.txt
new file mode 100644
index 0000000..449a0dfa
--- /dev/null
+++ b/content/test/data/fuzzer_corpus/http_structured_header_data/5.txt
@@ -0,0 +1 @@
+text/plain;gzip;en, "text/html;charset=utf8";gzip;en, "*/*";"identity";"*"
diff --git a/content/test/fuzzer/http_structured_header_fuzzer.cc b/content/test/fuzzer/http_structured_header_fuzzer.cc
index b7aea79..5779a33 100644
--- a/content/test/fuzzer/http_structured_header_fuzzer.cc
+++ b/content/test/fuzzer/http_structured_header_fuzzer.cc
@@ -9,6 +9,8 @@
 
 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
   base::StringPiece input(reinterpret_cast<const char*>(data), size);
+  ParseItem(input);
+  ParseListOfLists(input);
   ParseParameterisedList(input);
   return 0;
 }
diff --git a/content/test/gpu/gpu_tests/gpu_integration_test.py b/content/test/gpu/gpu_tests/gpu_integration_test.py
index 4e578de..39c01712 100644
--- a/content/test/gpu/gpu_tests/gpu_integration_test.py
+++ b/content/test/gpu/gpu_tests/gpu_integration_test.py
@@ -13,6 +13,13 @@
 
 _START_BROWSER_RETRIES = 3
 
+# Please expand the following lists when we expand to new bot configs.
+_SUPPORTED_WIN_VERSIONS = ['win7', 'win10']
+_SUPPORTED_WIN_VERSIONS_WITH_DIRECT_COMPOSITION = ['win10']
+_SUPPORTED_WIN_GPU_VENDORS = [0x8086, 0x10de, 0x1002]
+_SUPPORTED_WIN_INTEL_GPUS = [0x5912]
+_SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS = [0x5912]
+_SUPPORTED_WIN_INTEL_GPUS_WITH_NV12_OVERLAYS = [0x5912]
 
 class GpuIntegrationTest(
     serially_executed_browser_test_case.SeriallyExecutedBrowserTestCase):
@@ -254,6 +261,50 @@
     """
     raise NotImplementedError
 
+  def GetOverlayBotConfig(self):
+    """Returns expected bot config for DirectComposition and overlay support.
+
+    This is only meaningful on Windows platform.
+
+    The rules to determine bot config are:
+      1) Only win10 or newer supports DirectComposition
+      2) Only Intel supports hardware overlays with DirectComposition
+      3) Currently the Win/Intel GPU bot supports YUY2 and NV12 overlays
+    """
+    if self.browser is None:
+      raise Exception("Browser doesn't exist")
+    system_info = self.browser.GetSystemInfo()
+    if system_info is None:
+      raise Exception("Browser doesn't support GetSystemInfo")
+    gpu = system_info.gpu.devices[0]
+    if gpu is None:
+      raise Exception("System Info doesn't have a gpu")
+    gpu_vendor_id = gpu.vendor_id
+    gpu_device_id = gpu.device_id
+    os_version = self.browser.platform.GetOSVersionName()
+    if os_version is None:
+      raise Exception("browser.platform.GetOSVersionName() returns None")
+    os_version = os_version.lower()
+
+    config = {
+      'direct_composition': False,
+      'supports_overlays': False,
+      'overlay_cap_yuy2': 'NONE',
+      'overlay_cap_nv12': 'NONE',
+    }
+    assert os_version in _SUPPORTED_WIN_VERSIONS
+    assert gpu_vendor_id in _SUPPORTED_WIN_GPU_VENDORS
+    if os_version in _SUPPORTED_WIN_VERSIONS_WITH_DIRECT_COMPOSITION:
+      config['direct_composition'] = True
+      if gpu_vendor_id == 0x8086:
+        config['supports_overlays'] = True
+        assert gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS
+        if gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS:
+          config['overlay_cap_yuy2'] = 'SCALING'
+        if gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS_WITH_NV12_OVERLAYS:
+          config['overlay_cap_nv12'] = 'SCALING'
+    return config
+
   @classmethod
   def GetExpectations(cls):
     if not cls._cached_expectations:
diff --git a/content/test/gpu/gpu_tests/info_collection_test.py b/content/test/gpu/gpu_tests/info_collection_test.py
index 7f39ed3..931b3220 100644
--- a/content/test/gpu/gpu_tests/info_collection_test.py
+++ b/content/test/gpu/gpu_tests/info_collection_test.py
@@ -7,14 +7,6 @@
 
 import sys
 
-# Please expand the following lists when we expand to new bot configs.
-_SUPPORTED_WIN_VERSIONS = ['win7', 'win10']
-_SUPPORTED_WIN_VERSIONS_WITH_DIRECT_COMPOSITION = ['win10']
-_SUPPORTED_WIN_GPU_VENDORS = [0x8086, 0x10de, 0x1002]
-_SUPPORTED_WIN_INTEL_GPUS = [0x5912]
-_SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS = [0x5912]
-_SUPPORTED_WIN_INTEL_GPUS_WITH_NV12_OVERLAYS = [0x5912]
-
 # There are no expectations for info_collection
 class InfoCollectionExpectations(GpuTestExpectations):
   def SetExpectations(self):
@@ -43,32 +35,6 @@
     cls.CustomizeBrowserArgs([])
     cls.StartBrowser()
 
-  def _GetOverlayExpectations(self, os_version, gpu_vendor_id, gpu_device_id):
-    # The rules to set up per bot expectations are:
-    #  1) Only win10 or newer supports DirectComposition
-    #  2) Only Intel supports hardware overlays with DirectComposition
-    #  3) Currently the Win/Intel GPU bot supports YUY2 and NV12 overlays
-    expectations = {
-      'direct_composition': False,
-      'supports_overlays': False,
-      'overlay_cap_yuy2': 'NONE',
-      'overlay_cap_nv12': 'NONE',
-    }
-    assert os_version is not None
-    os_version = os_version.lower()
-    assert os_version in _SUPPORTED_WIN_VERSIONS
-    assert gpu_vendor_id in _SUPPORTED_WIN_GPU_VENDORS
-    if os_version in _SUPPORTED_WIN_VERSIONS_WITH_DIRECT_COMPOSITION:
-      expectations['direct_composition'] = True
-      if gpu_vendor_id == 0x8086:
-        expectations['supports_overlays'] = True
-        assert gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS
-        if gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS_WITH_YUY2_OVERLAYS:
-          expectations['overlay_cap_yuy2'] = 'SCALING'
-        if gpu_device_id in _SUPPORTED_WIN_INTEL_GPUS_WITH_NV12_OVERLAYS:
-          expectations['overlay_cap_nv12'] = 'SCALING'
-    return expectations
-
   def RunActualGpuTest(self, test_path, *args):
     # Make sure the GPU process is started
     self.tab.action_runner.Navigate('chrome:gpu')
@@ -103,15 +69,13 @@
 
     os_name = self.browser.platform.GetOSName()
     if os_name and os_name.lower() == 'win':
-      expectations = self._GetOverlayExpectations(
-          self.browser.platform.GetOSVersionName(),
-          detected_vendor_id, detected_device_id)
+      overlay_bot_config = self.GetOverlayBotConfig()
 
       aux_attributes = system_info.gpu.aux_attributes
       if not aux_attributes:
         self.fail('GPU info does not have aux_attributes.')
 
-      for (field, expected) in expectations.iteritems():
+      for field, expected in overlay_bot_config.iteritems():
         detected = aux_attributes.get(field, 'NONE')
         if  expected != detected:
           self.fail('%s mismatch, expected %s but got %s.' %
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index 265c0c13..4d97744 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -10,7 +10,8 @@
   """
   def __init__(self, url, name, test_rect, revision,
                tolerance=2, browser_args=None, expected_colors=None,
-               gpu_process_disabled=False, optional_action=None):
+               gpu_process_disabled=False, optional_action=None,
+               other_args=None):
     super(PixelTestPage, self).__init__()
     self.url = url
     self.name = name
@@ -35,6 +36,8 @@
     # action here is "CrashGpuProcess" then it would be defined in a
     # "_CrashGpuProcess" method in PixelIntegrationTest.
     self.optional_action = optional_action
+    # Whatever other settings a test need to specify.
+    self.other_args = other_args
 
   def CopyWithNewBrowserArgsAndSuffix(self, browser_args, suffix):
     return PixelTestPage(
@@ -1269,8 +1272,8 @@
           'size': [55, 110],
           'color': [12, 12, 255],
           'tolerance': tolerance_dc
-        }
-      ]),
+        }],
+      other_args={'video_is_rotated': True}),
 
     PixelTestPage(
       'pixel_video_mp4_four_colors_rot_180.html',
@@ -1306,8 +1309,8 @@
           'size': [110, 57],
           'color': [255, 255, 15],
           'tolerance': tolerance_dc
-        }
-      ]),
+        }],
+      other_args={'video_is_rotated': True}),
 
     PixelTestPage(
       'pixel_video_mp4_four_colors_rot_270.html',
@@ -1357,8 +1360,8 @@
           'size': [55, 110],
           'color': [255, 17, 24],
           'tolerance': tolerance_dc
-        }
-      ]),
+        }],
+      other_args={'video_is_rotated': True}),
 
     PixelTestPage(
       'pixel_video_vp9.html',
@@ -1526,6 +1529,6 @@
           'size': [65, 30],
           'color': [44, 255, 16],
           'tolerance': tolerance_dc
-        }
-      ]),
+        }],
+      other_args={'video_is_rotated': True}),
     ]
diff --git a/content/test/gpu/gpu_tests/trace_integration_test.py b/content/test/gpu/gpu_tests/trace_integration_test.py
index 2d2a6c95..bb5310e 100644
--- a/content/test/gpu/gpu_tests/trace_integration_test.py
+++ b/content/test/gpu/gpu_tests/trace_integration_test.py
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import logging
 import os
 import sys
 
@@ -13,9 +14,6 @@
 from telemetry.timeline import model as model_module
 from telemetry.timeline import tracing_config
 
-TOPLEVEL_SERVICE_CATEGORY = 'disabled-by-default-gpu.service'
-TOPLEVEL_DEVICE_CATEGORY = 'disabled-by-default-gpu.device'
-
 gpu_relative_path = "content/test/data/gpu/"
 
 data_paths = [os.path.join(
@@ -50,6 +48,49 @@
   window.domAutomationController = domAutomationController;
 """
 
+basic_test_harness_script = r"""
+  var domAutomationController = {};
+
+  domAutomationController._proceed = false;
+
+  domAutomationController._readyForActions = false;
+  domAutomationController._succeeded = false;
+  domAutomationController._finished = false;
+
+  domAutomationController.send = function(msg) {
+    domAutomationController._proceed = true;
+    let lmsg = msg.toLowerCase();
+    if (lmsg == "ready") {
+      domAutomationController._readyForActions = true;
+    } else {
+      domAutomationController._finished = true;
+      if (lmsg == "success") {
+        domAutomationController._succeeded = true;
+      } else {
+        domAutomationController._succeeded = false;
+      }
+    }
+  }
+
+  window.domAutomationController = domAutomationController;
+"""
+
+# Presentation mode enums match DXGI_FRAME_PRESENTATION_MODE
+_SWAP_CHAIN_PRESENTATION_MODE_COMPOSED = 0
+_SWAP_CHAIN_PRESENTATION_MODE_OVERLAY = 1
+_SWAP_CHAIN_PRESENTATION_MODE_NONE = 2
+_SWAP_CHAIN_PRESENTATION_MODE_COMPOSITION_FAILURE = 3
+
+# Pixel format enums match OverlayFormat in config/gpu/gpu_info.h
+_SWAP_CHAIN_PIXEL_FORMAT_BGRA = 0
+_SWAP_CHAIN_PIXEL_FORMAT_YUY2 = 1
+_SWAP_CHAIN_PIXEL_FORMAT_NV12 = 2
+
+_TEST_MAX_REPEATS = 30
+
+_TEST_DONE = 0 # Test finished, either failed or succeeded
+_TEST_REPEAT = 1 # Test failed, but it's flaky and should run again.
+
 class TraceIntegrationTest(gpu_integration_test.GpuIntegrationTest):
   """Tests GPU traces are plumbed through properly.
 
@@ -67,17 +108,31 @@
     for p in pixel_test_pages.DefaultPages('TraceTest'):
       yield (p.name, gpu_relative_path + p.url,
              {'browser_args': [],
-              'category': TOPLEVEL_SERVICE_CATEGORY,
+              'category': cls._DisabledByDefaultTraceCategory('gpu.service'),
               'test_harness_script': webgl_test_harness_script,
               'finish_js_condition': 'domAutomationController._finished',
-              'expected_event_args': {'gl_category': 'gpu_toplevel'}})
+              'success_eval_func': 'CheckGLCategory'})
     for p in pixel_test_pages.DefaultPages('DeviceTraceTest'):
       yield (p.name, gpu_relative_path + p.url,
              {'browser_args': [],
-              'category': TOPLEVEL_DEVICE_CATEGORY,
+              'category': cls._DisabledByDefaultTraceCategory('gpu.device'),
               'test_harness_script': webgl_test_harness_script,
               'finish_js_condition': 'domAutomationController._finished',
-              'expected_event_args': {'gl_category': 'gpu_toplevel'}})
+              'success_eval_func': 'CheckGLCategory'})
+    for p in pixel_test_pages.DirectCompositionPages('VideoTraceTest'):
+      success_eval_func = 'CheckVideoMode'
+      if (p.other_args is not None and
+          p.other_args.get('video_is_rotated', False)):
+        # On several Intel GPUs we tested that support hardware overlays,
+        # none of them promote a swap chain to hardware overlay if there is
+        # rotation.
+        success_eval_func = 'CheckVideoModeNoOverlay'
+      yield (p.name, gpu_relative_path + p.url,
+             {'browser_args': p.browser_args,
+              'category': cls._DisabledByDefaultTraceCategory('gpu.service'),
+              'test_harness_script': basic_test_harness_script,
+              'finish_js_condition': 'domAutomationController._finished',
+              'success_eval_func': success_eval_func})
 
   def RunActualGpuTest(self, test_path, *args):
     test_params = args[0]
@@ -85,55 +140,51 @@
     assert 'category' in test_params
     assert 'test_harness_script' in test_params
     assert 'finish_js_condition' in test_params
-    assert 'expected_event_args' in test_params
     browser_args = test_params['browser_args']
     category = test_params['category']
     test_harness_script = test_params['test_harness_script']
     finish_js_condition = test_params['finish_js_condition']
-    expected_event_args = test_params['expected_event_args']
+    success_eval_func = test_params['success_eval_func']
 
-    # The version of this test in the old GPU test harness restarted
-    # the browser after each test, so continue to do that to match its
-    # behavior.
-    self.RestartBrowserWithArgs(self._AddDefaultArgs(browser_args))
+    # Maximum repeat a flaky test 30 times
+    for ii in range(_TEST_MAX_REPEATS):
+      if ii > 0:
+        logging.info('Try the test again: #%d', ii + 1)
+      # The version of this test in the old GPU test harness restarted
+      # the browser after each test, so continue to do that to match its
+      # behavior.
+      self.RestartBrowserWithArgs(self._AddDefaultArgs(browser_args))
 
-    # Set up tracing.
-    config = tracing_config.TracingConfig()
-    config.chrome_trace_config.category_filter.AddExcludedCategory('*')
-    config.chrome_trace_config.category_filter.AddDisabledByDefault(category)
-    config.enable_chrome_trace = True
-    tab = self.tab
-    tab.browser.platform.tracing_controller.StartTracing(config, 60)
+      # Set up tracing.
+      config = tracing_config.TracingConfig()
+      config.chrome_trace_config.category_filter.AddExcludedCategory('*')
+      config.chrome_trace_config.category_filter.AddDisabledByDefault(category)
+      config.enable_chrome_trace = True
+      tab = self.tab
+      tab.browser.platform.tracing_controller.StartTracing(config, 60)
 
-    # Perform page navigation.
-    url = self.UrlOfStaticFilePath(test_path)
-    tab.Navigate(url, script_to_evaluate_on_commit=test_harness_script)
-    tab.action_runner.WaitForJavaScriptCondition(
-      finish_js_condition, timeout=30)
+      # Perform page navigation.
+      url = self.UrlOfStaticFilePath(test_path)
+      tab.Navigate(url, script_to_evaluate_on_commit=test_harness_script)
+      tab.action_runner.WaitForJavaScriptCondition(
+          finish_js_condition, timeout=30)
 
-    # Stop tracing.
-    timeline_data = tab.browser.platform.tracing_controller.StopTracing()[0]
+      # Stop tracing.
+      timeline_data = tab.browser.platform.tracing_controller.StopTracing()[0]
 
-    # Evaluate success.
-    timeline_model = model_module.TimelineModel(timeline_data)
-    event_iter = timeline_model.IterAllEvents(
-        event_type_predicate=timeline_model.IsSliceOrAsyncSlice)
-    self._EvaluateSuccess(event_iter, category, expected_event_args)
-
-  def _EvaluateSuccess(self, event_iterator, expected_category_name,
-                       expected_event_args):
-    for event in event_iterator:
-      if event.category != expected_category_name:
-        continue
-      for arg_name, arg_value in expected_event_args.iteritems():
-        if event.args.get(arg_name, None) != arg_value:
-          break
-      else:
-        print 'Found event with category name ' + expected_category_name
+      # Evaluate success.
+      timeline_model = model_module.TimelineModel(timeline_data)
+      event_iter = timeline_model.IterAllEvents(
+          event_type_predicate=timeline_model.IsSliceOrAsyncSlice)
+      test_result = _TEST_DONE
+      if success_eval_func:
+        prefixed_func_name = '_EvaluateSuccess_' + success_eval_func
+        test_result = getattr(self, prefixed_func_name)(category, event_iter)
+        assert test_result in [_TEST_DONE, _TEST_REPEAT]
+      if test_result == _TEST_DONE:
         break
     else:
-      self.fail('Trace markers for GPU category were not found: %s' %
-                expected_category_name)
+      self.fail('Test failed all %d tries' % _TEST_MAX_REPEATS)
 
   @classmethod
   def _CreateExpectations(cls):
@@ -154,6 +205,162 @@
       '--enable-logging',
       '--enable-experimental-web-platform-features'] + browser_args
 
+  def _GetOverlayBotConfigHelper(self):
+    system_info = self.browser.GetSystemInfo()
+    if not system_info:
+      raise Exception("Browser doesn't support GetSystemInfo")
+    gpu = system_info.gpu.devices[0]
+    if not gpu:
+      raise Exception("System Info doesn't have a gpu")
+    os_version_name = self.browser.platform.GetOSVersionName()
+    return self.GetOverlayBotConfig(
+        os_version_name, gpu.vendor_id, gpu.device_id)
+
+  @staticmethod
+  def _SwapChainPixelFormatToStr(pixel_format):
+    if pixel_format == _SWAP_CHAIN_PIXEL_FORMAT_BGRA:
+      return 'BGRA'
+    if pixel_format == _SWAP_CHAIN_PIXEL_FORMAT_YUY2:
+      return 'YUY2'
+    if pixel_format == _SWAP_CHAIN_PIXEL_FORMAT_NV12:
+      return 'NV12'
+    return str(pixel_format)
+
+  @staticmethod
+  def _SwapChainPixelFormatListToStr(pixel_format_list):
+    assert len(pixel_format_list) > 0
+    list_str = None
+    for pixel_format in pixel_format_list:
+      format_str = TraceIntegrationTest._SwapChainPixelFormatToStr(pixel_format)
+      if list_str is not None:
+        list_str = '%s, %s' % (list_str, format_str)
+      else:
+        list_str = format_str
+    return '[%s]' % list_str
+
+  @staticmethod
+  def _SwapChainPresentationModeToStr(presentation_mode):
+    if presentation_mode == _SWAP_CHAIN_PRESENTATION_MODE_COMPOSED:
+      return 'COMPOSED'
+    if presentation_mode == _SWAP_CHAIN_PRESENTATION_MODE_OVERLAY:
+      return 'OVERLAY'
+    if presentation_mode == _SWAP_CHAIN_PRESENTATION_MODE_NONE:
+      return 'NONE'
+    if presentation_mode == _SWAP_CHAIN_PRESENTATION_MODE_COMPOSITION_FAILURE:
+      return 'COMPOSITION_FAILURE'
+    return str(presentation_mode)
+
+  @staticmethod
+  def _SwapChainPresentationModeListToStr(presentation_mode_list):
+    assert len(presentation_mode_list) > 0
+    list_str = None
+    for mode in presentation_mode_list:
+      mode_str = TraceIntegrationTest._SwapChainPresentationModeToStr(mode)
+      if list_str is not None:
+        list_str = '%s, %s' % (list_str, mode_str)
+      else:
+        list_str = mode_str
+    return '[%s]' % list_str
+
+  @staticmethod
+  def _DisabledByDefaultTraceCategory(category):
+    return 'disabled-by-default-%s' % category
+
+  #########################################
+  # The test success evaluation functions
+
+  def _EvaluateSuccess_CheckGLCategory(self, category, event_iterator):
+    for event in event_iterator:
+      if (event.category == category and
+          event.args.get('gl_category', None) == 'gpu_toplevel'):
+        break
+    else:
+      self.fail('Trace markers for GPU category %s were not found' % category)
+    return _TEST_DONE
+
+  def _EvaluateSuccess_CheckVideoModeNoOverlay(self, category, event_iterator):
+    return self._CheckVideoModeHelper(category, event_iterator, no_overlay=True)
+
+  def _EvaluateSuccess_CheckVideoMode(self, category, event_iterator):
+    return self._CheckVideoModeHelper(category, event_iterator,
+                                      no_overlay=False)
+
+  def _CheckVideoModeHelper(self, category, event_iterator, no_overlay):
+    os_name = self.browser.platform.GetOSName()
+    assert os_name and os_name.lower() == 'win'
+
+    overlay_bot_config = self.GetOverlayBotConfig()
+    if overlay_bot_config is None:
+      self.fail('Overlay bot config can not be determined')
+    assert overlay_bot_config.get('direct_composition', False)
+
+    expected_pixel_format = _SWAP_CHAIN_PIXEL_FORMAT_NV12
+    expected_presentation_mode = _SWAP_CHAIN_PRESENTATION_MODE_COMPOSED
+    if overlay_bot_config.get('supports_overlays', False):
+      supports_yuy2 = False
+      supports_nv12 = False
+      if overlay_bot_config.get('overlay_cap_yuy2', 'NONE') != 'NONE':
+        supports_yuy2 = True
+      if overlay_bot_config.get('overlay_cap_nv12', 'NONE') != 'NONE':
+        supports_nv12 = True
+      assert supports_yuy2 or supports_nv12
+      if not no_overlay:
+        expected_presentation_mode = _SWAP_CHAIN_PRESENTATION_MODE_OVERLAY
+      if not supports_nv12:
+        expected_pixel_format = _SWAP_CHAIN_PIXEL_FORMAT_YUY2
+
+    pixel_format_history = []
+    presentation_mode_history = []
+    previous_time = None
+    invalid_info_encountered = False
+    for event in event_iterator:
+      if event.category != category:
+        continue
+      if event.name == 'SwapChainFrameInfoInvalid':
+        error_code = event.args.get('ErrorCode', None)
+        if error_code is None:
+          self.fail('ErrorCode is missing from SwapChainFrameInfoInvalid event')
+        invalid_info_encountered = True
+        logging.info('Swap chain presentation stats collection failed: %s',
+                     hex(error_code))
+        continue
+      if event.name != 'SwapChainFrameInfo':
+        continue
+      if previous_time is not None:
+        # Sanity check that events are chronically sorted
+        assert previous_time < event.start
+      pixel_format = event.args.get('SwapChain.PixelFormat', None)
+      presentation_mode = event.args.get('SwapChain.PresentationMode', None)
+      if pixel_format is None or presentation_mode is None:
+        self.fail('PixelFormat or PresentationMode is missing from event')
+      pixel_format_history.append(pixel_format)
+      presentation_mode_history.append(presentation_mode)
+      previous_time = event.start
+
+    if len(pixel_format_history) == 0 or len(presentation_mode_history) == 0:
+      # In theory 'supports_overlays' needs to be true to trigger a fail, but
+      # all DirectComposition test pages run with commandline switch
+      # --enable-direct-composition-layers.
+      if invalid_info_encountered:
+        return _TEST_REPEAT
+      self.fail('Trace markers of name SwapChainFrameInfo were not found')
+
+    # The last relevant event is selected.
+    if expected_pixel_format != pixel_format_history[-1]:
+      self.fail('SwapChain pixel format mismatch, expected %s got %s' %
+          (TraceIntegrationTest._SwapChainPixelFormatToStr(
+               expected_pixel_format),
+           TraceIntegrationTest._SwapChainPixelFormatListToStr(
+               pixel_format_history)))
+    if expected_presentation_mode != presentation_mode_history[-1]:
+      self.fail('SwapChain presentation mode mismatch, expected %s got %s' %
+          (TraceIntegrationTest._SwapChainPresentationModeToStr(
+               expected_presentation_mode),
+           TraceIntegrationTest._SwapChainPresentationModeListToStr(
+               presentation_mode_history)))
+    return _TEST_DONE
+
+
 def load_tests(loader, tests, pattern):
   del loader, tests, pattern  # Unused.
   return gpu_integration_test.LoadAllTestsInModule(sys.modules[__name__])
diff --git a/content/test/gpu/gpu_tests/trace_test_expectations.py b/content/test/gpu/gpu_tests/trace_test_expectations.py
index 1c02a70f..a21d620 100644
--- a/content/test/gpu/gpu_tests/trace_test_expectations.py
+++ b/content/test/gpu/gpu_tests/trace_test_expectations.py
@@ -21,3 +21,12 @@
     # context loss which results in hardware decoder loss.
     self.Skip('*_Video_Context_Loss_MP4', ['android'], bug=580386)
 
+    # Skip on platforms where DirectComposition isn't supported
+    self.Skip('VideoTraceTest_*',
+        ['mac', 'linux', 'android', 'chromeos', 'win7'], bug=867136)
+
+    self.Flaky('VideoTraceTest_DirectComposition_ComplexOverlays',
+        ['win10'], bug=928006)
+
+    self.Flaky('VideoTraceTest_DirectComposition_Nonroot',
+        ['win10'], bug=928166)
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 9e67639..3b25fbe 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -855,7 +855,7 @@
 
     self.Fail('conformance2/renderbuffers/' +
         'multisampled-depth-renderbuffer-initialization.html',
-        ['mac', 'intel'], bug=731877)
+        ['sierra', 'intel'], bug=731877)
 
     self.Fail('conformance/rendering/rendering-stencil-large-viewport.html',
         ['mac', 'intel'], bug=782317)
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index e663228..1a9f34d 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -647,9 +647,10 @@
     self.Skip('conformance/textures/video/' +
         'tex-2d-rgb-rgb-unsigned_byte.html',
         ['android', 'android-webview-instrumentation'], bug=352645)
+    # Also flaky on nexus9 non-webview (bug=834933) but can't specify both.
     self.Skip('conformance/textures/video/' +
         'tex-2d-rgb-rgb-unsigned_short_5_6_5.html',
-        ['android', 'android-webview-instrumentation'], bug=352645)
+        ['android'], bug=352645)
     self.Skip('conformance/textures/video/' +
         'tex-2d-rgba-rgba-unsigned_byte.html',
         ['android', 'android-webview-instrumentation'], bug=352645)
@@ -788,6 +789,9 @@
     self.Flaky('conformance/textures/image_bitmap_from_video/' +
         'tex-2d-rgb-rgb-unsigned_byte.html',
         ['android', ('qualcomm', 'Adreno (TM) 418'), 'no_angle'], bug=716496)
+    self.Fail('conformance/textures/image_bitmap_from_video/' +
+        'tex-2d-alpha-alpha-unsigned_byte.html',
+        ['android'], bug=928257)
     self.Skip('conformance/uniforms/uniform-samplers-test.html',
         ['android', ('qualcomm', 'Adreno (TM) 418')],
         bug=610951)
diff --git a/content/test/test_blink_web_unit_test_support.cc b/content/test/test_blink_web_unit_test_support.cc
index 1027934..cf6940e0 100644
--- a/content/test/test_blink_web_unit_test_support.cc
+++ b/content/test/test_blink_web_unit_test_support.cc
@@ -18,6 +18,7 @@
 #include "build/build_config.h"
 #include "cc/trees/layer_tree_settings.h"
 #include "content/app/mojo/mojo_init.h"
+#include "content/child/child_process.h"
 #include "content/public/common/service_names.mojom.h"
 #include "content/renderer/loader/web_data_consumer_handle_impl.h"
 #include "content/renderer/loader/web_url_loader_impl.h"
@@ -288,6 +289,12 @@
   return blink::WebString::FromASCII("en-US");
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+TestBlinkWebUnitTestSupport::GetIOTaskRunner() const {
+  return ChildProcess::current() ? ChildProcess::current()->io_task_runner()
+                                 : nullptr;
+}
+
 blink::WebURLLoaderMockFactory*
 TestBlinkWebUnitTestSupport::GetURLLoaderMockFactory() {
   return url_loader_factory_.get();
diff --git a/content/test/test_blink_web_unit_test_support.h b/content/test/test_blink_web_unit_test_support.h
index 44e7e95..b83e010f 100644
--- a/content/test/test_blink_web_unit_test_support.h
+++ b/content/test/test_blink_web_unit_test_support.h
@@ -49,6 +49,7 @@
       const blink::WebString& value1,
       const blink::WebString& value2) override;
   blink::WebString DefaultLocale() override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() const override;
 
   blink::WebURLLoaderMockFactory* GetURLLoaderMockFactory() override;
 
diff --git a/dbus/message.cc b/dbus/message.cc
index 7409033..ebdfe9d9 100644
--- a/dbus/message.cc
+++ b/dbus/message.cc
@@ -263,26 +263,50 @@
 }
 
 bool Message::SetDestination(const std::string& destination) {
+  if (!dbus_validate_bus_name(destination.c_str(), nullptr)) {
+    LOG(ERROR) << "Invalid bus name: " << destination;
+    return false;
+  }
   return dbus_message_set_destination(raw_message_, destination.c_str());
 }
 
 bool Message::SetPath(const ObjectPath& path) {
+  if (!path.IsValid()) {
+    LOG(ERROR) << "Invalid path: " << path.value();
+    return false;
+  }
   return dbus_message_set_path(raw_message_, path.value().c_str());
 }
 
 bool Message::SetInterface(const std::string& interface) {
+  if (!dbus_validate_interface(interface.c_str(), nullptr)) {
+    LOG(ERROR) << "Invalid interface: " << interface;
+    return false;
+  }
   return dbus_message_set_interface(raw_message_, interface.c_str());
 }
 
 bool Message::SetMember(const std::string& member) {
+  if (!dbus_validate_member(member.c_str(), nullptr)) {
+    LOG(ERROR) << "Invalid member: " << member;
+    return false;
+  }
   return dbus_message_set_member(raw_message_, member.c_str());
 }
 
 bool Message::SetErrorName(const std::string& error_name) {
+  if (!dbus_validate_error_name(error_name.c_str(), nullptr)) {
+    LOG(ERROR) << "Invalid error name: " << error_name;
+    return false;
+  }
   return dbus_message_set_error_name(raw_message_, error_name.c_str());
 }
 
 bool Message::SetSender(const std::string& sender) {
+  if (!dbus_validate_bus_name(sender.c_str(), nullptr)) {
+    LOG(ERROR) << "Invalid bus name: " << sender;
+    return false;
+  }
   return dbus_message_set_sender(raw_message_, sender.c_str());
 }
 
diff --git a/device/bluetooth/bluetooth_device.h b/device/bluetooth/bluetooth_device.h
index 87ec108..fb48054 100644
--- a/device/bluetooth/bluetooth_device.h
+++ b/device/bluetooth/bluetooth_device.h
@@ -611,7 +611,7 @@
 
   // Helper class to easily update the sets of UUIDs and keep them in sync with
   // the set of all the device's UUIDs.
-  class DeviceUUIDs {
+  class DEVICE_BLUETOOTH_EXPORT DeviceUUIDs {
    public:
     DeviceUUIDs();
     ~DeviceUUIDs();
diff --git a/device/bluetooth/test/fake_peripheral.cc b/device/bluetooth/test/fake_peripheral.cc
index 311740b5..7e52f80 100644
--- a/device/bluetooth/test/fake_peripheral.cc
+++ b/device/bluetooth/test/fake_peripheral.cc
@@ -4,11 +4,13 @@
 
 #include "device/bluetooth/test/fake_peripheral.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/memory/ptr_util.h"
 #include "base/memory/weak_ptr.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "device/bluetooth/bluetooth_uuid.h"
 #include "device/bluetooth/test/fake_remote_gatt_service.h"
@@ -36,7 +38,20 @@
 }
 
 void FakePeripheral::SetServiceUUIDs(UUIDSet service_uuids) {
-  service_uuids_ = std::move(service_uuids);
+  device::BluetoothDevice::GattServiceMap gatt_services;
+  bool inserted;
+
+  // Create a temporary map of services, because ReplaceServiceUUIDs expects a
+  // GattServiceMap even though it only uses the UUIDs.
+  int count = 0;
+  for (const auto& uuid : service_uuids) {
+    std::string id = base::IntToString(count++);
+    std::tie(std::ignore, inserted) =
+        gatt_services.emplace(id, std::make_unique<FakeRemoteGattService>(
+                                      id, uuid, /*is_primary=*/true, this));
+    DCHECK(inserted);
+  }
+  device_uuids_.ReplaceServiceUUIDs(gatt_services);
 }
 
 void FakePeripheral::SetNextGATTConnectionResponse(uint16_t code) {
@@ -67,6 +82,7 @@
   // for more details.
   system_connected_ = false;
   gatt_connected_ = false;
+  device_uuids_.ClearServiceUUIDs();
   SetGattServicesDiscoveryComplete(false);
   DidDisconnectGatt();
 }
@@ -181,10 +197,6 @@
   return false;
 }
 
-device::BluetoothDevice::UUIDSet FakePeripheral::GetUUIDs() const {
-  return service_uuids_;
-}
-
 bool FakePeripheral::ExpectingPinCode() const {
   NOTREACHED();
   return false;
@@ -329,6 +341,7 @@
 
   pending_gatt_discovery_ = false;
   if (code == mojom::kHCISuccess) {
+    device_uuids_.ReplaceServiceUUIDs(gatt_services_);
     SetGattServicesDiscoveryComplete(true);
     GetAdapter()->NotifyGattServicesDiscovered(this);
   } else {
diff --git a/device/bluetooth/test/fake_peripheral.h b/device/bluetooth/test/fake_peripheral.h
index 8494216..797cf79 100644
--- a/device/bluetooth/test/fake_peripheral.h
+++ b/device/bluetooth/test/fake_peripheral.h
@@ -81,7 +81,6 @@
   bool IsGattConnected() const override;
   bool IsConnectable() const override;
   bool IsConnecting() const override;
-  UUIDSet GetUUIDs() const override;
   bool ExpectingPinCode() const override;
   bool ExpectingPasskey() const override;
   bool ExpectingConfirmation() const override;
@@ -130,7 +129,6 @@
 
   const std::string address_;
   base::Optional<std::string> name_;
-  UUIDSet service_uuids_;
   // True when the system has connected to the device outside of the Bluetooth
   // interface e.g. the user connected to the device through system settings.
   bool system_connected_;
diff --git a/docs/android_accessing_cpp_enums_in_java.md b/docs/android_accessing_cpp_enums_in_java.md
index fb3382e..8743ca6 100644
--- a/docs/android_accessing_cpp_enums_in_java.md
+++ b/docs/android_accessing_cpp_enums_in_java.md
@@ -40,7 +40,7 @@
 
 2. Add a new build target
 
-    ```
+    ```gn
     import("//build/config/android/rules.gni")
 
     java_cpp_enum("foo_generated_enum") {
@@ -52,7 +52,7 @@
 
 3. Add the new target to the desired android_library targets srcjar_deps:
 
-    ```
+    ```gn
     android_library("base_java") {
       srcjar_deps = [
         ":foo_generated_enum",
@@ -85,7 +85,7 @@
 
 * Handling long package names:
 
-    ```
+    ```cpp
     // GENERATED_JAVA_ENUM_PACKAGE: (
     //   org.chromium.chrome.this.package.is.too.long.to.fit.on.a.single.line)
     ```
@@ -93,27 +93,33 @@
 * Enum entries
     * Single line enums should look like this:
 
-            // GENERATED_JAVA_ENUM_PACKAGE: org.foo
-            enum NotificationActionType { BUTTON, TEXT };
+        ```cpp
+        // GENERATED_JAVA_ENUM_PACKAGE: org.foo
+        enum NotificationActionType { BUTTON, TEXT };
+        ```
 
     * Multi-line enums should have one enum entry per line, like this:
 
-            // GENERATED_JAVA_ENUM_PACKAGE: org.foo
-            enum NotificationActionType {
-              BUTTON,
-              TEXT
-            };
+        ```cpp
+        // GENERATED_JAVA_ENUM_PACKAGE: org.foo
+        enum NotificationActionType {
+          BUTTON,
+          TEXT
+        };
+        ```
 
     * Multi-line enum entries are allowed but should be formatted like this:
 
-            // GENERATED_JAVA_ENUM_PACKAGE: org.foo
-            enum NotificationActionType {
-              LongKeyNumberOne,
-              LongKeyNumberTwo,
-              ...
-              LongKeyNumberThree =
-                  LongKeyNumberOne | LongKeyNumberTwo | ...
-            };
+        ```cpp
+        // GENERATED_JAVA_ENUM_PACKAGE: org.foo
+        enum NotificationActionType {
+          LongKeyNumberOne,
+          LongKeyNumberTwo,
+          ...
+          LongKeyNumberThree =
+              LongKeyNumberOne | LongKeyNumberTwo | ...
+        };
+        ```
 
 * Preserving comments
 
diff --git a/docs/android_build_instructions.md b/docs/android_build_instructions.md
index 9d39141..b18614b6 100644
--- a/docs/android_build_instructions.md
+++ b/docs/android_build_instructions.md
@@ -397,7 +397,7 @@
 
 To avoid typing `_incremental` when building targets, you can use the GN arg:
 
-```
+```gn
 incremental_apk_by_default = true
 ```
 
diff --git a/docs/android_logging.md b/docs/android_logging.md
index 255eb072..8e9c4b2 100644
--- a/docs/android_logging.md
+++ b/docs/android_logging.md
@@ -1,16 +1,17 @@
-# Logging #
+# Logging
 
 [TOC]
 
 
 ## Overview
 
-Logging used to be done using Android's [android.util.Log]
-(https://developer.android.com/reference/android/util/Log.html).
+Logging used to be done using Android's
+[android.util.Log](https://developer.android.com/reference/android/util/Log.html).
 
-A wrapper on that is now available: org.chromium.base.Log. It is designed to
-write logs as belonging to logical groups going beyond single classes, and to
-make it easy to switch logging on or off for individual groups.
+A wrapper on that is now available:
+[org.chromium.base.Log](/base/android/java/src/org/chromium/base/Log.java). It
+is designed to write logs as belonging to logical groups going beyond single
+classes, and to make it easy to switch logging on or off for individual groups.
 
 Usage:
 
@@ -30,9 +31,9 @@
 
 Here, **TAG** will be a feature or package name, "MediaRemote" or "NFC" for
 example. In most cases, the class name is not needed. It will be prepended by
-the "cr_" prefix to make obvious which logs are coming from Chrome.
+the "cr\_" prefix to make obvious which logs are coming from Chrome.
 
-### Verbose and Debug logs have special handling ###
+### Verbose and Debug logs have special handling
 
 *   `Log.v` and `Log.d` Calls made using `org.chromium.base.Log` are stripped
     out of production binaries using Proguard. There is no way to get those logs
@@ -41,9 +42,9 @@
 *   The file name and line number will be prepended to the log message.
     For higher priority logs, those are not added for performance concerns.
 
-### An exception trace is printed when the exception is the last parameter ###
+### An exception trace is printed when the exception is the last parameter
 
-As with `java.util.Log`, putting a throwable as last parameter will dump the
+As with `android.util.Log`, putting a throwable as last parameter will dump the
 corresponding stack trace:
 
 ```java
@@ -224,6 +225,6 @@
 @Before
 public void setUp() {
   ShadowLog.stream = System.out;
-  //you other setup here
+  // Your other setup here
 }
 ```
diff --git a/docs/android_test_instructions.md b/docs/android_test_instructions.md
index 6276cc9b..26ed2c7b 100644
--- a/docs/android_test_instructions.md
+++ b/docs/android_test_instructions.md
@@ -60,7 +60,7 @@
 which states, _Google may regularly check installed apps for potentially harmful
 behavior._ This can interfere with the test runner. To disable this dialog, run:
 
-```
+```shell
 adb shell settings put global package_verifier_enable 0
 ```
 
diff --git a/docs/linux_eclipse_dev.md b/docs/linux_eclipse_dev.md
index a3fe4090..47556aae 100644
--- a/docs/linux_eclipse_dev.md
+++ b/docs/linux_eclipse_dev.md
@@ -20,7 +20,7 @@
 
 ### Get & Configure Eclipse
 
-Eclipse 4.3 (Kepler) is known to work with Chromium for Linux.
+Eclipse 4.6.1 (Neon) is known to work with Chromium for Linux.
 
 *   [Download](http://www.eclipse.org/downloads/) the distribution appropriate
     for your OS. For example, for Linux 64-bit/Java 64-bit, use the Linux 64 bit
@@ -166,6 +166,15 @@
 Now the indexer will find many more include files, regardless of which approach
 you take below.
 
+Eclipse will still complain about unresolved includes or invalid declarations
+(semantic errors or code analysis errors in the ```Problems``` tab),
+which you can set eclipse to ignore:
+
+1.  Right-click on "src" and select "Properties..."
+    * Open C++ General > Code Analysis
+    * Change the severity from ```Error``` to ```Warning``` for each of the
+    settings that you want eclipse to ignore.
+
 #### Optional: Manual header paths and symbols
 
 You can manually tell Eclipse where to find header files, which will allow it to
@@ -255,6 +264,18 @@
 You can also drag the toolbar to the bottom of your window to save vertical
 space.
 
+### Optional: Running inside eclipse
+
+Running inside eclipse is fairly straightforward:
+
+1. Create a ```C/C++ Application```:
+     1. ```Run``` > ```Run configurations```
+     2. Double click on ```C/C++ Application```
+     3. Pick a  name (e.g. ```shell```)
+     4. Point to ```C/C++ Application```
+        (e.g. ```src/out/Default/content_shell```)
+     6. Click ```Debug``` to run the program.
+
 ### Optional: Debugging
 
 1.  From the toolbar at the top, click the arrow next to the debug icon and
@@ -269,6 +290,46 @@
 1.  Set a breakpoint somewhere in your code and click the debug icon to start
     debugging.
 
+#### Multi-process debugging
+
+If you set breakpoints and your debugger session doesn't stop it is because
+both ```chrome``` and ```content_shell ``` spawn sub-processes.
+To debug, you need to attach a debugger to one of those sub-processes.
+
+Eclipse can attach automatically to forked processes
+(Run -> Debug configurations -> Debugger tab), but that doesn't seem to
+work well.
+
+The overall idea is described [here](https://www.chromium.org/blink/getting-started-with-blink-debugging)
+, but one way to accomplish this in eclipse is to run two ```Debug configurations```:
+
+1. Create a ```C/C++ Application```:
+     1. ```Run``` > ```Debug configurations```
+     2. Double click on ```C/C++ Application```
+     3. Pick a  name (e.g. ```shell```)
+     4. Point to ```C/C++ Application```
+        (e.g. ```src/out/Default/content_shell```)
+     5. In the arguments tab, add the following the to program arguments:
+        ```--no-sandbox --renderer-startup-dialog test.html```
+     6. Click ```Debug``` to run the program.
+     7. That will run the application and it will stop with a message like the
+         following:
+       ```Renderer (239930) paused waiting for debugger to attach. Send SIGUSR1 to unpause.```
+     9. ```239930``` is the number of the process running waiting for the ```signal```.
+2. Create a ```C/C++ Attach to Application```:
+     1. ```Run``` > ```Debug configurations```
+     2. Double click on ```C/C++ Attach to Application```
+    2. Pick  a name (e.g. ```shell proc```)
+    3. Click ```Debug``` to run the configuration.
+    4. In the ```Select Processes``` dialog, pick the process that was
+        spawned above (if you type ```content_shell``` it will filter by
+        name)
+    5. Click on ```Debugger console``` to access the ```gdb``` console.
+    6. Send the original process a signal
+        ```signal SIGUSR1```
+    7. That should unblock the original process and you should now be able to
+        set breakpoints.
+
 ### Optional: Accurate symbol information
 
 If setup properly, Eclipse can do a great job of semantic navigation of C++ code
@@ -391,3 +452,5 @@
     is helpful:
 1.  For improved performance, I use medium-granularity projects (eg. one for
     WebKit/Source) instead of putting all of 'src/' in one project.
+1. Running [```content_shell```](https://www.chromium.org/developers/content-module)
+   as opposed to all of ```chrome```  is a lot faster/smaller.
diff --git a/docs/speed/microbenchmark_regressions.md b/docs/speed/microbenchmark_regressions.md
index f2f7f89e..31f49f5 100644
--- a/docs/speed/microbenchmark_regressions.md
+++ b/docs/speed/microbenchmark_regressions.md
@@ -2,8 +2,25 @@
 
 [TOC]
 
+##  Microbenchmarks
+
+A microbenchmark attempts to measure the performance of a "small" bit of code.
+These tests are typically in the sub-millisecond range. The code being tested
+usually performs no I/O, or else is a test of some single, specific I/O task.
+[1]
+
+We maintain and measure a set of such microbenchmarks for example
+[blink_perf](./benchmark/harnesses/blink_perf.md), dromaeo, gpu_perftests, and
+rasterize_and_record_micro.
+
+[1] https://github.com/google/caliper/wiki/JavaMicrobenchmarks#what-is-a-microbenchmark
+
 ## Interesting sources of false positives
 
+Due to their nature microbenchmarks are much more sensitive to changes in the
+underlying abstraction levels which can result in a large number of false
+positive alerts. Below are some of such changes relevant to Chromium.
+
 ### AFDO rolls.
 
 Automatic Feedback Directed Optimization is a process that produces
diff --git a/docs/updating_clang.md b/docs/updating_clang.md
index 1a160f7..da5b757d 100644
--- a/docs/updating_clang.md
+++ b/docs/updating_clang.md
@@ -31,10 +31,6 @@
         gsutil.py cp -n -a public-read gs://chromium-browser-clang-staging/$x/llvm-code-coverage-$rev.tgz \
             gs://chromium-browser-clang/$x/llvm-code-coverage-$rev.tgz ; \
         done
-    $ for x in Linux_x64 Mac ; do \
-        gsutil.py cp -n -a public-read gs://chromium-browser-clang-staging/$x/llvmstrip-$rev.tgz \
-            gs://chromium-browser-clang/$x/llvmstrip-$rev.tgz ; \
-        done
     $ gsutil.py cp -n -a public-read gs://chromium-browser-clang-staging/Mac/lld-$rev.tgz \
           gs://chromium-browser-clang/Mac/lld-$rev.tgz
     ```
diff --git a/extensions/browser/api/feedback_private/feedback_private_api.cc b/extensions/browser/api/feedback_private/feedback_private_api.cc
index 4e40552..e6a1a9a 100644
--- a/extensions/browser/api/feedback_private/feedback_private_api.cc
+++ b/extensions/browser/api/feedback_private/feedback_private_api.cc
@@ -30,10 +30,7 @@
 #include "extensions/common/constants.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/public/interfaces/assistant_controller.mojom.h"
-#include "ash/public/interfaces/constants.mojom.h"
 #include "extensions/browser/api/feedback_private/log_source_access_manager.h"
-#include "services/service_manager/public/cpp/connector.h"
 #endif  // defined(OS_CHROMEOS)
 
 using extensions::api::feedback_private::SystemInformation;
@@ -323,16 +320,11 @@
   }
 
 #if defined(OS_CHROMEOS)
-  // Send feedback to Assistant server if triggered from Google Assistant.
-  if (feedback_info.from_assistant && *feedback_info.from_assistant) {
-    ash::mojom::AssistantControllerPtr assistant_controller;
-    content::BrowserContext::GetConnectorFor(browser_context())
-        ->BindInterface(ash::mojom::kServiceName, &assistant_controller);
-    assistant_controller->SendAssistantFeedback(
-        feedback_info.assistant_debug_info_allowed &&
-            *feedback_info.assistant_debug_info_allowed,
-        feedback_data->description());
-  }
+  feedback_data->set_from_assistant(feedback_info.from_assistant &&
+                                    *feedback_info.from_assistant);
+  feedback_data->set_assistant_debug_info_allowed(
+      feedback_info.assistant_debug_info_allowed &&
+      *feedback_info.assistant_debug_info_allowed);
 
   delegate->FetchAndMergeIwlwifiDumpLogsIfPresent(
       std::move(sys_logs), browser_context(),
diff --git a/extensions/browser/api/feedback_private/feedback_service.cc b/extensions/browser/api/feedback_private/feedback_service.cc
index 4329df0..1912141e 100644
--- a/extensions/browser/api/feedback_private/feedback_service.cc
+++ b/extensions/browser/api/feedback_private/feedback_service.cc
@@ -16,6 +16,13 @@
 #include "extensions/browser/extensions_browser_client.h"
 #include "net/base/network_change_notifier.h"
 
+#if defined(OS_CHROMEOS)
+#include "ash/public/interfaces/assistant_controller.mojom.h"
+#include "ash/public/interfaces/constants.mojom.h"
+#include "extensions/browser/api/feedback_private/log_source_access_manager.h"
+#include "services/service_manager/public/cpp/connector.h"
+#endif  // defined(OS_CHROMEOS)
+
 using content::BrowserThread;
 using feedback::FeedbackData;
 
@@ -95,6 +102,20 @@
 
   if (screenshot_completed && attached_file_completed) {
     VLOG(1) << "Attachments are ready.";
+#if defined(OS_CHROMEOS)
+    // Send feedback to Assistant server if triggered from Google Assistant.
+    if (feedback_data->from_assistant()) {
+      ash::mojom::AssistantControllerPtr assistant_controller;
+      content::BrowserContext::GetConnectorFor(browser_context_)
+          ->BindInterface(ash::mojom::kServiceName, &assistant_controller);
+      assistant_controller->SendAssistantFeedback(
+          feedback_data->assistant_debug_info_allowed(),
+          feedback_data->description(),
+          (feedback_data->image() != nullptr) ? *(feedback_data->image())
+                                              : std::string());
+    }
+#endif
+
     // Signal the feedback object that the data from the feedback page has been
     // filled - the object will manage sending of the actual report.
     feedback_data->OnFeedbackPageDataComplete();
diff --git a/extensions/browser/api/web_request/web_request_api.cc b/extensions/browser/api/web_request/web_request_api.cc
index 71e2c99c..1a07827e 100644
--- a/extensions/browser/api/web_request/web_request_api.cc
+++ b/extensions/browser/api/web_request/web_request_api.cc
@@ -145,6 +145,14 @@
     keys::kOnHeadersReceivedEvent,
 };
 
+// List of all webRequest events that support extraHeaders in the extraInfoSpec.
+const char* const kWebRequestExtraHeadersEventNames[] = {
+    keys::kOnBeforeSendHeadersEvent, keys::kOnSendHeadersEvent,
+    keys::kOnHeadersReceivedEvent,   keys::kOnAuthRequiredEvent,
+    keys::kOnResponseStartedEvent,   keys::kOnBeforeRedirectEvent,
+    keys::kOnCompletedEvent,
+};
+
 // User data key for WebRequestAPI::ProxySet.
 const void* const kWebRequestProxySetUserDataKey =
     &kWebRequestProxySetUserDataKey;
@@ -984,7 +992,8 @@
   }
   request_time_tracker_->LogRequestStartTime(
       request->id, base::TimeTicks::Now(), has_listener,
-      HasExtraHeadersListener(browser_context, extension_info_map, request));
+      HasExtraHeadersListenerForRequest(browser_context, extension_info_map,
+                                        request));
 
   const bool is_incognito_context = IsIncognitoBrowserContext(browser_context);
 
@@ -1726,18 +1735,12 @@
   callbacks_for_page_load_.push_back(callback);
 }
 
-bool ExtensionWebRequestEventRouter::HasExtraHeadersListener(
+bool ExtensionWebRequestEventRouter::HasExtraHeadersListenerForRequest(
     void* browser_context,
     const extensions::InfoMap* extension_info_map,
     const WebRequestInfo* request) {
-  static const char* kEventNames[] = {
-      keys::kOnBeforeSendHeadersEvent, keys::kOnSendHeadersEvent,
-      keys::kOnHeadersReceivedEvent,   keys::kOnAuthRequiredEvent,
-      keys::kOnResponseStartedEvent,   keys::kOnBeforeRedirectEvent,
-      keys::kOnCompletedEvent,
-  };
   int extra_info_spec = 0;
-  for (const char* name : kEventNames) {
+  for (const char* name : kWebRequestExtraHeadersEventNames) {
     GetMatchingListeners(browser_context, extension_info_map, name, request,
                          &extra_info_spec);
     if (extra_info_spec & ExtraInfoSpec::EXTRA_HEADERS)
@@ -1746,6 +1749,30 @@
   return false;
 }
 
+bool ExtensionWebRequestEventRouter::HasAnyExtraHeadersListener(
+    void* browser_context) {
+  if (HasAnyExtraHeadersListenerImpl(browser_context))
+    return true;
+
+  void* cross_browser_context = GetCrossBrowserContext(browser_context);
+  if (cross_browser_context)
+    return HasAnyExtraHeadersListenerImpl(cross_browser_context);
+
+  return false;
+}
+
+bool ExtensionWebRequestEventRouter::HasAnyExtraHeadersListenerImpl(
+    void* browser_context) {
+  for (const char* name : kWebRequestExtraHeadersEventNames) {
+    const Listeners& listeners = listeners_[browser_context][name];
+    for (const auto& listener : listeners) {
+      if (listener->extra_info_spec & ExtraInfoSpec::EXTRA_HEADERS)
+        return true;
+    }
+  }
+  return false;
+}
+
 bool ExtensionWebRequestEventRouter::IsPageLoad(
     const WebRequestInfo& request) const {
   return request.type == content::RESOURCE_TYPE_MAIN_FRAME;
diff --git a/extensions/browser/api/web_request/web_request_api.h b/extensions/browser/api/web_request/web_request_api.h
index 8d28864..ec3a75e 100644
--- a/extensions/browser/api/web_request/web_request_api.h
+++ b/extensions/browser/api/web_request/web_request_api.h
@@ -479,9 +479,14 @@
 
   // Whether there is a listener matching the request that has
   // ExtraInfoSpec::EXTRA_HEADERS set.
-  bool HasExtraHeadersListener(void* browser_context,
-                               const extensions::InfoMap* extension_info_map,
-                               const WebRequestInfo* request);
+  bool HasExtraHeadersListenerForRequest(
+      void* browser_context,
+      const extensions::InfoMap* extension_info_map,
+      const WebRequestInfo* request);
+
+  // Whether there are any listeners for this context that have
+  // ExtraInfoSpec::EXTRA_HEADERS set.
+  bool HasAnyExtraHeadersListener(void* browser_context);
 
  private:
   friend class WebRequestAPI;
@@ -698,6 +703,9 @@
   // Returns true if |request| was already signaled to some event handlers.
   bool WasSignaled(const WebRequestInfo& request) const;
 
+  // Helper for |HasAnyExtraHeadersListener()|.
+  bool HasAnyExtraHeadersListenerImpl(void* browser_context);
+
   // Get the number of listeners - for testing only.
   size_t GetListenerCountForTesting(void* browser_context,
                                     const std::string& event_name);
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
index e2d7cc9f..9e5b6f7 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -22,6 +22,22 @@
 
 namespace extensions {
 
+struct WebRequestProxyingURLLoaderFactory::InProgressRequest::
+    PendingBeforeSendHeadersData {
+  PendingBeforeSendHeadersData(const net::HttpRequestHeaders& headers_,
+                               OnBeforeSendHeadersCallback callback_)
+      : headers(headers_), callback(std::move(callback_)) {}
+  PendingBeforeSendHeadersData(PendingBeforeSendHeadersData&&) = default;
+
+  ~PendingBeforeSendHeadersData() {
+    if (callback)
+      std::move(callback).Run(net::ERR_ABORTED, base::nullopt);
+  }
+
+  net::HttpRequestHeaders headers;
+  OnBeforeSendHeadersCallback callback;
+};
+
 WebRequestProxyingURLLoaderFactory::InProgressRequest::InProgressRequest(
     WebRequestProxyingURLLoaderFactory* factory,
     uint64_t request_id,
@@ -42,6 +58,9 @@
       proxied_loader_binding_(this, std::move(loader_request)),
       target_client_(std::move(client)),
       proxied_client_binding_(this),
+      has_any_extra_headers_listeners_(
+          ExtensionWebRequestEventRouter::GetInstance()
+              ->HasAnyExtraHeadersListener(factory_->browser_context_)),
       weak_factory_(this) {
   // If there is a client error, clean up the request.
   target_client_.set_connection_error_handler(base::BindOnce(
@@ -69,6 +88,7 @@
 
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::Restart() {
   request_completed_ = false;
+  network_request_started_ = false;
   // Derive a new WebRequestInfo value any time |Restart()| is called, because
   // the details in |request_| may have changed e.g. if we've been redirected.
   info_.emplace(
@@ -78,17 +98,18 @@
       routing_id_, factory_->resource_context_, request_,
       !(options_ & network::mojom::kURLLoadOptionSynchronous));
 
-  uses_header_client_ =
+  current_request_uses_header_client_ =
       factory_->header_client_binding_ && request_.url.SchemeIsHTTPOrHTTPS() &&
       network_service_request_id_ != 0 &&
-      ExtensionWebRequestEventRouter::GetInstance()->HasExtraHeadersListener(
-          factory_->browser_context_, factory_->info_map_, &info_.value());
+      ExtensionWebRequestEventRouter::GetInstance()
+          ->HasExtraHeadersListenerForRequest(
+              factory_->browser_context_, factory_->info_map_, &info_.value());
 
   // If the header client will be used, we start the request immediately, and
   // OnBeforeSendHeaders and OnSendHeaders will be handled there. Otherwise,
   // send these events before the request starts.
   base::RepeatingCallback<void(int)> continuation;
-  if (uses_header_client_) {
+  if (current_request_uses_header_client_) {
     continuation = base::BindRepeating(
         &InProgressRequest::ContinueToStartRequest, weak_factory_.GetWeakPtr());
   } else {
@@ -175,7 +196,7 @@
     const network::ResourceResponseHead& head) {
   current_response_ = head;
   on_receive_response_received_ = true;
-  if (uses_header_client_) {
+  if (current_request_uses_header_client_) {
     ContinueToResponseStarted(net::OK);
   } else {
     HandleResponseOrRedirectHeaders(
@@ -195,7 +216,7 @@
   }
 
   current_response_ = head;
-  if (uses_header_client_) {
+  if (current_request_uses_header_client_) {
     ContinueToBeforeRedirect(redirect_info, net::OK);
   } else {
     HandleResponseOrRedirectHeaders(
@@ -269,7 +290,19 @@
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnBeforeSendHeaders(
     const net::HttpRequestHeaders& headers,
     OnBeforeSendHeadersCallback callback) {
-  DCHECK(uses_header_client_);
+  if (!current_request_uses_header_client_) {
+    std::move(callback).Run(net::OK, base::nullopt);
+    return;
+  }
+
+  if (!network_request_started_) {
+    DCHECK(!pending_before_send_headers_data_);
+    pending_before_send_headers_data_ =
+        std::make_unique<PendingBeforeSendHeadersData>(headers,
+                                                       std::move(callback));
+    return;
+  }
+
   request_.headers = headers;
   on_before_send_headers_callback_ = std::move(callback);
   ContinueToBeforeSendHeaders(net::OK);
@@ -278,7 +311,11 @@
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnHeadersReceived(
     const std::string& headers,
     OnHeadersReceivedCallback callback) {
-  DCHECK(uses_header_client_);
+  if (!current_request_uses_header_client_) {
+    std::move(callback).Run(net::OK, base::nullopt, GURL());
+    return;
+  }
+
   on_headers_received_callback_ = std::move(callback);
   current_response_.headers =
       base::MakeRefCounted<net::HttpResponseHeaders>(headers);
@@ -344,7 +381,7 @@
     return;
   }
 
-  if (!uses_header_client_ && !redirect_url_.is_empty()) {
+  if (!current_request_uses_header_client_ && !redirect_url_.is_empty()) {
     HandleBeforeRequestRedirect();
     return;
   }
@@ -389,12 +426,17 @@
 
 void WebRequestProxyingURLLoaderFactory::InProgressRequest::
     ContinueToStartRequest(int error_code) {
+  // Move to a local to make sure this gets destroyed at the end of this
+  // function if it isn't used.
+  std::unique_ptr<PendingBeforeSendHeadersData> before_send_headers_data =
+      std::move(pending_before_send_headers_data_);
+
   if (error_code != net::OK) {
     OnRequestError(network::URLLoaderCompletionStatus(error_code));
     return;
   }
 
-  if (uses_header_client_ && !redirect_url_.is_empty()) {
+  if (current_request_uses_header_client_ && !redirect_url_.is_empty()) {
     HandleBeforeRequestRedirect();
     return;
   }
@@ -402,18 +444,24 @@
   if (proxied_client_binding_.is_bound())
     proxied_client_binding_.ResumeIncomingMethodCallProcessing();
 
+  network_request_started_ = true;
   if (!target_loader_.is_bound() && factory_->target_factory_.is_bound()) {
     // No extensions have cancelled us up to this point, so it's now OK to
     // initiate the real network request.
     network::mojom::URLLoaderClientPtr proxied_client;
     proxied_client_binding_.Bind(mojo::MakeRequest(&proxied_client));
     uint32_t options = options_;
-    if (uses_header_client_)
+    // Even if this request does not use the header client, future redirects
+    // might, so we need to set the option on the loader.
+    if (has_any_extra_headers_listeners_)
       options |= network::mojom::kURLLoadOptionUseHeaderClient;
     factory_->target_factory_->CreateLoaderAndStart(
         mojo::MakeRequest(&target_loader_), info_->routing_id,
         network_service_request_id_, options, request_,
         std::move(proxied_client), traffic_annotation_);
+  } else if (before_send_headers_data) {
+    OnBeforeSendHeaders(before_send_headers_data->headers,
+                        std::move(before_send_headers_data->callback));
   }
 
   // From here the lifecycle of this request is driven by subsequent events on
@@ -428,7 +476,7 @@
     return;
   }
 
-  if (uses_header_client_) {
+  if (current_request_uses_header_client_) {
     DCHECK(on_before_send_headers_callback_);
     std::move(on_before_send_headers_callback_)
         .Run(error_code, request_.headers);
@@ -446,7 +494,7 @@
         request_.headers);
   }
 
-  if (!uses_header_client_)
+  if (!current_request_uses_header_client_)
     ContinueToStartRequest(net::OK);
 }
 
@@ -541,7 +589,7 @@
     return;
   }
 
-  DCHECK(!uses_header_client_ || !override_headers_);
+  DCHECK(!current_request_uses_header_client_ || !override_headers_);
   if (override_headers_)
     current_response_.headers = override_headers_;
 
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
index 1ca71ad..17cb6f6 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -153,15 +153,32 @@
 
     bool request_completed_ = false;
 
-    // If |uses_header_client_| is set to true, the request will be sent with
-    // the network::mojom::kURLLoadOptionUseHeaderClient option, and we expect
-    // events to come through the network::mojom::TrustedURLLoaderHeaderClient
-    // binding on the factory. This is only set to true if there is a listener
-    // that needs to view or modify headers set in the network process.
-    bool uses_header_client_ = false;
+    // If |has_any_extra_headers_listeners_| is set to true, the request will be
+    // sent with the network::mojom::kURLLoadOptionUseHeaderClient option, and
+    // we expect events to come through the
+    // network::mojom::TrustedURLLoaderHeaderClient binding on the factory. This
+    // is only set to true if there is a listener that needs to view or modify
+    // headers set in the network process.
+    bool has_any_extra_headers_listeners_ = false;
+    bool current_request_uses_header_client_ = false;
     OnBeforeSendHeadersCallback on_before_send_headers_callback_;
     OnHeadersReceivedCallback on_headers_received_callback_;
 
+    // If extraHeaders listeners are present, we may receive the
+    // OnBeforeSendHeaders event for a redirect before OnBeforeRequest has been
+    // run on that URL. In that case we need to queue up this event until
+    // OnBeforeRequest has been completed and we know the request will not be
+    // canceled/redirected there.
+    struct PendingBeforeSendHeadersData;
+    std::unique_ptr<PendingBeforeSendHeadersData>
+        pending_before_send_headers_data_;
+
+    // Whether the request has gone to the network yet. If an
+    // OnBeforeSendHeaders event reaches us before the request has gone to the
+    // network, it should be saved for later in
+    // |pending_before_send_headers_data_|.
+    bool network_request_started_ = false;
+
     base::WeakPtrFactory<InProgressRequest> weak_factory_;
 
     DISALLOW_COPY_AND_ASSIGN(InProgressRequest);
diff --git a/extensions/browser/value_store/leveldb_value_store.cc b/extensions/browser/value_store/leveldb_value_store.cc
index 9f31b2f..2b31c98 100644
--- a/extensions/browser/value_store/leveldb_value_store.cc
+++ b/extensions/browser/value_store/leveldb_value_store.cc
@@ -98,14 +98,13 @@
   if (!status.ok())
     return ReadResult(std::move(status));
 
-  base::JSONReader json_reader;
   std::unique_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
 
   std::unique_ptr<leveldb::Iterator> it(db()->NewIterator(read_options()));
   for (it->SeekToFirst(); it->Valid(); it->Next()) {
     std::string key = it->key().ToString();
-    std::unique_ptr<base::Value> value =
-        json_reader.Read(StringPiece(it->value().data(), it->value().size()));
+    std::unique_ptr<base::Value> value = base::JSONReader::Read(
+        StringPiece(it->value().data(), it->value().size()));
     if (!value) {
       return ReadResult(Status(CORRUPTION,
                                Delete(key).ok() ? VALUE_RESTORE_DELETE_SUCCESS
diff --git a/extensions/common/api/declarative_net_request.idl b/extensions/common/api/declarative_net_request.idl
index 1818785..2c32ce2 100644
--- a/extensions/common/api/declarative_net_request.idl
+++ b/extensions/common/api/declarative_net_request.idl
@@ -145,7 +145,9 @@
     // MAX_NUMBER_OF_ALLOWED_PAGES</a> is the maximum number of
     // allowed page an extension can add. Also, adding page patterns is
     // atomic. In case of an error, no page pattern is added.
-    // |page_patterns| : Array of match patterns which are to be allowed.
+    // |page_patterns| : Array of
+    // <a href="/extensions/match_patterns">match patterns</a> which are to be
+    // allowed.
     // |callback|: Called after the <code>page_patterns</code> have been added.
     // chrome.runtime.lastError will be set in case of an error, for example if
     // an invalid page pattern is specified or the extension exceeded the
@@ -156,7 +158,9 @@
     // Removes <code>page_patterns</code> from the set of allowed pages.
     // Note: Removing page patterns is atomic. In case of an error, no page
     // pattern is removed.
-    // |page_patterns| : Array of match patterns which are to removed.
+    // |page_patterns| : Array of
+    // <a href="/extensions/match_patterns">match patterns</a> which are to be
+    // removed.
     // |callback|: Called after the <code>page_patterns</code> have been
     // removed. chrome.runtime.lastError will be set in case of an error.
     static void removeAllowedPages(DOMString[] page_patterns, optional EmptyCallback callback);
diff --git a/extensions/renderer/bindings/api_request_handler.cc b/extensions/renderer/bindings/api_request_handler.cc
index f98de6e2..586f97e 100644
--- a/extensions/renderer/bindings/api_request_handler.cc
+++ b/extensions/renderer/bindings/api_request_handler.cc
@@ -76,12 +76,12 @@
     const std::string& method_name,
     v8::Local<v8::Function> request_callback,
     const base::Optional<std::vector<v8::Local<v8::Value>>>&
-        local_callback_args)
+        local_callback_args,
+    const base::Optional<blink::WebUserGestureToken>& gesture_token)
     : isolate(isolate), context(isolate, context), method_name(method_name) {
   if (!request_callback.IsEmpty()) {
     callback.emplace(isolate, request_callback);
-    user_gesture_token =
-        blink::WebUserGestureIndicator::CurrentUserGestureToken();
+    user_gesture_token = gesture_token;
 
     if (local_callback_args) {
       callback_arguments = std::vector<v8::Global<v8::Value>>();
@@ -89,6 +89,11 @@
       for (const auto& arg : *local_callback_args)
         callback_arguments->emplace_back(isolate, arg);
     }
+  } else {
+    DCHECK(!local_callback_args.has_value())
+        << "Cannot specify callback arguments without a callback.";
+    DCHECK(!user_gesture_token.has_value())
+        << "Cannot specify a user gesture token without a callback.";
   }
 }
 
@@ -133,6 +138,7 @@
 
   base::Optional<std::vector<v8::Local<v8::Value>>> callback_args;
   v8::Isolate* isolate = context->GetIsolate();
+  base::Optional<blink::WebUserGestureToken> user_gesture_token;
   if (!custom_callback.IsEmpty() || !callback.IsEmpty()) {
     // In the JS bindings, custom callbacks are called with the arguments of
     // name, the full request object (see below), the original callback, and
@@ -155,11 +161,13 @@
       callback = custom_callback;
     }
 
+    user_gesture_token =
+        blink::WebUserGestureIndicator::CurrentUserGestureToken();
     request->has_callback = true;
   }
-  pending_requests_.insert(std::make_pair(
-      request_id,
-      PendingRequest(isolate, context, method, callback, callback_args)));
+  pending_requests_.emplace(request_id,
+                            PendingRequest(isolate, context, method, callback,
+                                           callback_args, user_gesture_token));
 
   request->has_user_gesture = get_user_activation_state_callback_.Run(context);
   request->arguments = std::move(arguments);
@@ -187,9 +195,17 @@
 int APIRequestHandler::AddPendingRequest(v8::Local<v8::Context> context,
                                          v8::Local<v8::Function> callback) {
   int request_id = next_request_id_++;
+
+  // NOTE(devlin): We ignore the UserGestureToken for synthesized requests like
+  // these that aren't sent to the browser. It is the caller's responsibility to
+  // handle any user gesture behavior. This prevents an issue where messaging
+  // handling would create an extra scoped user gesture, causing issues. See
+  // https://crbug.com/921141.
+  base::Optional<blink::WebUserGestureToken> null_user_gesture_token;
   pending_requests_.emplace(
-      request_id, PendingRequest(context->GetIsolate(), context, std::string(),
-                                 callback, base::nullopt));
+      request_id,
+      PendingRequest(context->GetIsolate(), context, std::string(), callback,
+                     base::nullopt, null_user_gesture_token));
   return request_id;
 }
 
@@ -261,7 +277,8 @@
   std::unique_ptr<blink::WebScopedUserGesture> user_gesture;
   // UserActivationV2 replaces the concept of (scoped) tokens with a frame-wide
   // state, hence skips token forwarding.
-  if (!base::FeatureList::IsEnabled(features::kUserActivationV2)) {
+  if (!base::FeatureList::IsEnabled(features::kUserActivationV2) &&
+      pending_request.user_gesture_token) {
     user_gesture = std::make_unique<blink::WebScopedUserGesture>(
         *pending_request.user_gesture_token);
   }
diff --git a/extensions/renderer/bindings/api_request_handler.h b/extensions/renderer/bindings/api_request_handler.h
index a273a4bec..482ad34 100644
--- a/extensions/renderer/bindings/api_request_handler.h
+++ b/extensions/renderer/bindings/api_request_handler.h
@@ -74,6 +74,7 @@
   // CompleteRequest). This is used by renderer-side implementations that
   // shouldn't be dispatched to the browser in the normal flow, but means other
   // classes don't have to worry about context invalidation.
+  // Note: Unlike StartRequest(), this will not track user gesture state.
   int AddPendingRequest(v8::Local<v8::Context> context,
                         v8::Local<v8::Function> callback);
 
@@ -111,7 +112,8 @@
         v8::Local<v8::Context> context,
         const std::string& method_name,
         v8::Local<v8::Function> callback,
-        const base::Optional<std::vector<v8::Local<v8::Value>>>& callback_args);
+        const base::Optional<std::vector<v8::Local<v8::Value>>>& callback_args,
+        const base::Optional<blink::WebUserGestureToken>& user_gesture_token);
     ~PendingRequest();
     PendingRequest(PendingRequest&&);
     PendingRequest& operator=(PendingRequest&&);
diff --git a/extensions/renderer/safe_builtins.cc b/extensions/renderer/safe_builtins.cc
index 5d8056ff..ec31c30 100644
--- a/extensions/renderer/safe_builtins.cc
+++ b/extensions/renderer/safe_builtins.cc
@@ -223,7 +223,9 @@
 }  // namespace
 
 // static
-v8::Extension* SafeBuiltins::CreateV8Extension() { return new ExtensionImpl(); }
+std::unique_ptr<v8::Extension> SafeBuiltins::CreateV8Extension() {
+  return std::make_unique<ExtensionImpl>();
+}
 
 SafeBuiltins::SafeBuiltins(ScriptContext* context) : context_(context) {}
 
diff --git a/extensions/renderer/safe_builtins.h b/extensions/renderer/safe_builtins.h
index cf2432434..dfb7721 100644
--- a/extensions/renderer/safe_builtins.h
+++ b/extensions/renderer/safe_builtins.h
@@ -5,6 +5,8 @@
 #ifndef EXTENSIONS_RENDERER_SAFE_BUILTINS_H_
 #define EXTENSIONS_RENDERER_SAFE_BUILTINS_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "v8/include/v8.h"
 
@@ -16,7 +18,7 @@
 class SafeBuiltins {
  public:
   // Creates the v8::Extension which manages SafeBuiltins instances.
-  static v8::Extension* CreateV8Extension();
+  static std::unique_ptr<v8::Extension> CreateV8Extension();
 
   explicit SafeBuiltins(ScriptContext* context);
   ~SafeBuiltins();
diff --git a/extensions/renderer/test_v8_extension_configuration.cc b/extensions/renderer/test_v8_extension_configuration.cc
index 20b96cf6..6123a86 100644
--- a/extensions/renderer/test_v8_extension_configuration.cc
+++ b/extensions/renderer/test_v8_extension_configuration.cc
@@ -4,6 +4,9 @@
 
 #include "extensions/renderer/test_v8_extension_configuration.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/lazy_instance.h"
 #include "extensions/renderer/safe_builtins.h"
 #include "v8/include/v8.h"
@@ -18,12 +21,11 @@
 }  // namespace
 
 TestV8ExtensionConfiguration::TestV8ExtensionConfiguration()
-    : safe_builtins_(SafeBuiltins::CreateV8Extension()),
-      v8_extension_names_(1, safe_builtins_->name()),
-      v8_extension_configuration_(new v8::ExtensionConfiguration(
-          static_cast<int>(v8_extension_names_.size()),
-          v8_extension_names_.data())) {
-  v8::RegisterExtension(safe_builtins_.get());
+    : v8_extension_configuration_(
+          new v8::ExtensionConfiguration(1, &v8_extension_name_)) {
+  auto safe_builtins = SafeBuiltins::CreateV8Extension();
+  v8_extension_name_ = safe_builtins->name();
+  v8::RegisterExtension(std::move(safe_builtins));
 }
 
 TestV8ExtensionConfiguration::~TestV8ExtensionConfiguration() {}
diff --git a/extensions/renderer/test_v8_extension_configuration.h b/extensions/renderer/test_v8_extension_configuration.h
index d3f946a52..6d1dad01 100644
--- a/extensions/renderer/test_v8_extension_configuration.h
+++ b/extensions/renderer/test_v8_extension_configuration.h
@@ -27,8 +27,7 @@
   static v8::ExtensionConfiguration* GetConfiguration();
 
  private:
-  std::unique_ptr<v8::Extension> safe_builtins_;
-  std::vector<const char*> v8_extension_names_;
+  const char* v8_extension_name_;
   std::unique_ptr<v8::ExtensionConfiguration> v8_extension_configuration_;
 
   DISALLOW_COPY_AND_ASSIGN(TestV8ExtensionConfiguration);
diff --git a/extensions/shell/BUILD.gn b/extensions/shell/BUILD.gn
index 6d6db7c..691b82a 100644
--- a/extensions/shell/BUILD.gn
+++ b/extensions/shell/BUILD.gn
@@ -196,7 +196,6 @@
       "browser/shell_native_app_window_aura.h",
     ]
     deps += [
-      "//ui/base/user_activity",
       "//ui/platform_window",
       "//ui/wm",
       "//ui/wm/public",
diff --git a/fuchsia/browser/frame_impl.cc b/fuchsia/browser/frame_impl.cc
index 0433bdb..2bbc7765 100644
--- a/fuchsia/browser/frame_impl.cc
+++ b/fuchsia/browser/frame_impl.cc
@@ -185,8 +185,6 @@
                      ContextImpl* context,
                      fidl::InterfaceRequest<chromium::web::Frame> frame_request)
     : web_contents_(std::move(web_contents)),
-      focus_controller_(
-          std::make_unique<wm::FocusController>(new FrameFocusRules)),
       context_(context),
       binding_(this, std::move(frame_request)) {
   web_contents_->SetDelegate(this);
@@ -202,19 +200,7 @@
 }
 
 FrameImpl::~FrameImpl() {
-  if (window_tree_host_) {
-    aura::client::SetFocusClient(root_window(), nullptr);
-    wm::SetActivationClient(root_window(), nullptr);
-    root_window()->RemovePreTargetHandler(focus_controller_.get());
-    web_contents_->ClosePage();
-    window_tree_host_->Hide();
-    window_tree_host_->compositor()->SetVisible(false);
-
-    // Allows posted focus events to process before the FocusController
-    // is torn down.
-    content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE,
-                                       focus_controller_.release());
-  }
+  TearDownView();
 }
 
 zx::unowned_channel FrameImpl::GetBindingChannelForTest() const {
@@ -256,6 +242,9 @@
     zx::eventpair view_token,
     fidl::InterfaceRequest<fuchsia::sys::ServiceProvider> incoming_services,
     fidl::InterfaceHandle<fuchsia::sys::ServiceProvider> outgoing_services) {
+  // If a View to this Frame is already active then disconnect it.
+  TearDownView();
+
   ui::PlatformWindowInitProperties properties;
   properties.view_token = std::move(view_token);
 
@@ -263,6 +252,9 @@
       std::make_unique<ScenicWindowTreeHost>(std::move(properties));
   window_tree_host_->InitHost();
 
+  focus_controller_ =
+      std::make_unique<wm::FocusController>(new FrameFocusRules);
+
   aura::client::SetFocusClient(root_window(), focus_controller_.get());
   wm::SetActivationClient(root_window(), focus_controller_.get());
 
@@ -288,76 +280,6 @@
   log_level_ = level;
 }
 
-void FrameImpl::LoadUrl(std::string url,
-                        std::unique_ptr<chromium::web::LoadUrlParams> params) {
-  GURL validated_url(url);
-  if (!validated_url.is_valid()) {
-    DLOG(WARNING) << "Invalid URL: " << url;
-    return;
-  }
-
-  content::NavigationController::LoadURLParams params_converted(validated_url);
-
-  if (params && !params->headers.empty()) {
-    std::vector<base::StringPiece> extra_headers;
-    extra_headers.reserve(params->headers.size());
-    for (const auto& header : params->headers) {
-      extra_headers.push_back(base::StringPiece(
-          reinterpret_cast<const char*>(header.data()), header.size()));
-    }
-    params_converted.extra_headers = base::JoinString(extra_headers, "\n");
-  }
-
-  if (validated_url.scheme() == url::kDataScheme)
-    params_converted.load_type = content::NavigationController::LOAD_TYPE_DATA;
-
-  params_converted.transition_type = ui::PageTransitionFromInt(
-      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
-  params_converted.was_activated = (params && params->user_activated)
-                                       ? content::WasActivatedOption::kYes
-                                       : content::WasActivatedOption::kNo;
-  web_contents_->GetController().LoadURLWithParams(params_converted);
-}
-
-void FrameImpl::GoBack() {
-  if (web_contents_->GetController().CanGoBack())
-    web_contents_->GetController().GoBack();
-}
-
-void FrameImpl::GoForward() {
-  if (web_contents_->GetController().CanGoForward())
-    web_contents_->GetController().GoForward();
-}
-
-void FrameImpl::Stop() {
-  web_contents_->Stop();
-}
-
-void FrameImpl::Reload(chromium::web::ReloadType type) {
-  content::ReloadType internal_reload_type;
-  switch (type) {
-    case chromium::web::ReloadType::PARTIAL_CACHE:
-      internal_reload_type = content::ReloadType::NORMAL;
-      break;
-    case chromium::web::ReloadType::NO_CACHE:
-      internal_reload_type = content::ReloadType::BYPASSING_CACHE;
-      break;
-  }
-  web_contents_->GetController().Reload(internal_reload_type, false);
-}
-
-void FrameImpl::GetVisibleEntry(GetVisibleEntryCallback callback) {
-  content::NavigationEntry* entry =
-      web_contents_->GetController().GetVisibleEntry();
-  if (!entry) {
-    callback(nullptr);
-    return;
-  }
-
-  chromium::web::NavigationEntry output = ConvertContentNavigationEntry(entry);
-  callback(std::make_unique<chromium::web::NavigationEntry>(std::move(output)));
-}
-
 void FrameImpl::SetNavigationEventObserver(
     fidl::InterfaceHandle<chromium::web::NavigationEventObserver> observer) {
   // Reset the event buffer state.
@@ -434,21 +356,73 @@
   callback(true);
 }
 
-void FrameImpl::DidFinishLoad(content::RenderFrameHost* render_frame_host,
-                              const GURL& validated_url) {
-  if (web_contents_->GetMainFrame() != render_frame_host) {
+void FrameImpl::PostMessage(chromium::web::WebMessage message,
+                            std::string target_origin,
+                            PostMessageCallback callback) {
+  constexpr char kWildcardOrigin[] = "*";
+
+  if (target_origin.empty()) {
+    callback(false);
     return;
   }
 
-  chromium::web::NavigationEntry current_navigation_state =
-      ConvertContentNavigationEntry(
-          web_contents_->GetController().GetVisibleEntry());
-  pending_navigation_event_is_dirty_ |=
-      ComputeNavigationEvent(cached_navigation_state_, current_navigation_state,
-                             &pending_navigation_event_);
-  cached_navigation_state_ = std::move(current_navigation_state);
+  base::Optional<base::string16> target_origin_utf16;
+  if (target_origin != kWildcardOrigin)
+    target_origin_utf16 = base::UTF8ToUTF16(target_origin);
 
-  MaybeSendNavigationEvent();
+  base::string16 data_utf16;
+  if (!ReadUTF8FromVMOAsUTF16(message.data, &data_utf16)) {
+    DLOG(WARNING) << "PostMessage() rejected non-UTF8 |message.data|.";
+    callback(false);
+    return;
+  }
+
+  // Include outgoing MessagePorts in the message.
+  std::vector<mojo::ScopedMessagePipeHandle> message_ports;
+  if (message.outgoing_transfer) {
+    if (!message.outgoing_transfer->is_message_port()) {
+      DLOG(WARNING) << "|outgoing_transfer| is not a MessagePort.";
+      callback(false);
+      return;
+    }
+
+    mojo::ScopedMessagePipeHandle port = MessagePortImpl::FromFidl(
+        std::move(message.outgoing_transfer->message_port()));
+    if (!port) {
+      callback(false);
+      return;
+    }
+    message_ports.push_back(std::move(port));
+  }
+
+  content::MessagePortProvider::PostMessageToFrame(
+      web_contents_.get(), base::string16(), target_origin_utf16,
+      std::move(data_utf16), std::move(message_ports));
+  callback(true);
+}
+
+FrameImpl::OriginScopedScript::OriginScopedScript(
+    std::vector<std::string> origins,
+    base::ReadOnlySharedMemoryRegion script)
+    : origins(std::move(origins)), script(std::move(script)) {}
+
+FrameImpl::OriginScopedScript::~OriginScopedScript() = default;
+
+void FrameImpl::TearDownView() {
+  if (window_tree_host_) {
+    aura::client::SetFocusClient(root_window(), nullptr);
+    wm::SetActivationClient(root_window(), nullptr);
+    root_window()->RemovePreTargetHandler(focus_controller_.get());
+    web_contents_->GetNativeView()->Hide();
+    window_tree_host_->Hide();
+    window_tree_host_->compositor()->SetVisible(false);
+    window_tree_host_ = nullptr;
+
+    // Allows posted focus events to process before the FocusController is torn
+    // down.
+    content::BrowserThread::DeleteSoon(content::BrowserThread::UI, FROM_HERE,
+                                       std::move(focus_controller_));
+  }
 }
 
 void FrameImpl::MaybeSendNavigationEvent() {
@@ -472,6 +446,93 @@
       });
 }
 
+void FrameImpl::LoadUrl(std::string url,
+                        std::unique_ptr<chromium::web::LoadUrlParams> params) {
+  GURL validated_url(url);
+  if (!validated_url.is_valid()) {
+    DLOG(WARNING) << "Invalid URL: " << url;
+    return;
+  }
+
+  content::NavigationController::LoadURLParams params_converted(validated_url);
+
+  if (params && !params->headers.empty()) {
+    std::vector<base::StringPiece> extra_headers;
+    extra_headers.reserve(params->headers.size());
+    for (const auto& header : params->headers) {
+      extra_headers.push_back(base::StringPiece(
+          reinterpret_cast<const char*>(header.data()), header.size()));
+    }
+    params_converted.extra_headers = base::JoinString(extra_headers, "\n");
+  }
+
+  if (validated_url.scheme() == url::kDataScheme)
+    params_converted.load_type = content::NavigationController::LOAD_TYPE_DATA;
+
+  params_converted.transition_type = ui::PageTransitionFromInt(
+      ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
+  params_converted.was_activated = (params && params->user_activated)
+                                       ? content::WasActivatedOption::kYes
+                                       : content::WasActivatedOption::kNo;
+  web_contents_->GetController().LoadURLWithParams(params_converted);
+}
+
+void FrameImpl::GoBack() {
+  if (web_contents_->GetController().CanGoBack())
+    web_contents_->GetController().GoBack();
+}
+
+void FrameImpl::GoForward() {
+  if (web_contents_->GetController().CanGoForward())
+    web_contents_->GetController().GoForward();
+}
+
+void FrameImpl::Stop() {
+  web_contents_->Stop();
+}
+
+void FrameImpl::Reload(chromium::web::ReloadType type) {
+  content::ReloadType internal_reload_type;
+  switch (type) {
+    case chromium::web::ReloadType::PARTIAL_CACHE:
+      internal_reload_type = content::ReloadType::NORMAL;
+      break;
+    case chromium::web::ReloadType::NO_CACHE:
+      internal_reload_type = content::ReloadType::BYPASSING_CACHE;
+      break;
+  }
+  web_contents_->GetController().Reload(internal_reload_type, false);
+}
+
+void FrameImpl::GetVisibleEntry(GetVisibleEntryCallback callback) {
+  content::NavigationEntry* entry =
+      web_contents_->GetController().GetVisibleEntry();
+  if (!entry) {
+    callback(nullptr);
+    return;
+  }
+
+  chromium::web::NavigationEntry output = ConvertContentNavigationEntry(entry);
+  callback(std::make_unique<chromium::web::NavigationEntry>(std::move(output)));
+}
+
+void FrameImpl::DidFinishLoad(content::RenderFrameHost* render_frame_host,
+                              const GURL& validated_url) {
+  if (web_contents_->GetMainFrame() != render_frame_host) {
+    return;
+  }
+
+  chromium::web::NavigationEntry current_navigation_state =
+      ConvertContentNavigationEntry(
+          web_contents_->GetController().GetVisibleEntry());
+  pending_navigation_event_is_dirty_ |=
+      ComputeNavigationEvent(cached_navigation_state_, current_navigation_state,
+                             &pending_navigation_event_);
+  cached_navigation_state_ = std::move(current_navigation_state);
+
+  MaybeSendNavigationEvent();
+}
+
 void FrameImpl::ReadyToCommitNavigation(
     content::NavigationHandle* navigation_handle) {
   if (before_load_scripts_.empty())
@@ -538,56 +599,4 @@
   return true;
 }
 
-FrameImpl::OriginScopedScript::OriginScopedScript(
-    std::vector<std::string> origins,
-    base::ReadOnlySharedMemoryRegion script)
-    : origins(std::move(origins)), script(std::move(script)) {}
-
-FrameImpl::OriginScopedScript::~OriginScopedScript() = default;
-
-void FrameImpl::PostMessage(chromium::web::WebMessage message,
-                            std::string target_origin,
-                            PostMessageCallback callback) {
-  constexpr char kWildcardOrigin[] = "*";
-
-  if (target_origin.empty()) {
-    callback(false);
-    return;
-  }
-
-  base::Optional<base::string16> target_origin_utf16;
-  if (target_origin != kWildcardOrigin)
-    target_origin_utf16 = base::UTF8ToUTF16(target_origin);
-
-  base::string16 data_utf16;
-  if (!ReadUTF8FromVMOAsUTF16(message.data, &data_utf16)) {
-    DLOG(WARNING) << "PostMessage() rejected non-UTF8 |message.data|.";
-    callback(false);
-    return;
-  }
-
-  // Include outgoing MessagePorts in the message.
-  std::vector<mojo::ScopedMessagePipeHandle> message_ports;
-  if (message.outgoing_transfer) {
-    if (!message.outgoing_transfer->is_message_port()) {
-      DLOG(WARNING) << "|outgoing_transfer| is not a MessagePort.";
-      callback(false);
-      return;
-    }
-
-    mojo::ScopedMessagePipeHandle port = MessagePortImpl::FromFidl(
-        std::move(message.outgoing_transfer->message_port()));
-    if (!port) {
-      callback(false);
-      return;
-    }
-    message_ports.push_back(std::move(port));
-  }
-
-  content::MessagePortProvider::PostMessageToFrame(
-      web_contents_.get(), base::string16(), target_origin_utf16,
-      std::move(data_utf16), std::move(message_ports));
-  callback(true);
-}
-
 }  // namespace webrunner
diff --git a/fuchsia/browser/frame_impl.h b/fuchsia/browser/frame_impl.h
index 65faf0a..0181622 100644
--- a/fuchsia/browser/frame_impl.h
+++ b/fuchsia/browser/frame_impl.h
@@ -50,6 +50,7 @@
   zx::unowned_channel GetBindingChannelForTest() const;
 
   content::WebContents* web_contents_for_test() { return web_contents_.get(); }
+  bool has_view_for_test() { return window_tree_host_ != nullptr; }
 
   // chromium::web::Frame implementation.
   void CreateView(
@@ -96,6 +97,15 @@
     DISALLOW_COPY_AND_ASSIGN(OriginScopedScript);
   };
 
+  aura::Window* root_window() const { return window_tree_host_->window(); }
+
+  // Release the resources associated with the View, if one is active.
+  void TearDownView();
+
+  // Sends |pending_navigation_event_| to the observer if there are any changes
+  // to be reported.
+  void MaybeSendNavigationEvent();
+
   // chromium::web::NavigationController implementation.
   void LoadUrl(std::string url,
                std::unique_ptr<chromium::web::LoadUrlParams> params) override;
@@ -105,12 +115,6 @@
   void Reload(chromium::web::ReloadType type) override;
   void GetVisibleEntry(GetVisibleEntryCallback callback) override;
 
-  aura::Window* root_window() const { return window_tree_host_->window(); }
-
-  // Sends |pending_navigation_event_| to the observer if there are any changes
-  // to be reported.
-  void MaybeSendNavigationEvent();
-
   // content::WebContentsDelegate implementation.
   bool ShouldCreateWebContents(
       content::WebContents* web_contents,
diff --git a/fuchsia/browser/frame_impl_browsertest.cc b/fuchsia/browser/frame_impl_browsertest.cc
index aa678a8..f1d2753 100644
--- a/fuchsia/browser/frame_impl_browsertest.cc
+++ b/fuchsia/browser/frame_impl_browsertest.cc
@@ -978,6 +978,46 @@
   EXPECT_FALSE(unused_message_read.has_value());
 }
 
+IN_PROC_BROWSER_TEST_F(FrameImplTest, RecreateView) {
+  chromium::web::FramePtr frame = CreateFrame();
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  // Process the Frame creation request, and verify we can get the FrameImpl.
+  base::RunLoop().RunUntilIdle();
+  FrameImpl* frame_impl = context_impl()->GetFrameImplForTest(&frame);
+  ASSERT_TRUE(frame_impl);
+  EXPECT_FALSE(frame_impl->has_view_for_test());
+
+  chromium::web::NavigationControllerPtr controller;
+  frame->GetNavigationController(controller.NewRequest());
+
+  // Verify that the Frame can navigate, prior to the View being created.
+  const GURL page1_url(embedded_test_server()->GetURL(kPage1Path));
+  CheckLoadUrl(page1_url.spec(), kPage1Title, nullptr, controller.get());
+
+  // Request a View from the Frame, and pump the loop to process the request.
+  zx::eventpair owner_token, frame_token;
+  ASSERT_EQ(zx::eventpair::create(0, &owner_token, &frame_token), ZX_OK);
+  frame->CreateView2(std::move(frame_token), nullptr, nullptr);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(frame_impl->has_view_for_test());
+
+  // Verify that the Frame still works, by navigating to Page #2.
+  const GURL page2_url(embedded_test_server()->GetURL(kPage2Path));
+  CheckLoadUrl(page2_url.spec(), kPage2Title, nullptr, controller.get());
+
+  // Create new View tokens and request a new view.
+  zx::eventpair owner_token2, frame_token2;
+  ASSERT_EQ(zx::eventpair::create(0, &owner_token2, &frame_token2), ZX_OK);
+  frame->CreateView2(std::move(frame_token), nullptr, nullptr);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_TRUE(frame_impl->has_view_for_test());
+
+  // Verify that the Frame still works, by navigating back to Page #1.
+  CheckLoadUrl(page1_url.spec(), kPage1Title, nullptr, controller.get());
+}
+
 class RequestMonitoringFrameImplBrowserTest : public FrameImplTest {
  public:
   RequestMonitoringFrameImplBrowserTest() = default;
diff --git a/gin/arguments.cc b/gin/arguments.cc
index 46552a5..07335c7 100644
--- a/gin/arguments.cc
+++ b/gin/arguments.cc
@@ -10,42 +10,49 @@
 namespace gin {
 
 Arguments::Arguments()
-    : isolate_(NULL),
-      info_(NULL),
-      next_(0),
-      insufficient_arguments_(false) {
-}
+    : isolate_(nullptr), info_for_function_(nullptr), is_for_property_(false) {}
 
 Arguments::Arguments(const v8::FunctionCallbackInfo<v8::Value>& info)
     : isolate_(info.GetIsolate()),
-      info_(&info),
-      next_(0),
-      insufficient_arguments_(false) {
-}
+      info_for_function_(&info),
+      is_for_property_(false) {}
+
+Arguments::Arguments(const v8::PropertyCallbackInfo<v8::Value>& info)
+    : isolate_(info.GetIsolate()),
+      info_for_property_(&info),
+      is_for_property_(true) {}
 
 Arguments::~Arguments() = default;
 
 v8::Local<v8::Value> Arguments::PeekNext() const {
-  if (next_ >= info_->Length())
+  if (is_for_property_)
     return v8::Local<v8::Value>();
-  return (*info_)[next_];
+  if (next_ >= info_for_function_->Length())
+    return v8::Local<v8::Value>();
+  return (*info_for_function_)[next_];
 }
 
 std::vector<v8::Local<v8::Value>> Arguments::GetAll() const {
   std::vector<v8::Local<v8::Value>> result;
-  int length = info_->Length();
+  if (is_for_property_)
+    return result;
+
+  int length = info_for_function_->Length();
   if (length == 0)
     return result;
 
   result.reserve(length);
   for (int i = 0; i < length; ++i)
-    result.push_back((*info_)[i]);
+    result.push_back((*info_for_function_)[i]);
 
   return result;
 }
 
 v8::Local<v8::Context> Arguments::GetHolderCreationContext() const {
-  return info_->Holder()->CreationContext();
+  v8::Local<v8::Object> holder = is_for_property_
+                                     ? info_for_property_->Holder()
+                                     : info_for_function_->Holder();
+  return holder->CreationContext();
 }
 
 std::string V8TypeAsString(v8::Isolate* isolate, v8::Local<v8::Value> value) {
@@ -62,12 +69,16 @@
 }
 
 void Arguments::ThrowError() const {
+  if (is_for_property_)
+    return ThrowTypeError("Error processing property accessor arguments.");
+
   if (insufficient_arguments_)
     return ThrowTypeError("Insufficient number of arguments.");
 
+  v8::Local<v8::Value> value = (*info_for_function_)[next_ - 1];
   return ThrowTypeError(base::StringPrintf(
       "Error processing argument at index %d, conversion failure from %s",
-      next_ - 1, V8TypeAsString(isolate_, (*info_)[next_ - 1]).c_str()));
+      next_ - 1, V8TypeAsString(isolate_, value).c_str()));
 }
 
 void Arguments::ThrowTypeError(const std::string& message) const {
@@ -76,7 +87,7 @@
 }
 
 bool Arguments::IsConstructCall() const {
-  return info_->IsConstructCall();
+  return !is_for_property_ && info_for_function_->IsConstructCall();
 }
 
 }  // namespace gin
diff --git a/gin/arguments.h b/gin/arguments.h
index 234cf53..eaded13 100644
--- a/gin/arguments.h
+++ b/gin/arguments.h
@@ -13,42 +13,51 @@
 // Arguments is a wrapper around v8::FunctionCallbackInfo that integrates
 // with Converter to make it easier to marshall arguments and return values
 // between V8 and C++.
+//
+// If constructed instead with a v8::PropertyCallbackInfo, behaves as though a
+// function with no arguments had been called.
 class GIN_EXPORT Arguments {
  public:
   Arguments();
   explicit Arguments(const v8::FunctionCallbackInfo<v8::Value>& info);
+  explicit Arguments(const v8::PropertyCallbackInfo<v8::Value>& info);
   ~Arguments();
 
   template <typename T>
   bool GetHolder(T* out) const {
-    return ConvertFromV8(isolate_, info_->Holder(), out);
+    v8::Local<v8::Object> holder = is_for_property_
+                                       ? info_for_property_->Holder()
+                                       : info_for_function_->Holder();
+    return ConvertFromV8(isolate_, holder, out);
   }
 
   template<typename T>
   bool GetData(T* out) {
-    return ConvertFromV8(isolate_, info_->Data(), out);
+    v8::Local<v8::Value> data = is_for_property_ ? info_for_property_->Data()
+                                                 : info_for_function_->Data();
+    return ConvertFromV8(isolate_, data, out);
   }
 
   template<typename T>
   bool GetNext(T* out) {
-    if (next_ >= info_->Length()) {
+    if (is_for_property_ || next_ >= info_for_function_->Length()) {
       insufficient_arguments_ = true;
       return false;
     }
-    v8::Local<v8::Value> val = (*info_)[next_++];
+    v8::Local<v8::Value> val = (*info_for_function_)[next_++];
     return ConvertFromV8(isolate_, val, out);
   }
 
   template<typename T>
   bool GetRemaining(std::vector<T>* out) {
-    if (next_ >= info_->Length()) {
+    if (is_for_property_ || next_ >= info_for_function_->Length()) {
       insufficient_arguments_ = true;
       return false;
     }
-    int remaining = info_->Length() - next_;
+    int remaining = info_for_function_->Length() - next_;
     out->resize(remaining);
     for (int i = 0; i < remaining; ++i) {
-      v8::Local<v8::Value> val = (*info_)[next_++];
+      v8::Local<v8::Value> val = (*info_for_function_)[next_++];
       if (!ConvertFromV8(isolate_, val, &out->at(i)))
         return false;
     }
@@ -56,14 +65,16 @@
   }
 
   bool Skip() {
-    if (next_ >= info_->Length())
+    if (is_for_property_)
+      return false;
+    if (next_ >= info_for_function_->Length())
       return false;
     next_++;
     return true;
   }
 
   int Length() const {
-    return info_->Length();
+    return is_for_property_ ? 0 : info_for_function_->Length();
   }
 
   template<typename T>
@@ -71,7 +82,9 @@
     v8::Local<v8::Value> v8_value;
     if (!TryConvertToV8(isolate_, val, &v8_value))
       return;
-    info_->GetReturnValue().Set(v8_value);
+    (is_for_property_ ? info_for_property_->GetReturnValue()
+                      : info_for_function_->GetReturnValue())
+        .Set(v8_value);
   }
 
   // Returns the creation context of the Holder.
@@ -97,9 +110,13 @@
 
  private:
   v8::Isolate* isolate_;
-  const v8::FunctionCallbackInfo<v8::Value>* info_;
-  int next_;
-  bool insufficient_arguments_;
+  union {
+    const v8::FunctionCallbackInfo<v8::Value>* info_for_function_;
+    const v8::PropertyCallbackInfo<v8::Value>* info_for_property_;
+  };
+  int next_ = 0;
+  bool insufficient_arguments_ = false;
+  bool is_for_property_ = false;
 };
 
 }  // namespace gin
diff --git a/gin/function_template.h b/gin/function_template.h
index 498ca1c..7edcc9e 100644
--- a/gin/function_template.h
+++ b/gin/function_template.h
@@ -6,6 +6,7 @@
 #define GIN_FUNCTION_TEMPLATE_H_
 
 #include <stddef.h>
+#include <utility>
 
 #include "base/callback.h"
 #include "base/logging.h"
@@ -192,11 +193,9 @@
 
 template <typename ReturnType, typename... ArgTypes>
 struct Dispatcher<ReturnType(ArgTypes...)> {
-  static void DispatchToCallback(
-      const v8::FunctionCallbackInfo<v8::Value>& info) {
-    Arguments args(info);
+  static void DispatchToCallbackImpl(Arguments* args) {
     v8::Local<v8::External> v8_holder;
-    CHECK(args.GetData(&v8_holder));
+    CHECK(args->GetData(&v8_holder));
     CallbackHolderBase* holder_base = reinterpret_cast<CallbackHolderBase*>(
         v8_holder->Value());
 
@@ -204,10 +203,23 @@
     HolderT* holder = static_cast<HolderT*>(holder_base);
 
     using Indices = std::index_sequence_for<ArgTypes...>;
-    Invoker<Indices, ArgTypes...> invoker(&args, holder->invoker_options);
+    Invoker<Indices, ArgTypes...> invoker(args, holder->invoker_options);
     if (invoker.IsOK())
       invoker.DispatchToCallback(holder->callback);
   }
+
+  static void DispatchToCallback(
+      const v8::FunctionCallbackInfo<v8::Value>& info) {
+    Arguments args(info);
+    DispatchToCallbackImpl(&args);
+  }
+
+  static void DispatchToCallbackForProperty(
+      v8::Local<v8::Name>,
+      const v8::PropertyCallbackInfo<v8::Value>& info) {
+    Arguments args(info);
+    DispatchToCallbackImpl(&args);
+  }
 };
 
 }  // namespace internal
@@ -241,6 +253,26 @@
   return tmpl;
 }
 
+// CreateDataPropertyCallback creates a v8::AccessorNameGetterCallback and
+// corresponding data value that will hold and execute the provided
+// base::RepeatingCallback, using automatic conversions similar to
+// |CreateFunctionTemplate|.
+//
+// It is expected that these will be passed to v8::Template::SetLazyDataProperty
+// or another similar function.
+template <typename Sig>
+std::pair<v8::AccessorNameGetterCallback, v8::Local<v8::Value>>
+CreateDataPropertyCallback(v8::Isolate* isolate,
+                           base::RepeatingCallback<Sig> callback,
+                           InvokerOptions invoker_options = {}) {
+  typedef internal::CallbackHolder<Sig> HolderT;
+  HolderT* holder =
+      new HolderT(isolate, std::move(callback), std::move(invoker_options));
+  return {&internal::Dispatcher<Sig>::DispatchToCallbackForProperty,
+          ConvertToV8<v8::Local<v8::External>>(isolate,
+                                               holder->GetHandle(isolate))};
+}
+
 }  // namespace gin
 
 #endif  // GIN_FUNCTION_TEMPLATE_H_
diff --git a/gin/gin_features.cc b/gin/gin_features.cc
index a487e25..5cdcf06 100644
--- a/gin/gin_features.cc
+++ b/gin/gin_features.cc
@@ -10,4 +10,8 @@
 const base::Feature kV8OptimizeJavascript{"V8OptimizeJavascript",
                                           base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Enables flushing of JS bytecode in V8.
+const base::Feature kV8FlushBytecode{"V8FlushBytecode",
+                                     base::FEATURE_ENABLED_BY_DEFAULT};
+
 }  // namespace features
diff --git a/gin/gin_features.h b/gin/gin_features.h
index 58bb433..da834384 100644
--- a/gin/gin_features.h
+++ b/gin/gin_features.h
@@ -11,6 +11,7 @@
 namespace features {
 
 GIN_EXPORT extern const base::Feature kV8OptimizeJavascript;
+GIN_EXPORT extern const base::Feature kV8FlushBytecode;
 
 }  // namespace features
 
diff --git a/gin/object_template_builder.cc b/gin/object_template_builder.cc
index 83abe42a..543cd69 100644
--- a/gin/object_template_builder.cc
+++ b/gin/object_template_builder.cc
@@ -187,6 +187,15 @@
   return *this;
 }
 
+ObjectTemplateBuilder& ObjectTemplateBuilder::SetLazyDataPropertyImpl(
+    const base::StringPiece& name,
+    v8::AccessorNameGetterCallback callback,
+    v8::Local<v8::Value> data) {
+  template_->SetLazyDataProperty(StringToSymbol(isolate_, name), callback,
+                                 data);
+  return *this;
+}
+
 v8::Local<v8::ObjectTemplate> ObjectTemplateBuilder::Build() {
   v8::Local<v8::ObjectTemplate> result = template_;
   template_.Clear();
diff --git a/gin/object_template_builder.h b/gin/object_template_builder.h
index 3f3b7a5..6fb331c 100644
--- a/gin/object_template_builder.h
+++ b/gin/object_template_builder.h
@@ -5,7 +5,9 @@
 #ifndef GIN_OBJECT_TEMPLATE_BUILDER_H_
 #define GIN_OBJECT_TEMPLATE_BUILDER_H_
 
+#include <tuple>
 #include <type_traits>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/callback.h"
@@ -79,6 +81,25 @@
         name, internal::CreateFunctionTemplate(isolate_, getter, type_name_),
         internal::CreateFunctionTemplate(isolate_, setter, type_name_));
   }
+
+  // Whereas SetProperty creates an accessor property, this creates what appears
+  // to be a data property but whose value is lazily computed the first time the
+  // [[Get]] operation occurs.
+  template <typename T>
+  ObjectTemplateBuilder& SetLazyDataProperty(const base::StringPiece& name,
+                                             const T& getter) {
+    InvokerOptions options;
+    if (std::is_member_function_pointer<T>::value) {
+      options.holder_is_first_argument = true;
+      options.holder_type = type_name_;
+    }
+    v8::AccessorNameGetterCallback callback;
+    v8::Local<v8::Value> data;
+    std::tie(callback, data) = CreateDataPropertyCallback(
+        isolate_, base::BindRepeating(getter), std::move(options));
+    return SetLazyDataPropertyImpl(name, callback, data);
+  }
+
   ObjectTemplateBuilder& AddNamedPropertyInterceptor();
   ObjectTemplateBuilder& AddIndexedPropertyInterceptor();
 
@@ -90,6 +111,10 @@
   ObjectTemplateBuilder& SetPropertyImpl(
       const base::StringPiece& name, v8::Local<v8::FunctionTemplate> getter,
       v8::Local<v8::FunctionTemplate> setter);
+  ObjectTemplateBuilder& SetLazyDataPropertyImpl(
+      const base::StringPiece& name,
+      v8::AccessorNameGetterCallback callback,
+      v8::Local<v8::Value> data);
 
   v8::Isolate* isolate_;
 
diff --git a/gin/v8_initializer.cc b/gin/v8_initializer.cc
index 006f6f4a..0407c6b 100644
--- a/gin/v8_initializer.cc
+++ b/gin/v8_initializer.cc
@@ -219,6 +219,12 @@
     v8::V8::SetFlagsFromString(no_optimize, sizeof(no_optimize) - 1);
   }
 
+  if (!base::FeatureList::IsEnabled(features::kV8FlushBytecode)) {
+    static const char no_flush_bytecode[] = "--no-flush-bytecode";
+    v8::V8::SetFlagsFromString(no_flush_bytecode,
+                               sizeof(no_flush_bytecode) - 1);
+  }
+
   if (IsolateHolder::kStrictMode == mode) {
     static const char use_strict[] = "--use_strict";
     v8::V8::SetFlagsFromString(use_strict, sizeof(use_strict) - 1);
diff --git a/gin/wrappable_unittest.cc b/gin/wrappable_unittest.cc
index 74aa7e8..6bc99454 100644
--- a/gin/wrappable_unittest.cc
+++ b/gin/wrappable_unittest.cc
@@ -291,4 +291,99 @@
   EXPECT_EQ(std::string(), get_error(non_member_method, v8::Null(isolate)));
 }
 
+class MyObjectWithLazyProperties
+    : public Wrappable<MyObjectWithLazyProperties> {
+ public:
+  static WrapperInfo kWrapperInfo;
+
+  static gin::Handle<MyObjectWithLazyProperties> Create(v8::Isolate* isolate) {
+    return CreateHandle(isolate, new MyObjectWithLazyProperties());
+  }
+
+  int access_count() const { return access_count_; }
+
+ private:
+  MyObjectWithLazyProperties() = default;
+
+  ObjectTemplateBuilder GetObjectTemplateBuilder(v8::Isolate* isolate) final {
+    return Wrappable::GetObjectTemplateBuilder(isolate)
+        .SetLazyDataProperty("fortyTwo", &MyObjectWithLazyProperties::FortyTwo)
+        .SetLazyDataProperty("self",
+                             base::BindRepeating([](gin::Arguments* arguments) {
+                               v8::Local<v8::Value> holder;
+                               CHECK(arguments->GetHolder(&holder));
+                               return holder;
+                             }));
+  }
+
+  int FortyTwo() {
+    access_count_++;
+    return 42;
+  }
+
+  int access_count_ = 0;
+  DISALLOW_COPY_AND_ASSIGN(MyObjectWithLazyProperties);
+};
+
+WrapperInfo MyObjectWithLazyProperties::kWrapperInfo = {kEmbedderNativeGin};
+
+TEST_F(WrappableTest, LazyPropertyGetterIsCalledOnce) {
+  v8::Isolate* isolate = instance_->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Context> context = context_.Get(isolate);
+
+  auto handle = MyObjectWithLazyProperties::Create(isolate);
+  v8::Local<v8::Object> v8_object = handle.ToV8().As<v8::Object>();
+  v8::Local<v8::String> key = StringToSymbol(isolate, "fortyTwo");
+  v8::Local<v8::Value> value;
+
+  bool has_own_property = false;
+  ASSERT_TRUE(v8_object->HasOwnProperty(context, key).To(&has_own_property));
+  EXPECT_TRUE(has_own_property);
+
+  EXPECT_EQ(0, handle->access_count());
+
+  ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
+  EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate, 42)));
+  EXPECT_EQ(1, handle->access_count());
+
+  ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
+  EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate, 42)));
+  EXPECT_EQ(1, handle->access_count());
+}
+
+TEST_F(WrappableTest, LazyPropertyGetterCanBeSetFirst) {
+  v8::Isolate* isolate = instance_->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Context> context = context_.Get(isolate);
+
+  auto handle = MyObjectWithLazyProperties::Create(isolate);
+  v8::Local<v8::Object> v8_object = handle.ToV8().As<v8::Object>();
+  v8::Local<v8::String> key = StringToSymbol(isolate, "fortyTwo");
+  v8::Local<v8::Value> value;
+
+  EXPECT_EQ(0, handle->access_count());
+
+  bool set_ok = false;
+  ASSERT_TRUE(
+      v8_object->Set(context, key, v8::Int32::New(isolate, 1701)).To(&set_ok));
+  ASSERT_TRUE(set_ok);
+  ASSERT_TRUE(v8_object->Get(context, key).ToLocal(&value));
+  EXPECT_TRUE(value->StrictEquals(v8::Int32::New(isolate, 1701)));
+  EXPECT_EQ(0, handle->access_count());
+}
+
+TEST_F(WrappableTest, LazyPropertyGetterCanBindSpecialArguments) {
+  v8::Isolate* isolate = instance_->isolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::Local<v8::Context> context = context_.Get(isolate);
+
+  auto handle = MyObjectWithLazyProperties::Create(isolate);
+  v8::Local<v8::Object> v8_object = handle.ToV8().As<v8::Object>();
+  v8::Local<v8::Value> value;
+  ASSERT_TRUE(
+      v8_object->Get(context, StringToSymbol(isolate, "self")).ToLocal(&value));
+  EXPECT_TRUE(v8_object == value);
+}
+
 }  // namespace gin
diff --git a/gpu/command_buffer/common/id_type_unittest.cc b/gpu/command_buffer/common/id_type_unittest.cc
index 1ba2d01..a8f0b349 100644
--- a/gpu/command_buffer/common/id_type_unittest.cc
+++ b/gpu/command_buffer/common/id_type_unittest.cc
@@ -188,13 +188,13 @@
   EXPECT_EQ(map[other_id()], "other_id");
 }
 
-INSTANTIATE_TEST_CASE_P(,
-                        IdTypeSpecificValueTest,
-                        ::testing::Values(std::numeric_limits<int>::min(),
-                                          -1,
-                                          0,
-                                          1,
-                                          123,
-                                          std::numeric_limits<int>::max()));
+INSTANTIATE_TEST_SUITE_P(,
+                         IdTypeSpecificValueTest,
+                         ::testing::Values(std::numeric_limits<int>::min(),
+                                           -1,
+                                           0,
+                                           1,
+                                           123,
+                                           std::numeric_limits<int>::max()));
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/feature_info_unittest.cc b/gpu/command_buffer/service/feature_info_unittest.cc
index af214f27..f3409215e 100644
--- a/gpu/command_buffer/service/feature_info_unittest.cc
+++ b/gpu/command_buffer/service/feature_info_unittest.cc
@@ -173,9 +173,9 @@
   ES3_on_Version3_2Compatibility
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        FeatureInfoTest,
-                        ::testing::ValuesIn(kGLVersionKinds));
+INSTANTIATE_TEST_SUITE_P(Service,
+                         FeatureInfoTest,
+                         ::testing::ValuesIn(kGLVersionKinds));
 
 TEST_P(FeatureInfoTest, Basic) {
   SetupWithoutInit();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_commands.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_commands.cc
index 3fbe718..3fadfd9 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_commands.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_unittest_commands.cc
@@ -15,33 +15,33 @@
 template <typename T>
 class GLES2DecoderPassthroughFixedCommandTest
     : public GLES2DecoderPassthroughTest {};
-TYPED_TEST_CASE_P(GLES2DecoderPassthroughFixedCommandTest);
+TYPED_TEST_SUITE_P(GLES2DecoderPassthroughFixedCommandTest);
 
 TYPED_TEST_P(GLES2DecoderPassthroughFixedCommandTest, InvalidCommand) {
   TypeParam cmd;
   cmd.SetHeader();
   EXPECT_EQ(error::kUnknownCommand, this->ExecuteCmd(cmd));
 }
-REGISTER_TYPED_TEST_CASE_P(GLES2DecoderPassthroughFixedCommandTest,
-                           InvalidCommand);
+REGISTER_TYPED_TEST_SUITE_P(GLES2DecoderPassthroughFixedCommandTest,
+                            InvalidCommand);
 
 template <typename T>
 class GLES2DecoderPassthroughImmediateNoArgCommandTest
     : public GLES2DecoderPassthroughTest {};
-TYPED_TEST_CASE_P(GLES2DecoderPassthroughImmediateNoArgCommandTest);
+TYPED_TEST_SUITE_P(GLES2DecoderPassthroughImmediateNoArgCommandTest);
 
 TYPED_TEST_P(GLES2DecoderPassthroughImmediateNoArgCommandTest, InvalidCommand) {
   TypeParam& cmd = *(this->template GetImmediateAs<TypeParam>());
   cmd.SetHeader();
   EXPECT_EQ(error::kUnknownCommand, this->ExecuteImmediateCmd(cmd, 64));
 }
-REGISTER_TYPED_TEST_CASE_P(GLES2DecoderPassthroughImmediateNoArgCommandTest,
-                           InvalidCommand);
+REGISTER_TYPED_TEST_SUITE_P(GLES2DecoderPassthroughImmediateNoArgCommandTest,
+                            InvalidCommand);
 
 template <typename T>
 class GLES2DecoderPassthroughImmediateSizeArgCommandTest
     : public GLES2DecoderPassthroughTest {};
-TYPED_TEST_CASE_P(GLES2DecoderPassthroughImmediateSizeArgCommandTest);
+TYPED_TEST_SUITE_P(GLES2DecoderPassthroughImmediateSizeArgCommandTest);
 
 TYPED_TEST_P(GLES2DecoderPassthroughImmediateSizeArgCommandTest,
              InvalidCommand) {
@@ -49,8 +49,8 @@
   cmd.SetHeader(0);
   EXPECT_EQ(error::kUnknownCommand, this->ExecuteImmediateCmd(cmd, 0));
 }
-REGISTER_TYPED_TEST_CASE_P(GLES2DecoderPassthroughImmediateSizeArgCommandTest,
-                           InvalidCommand);
+REGISTER_TYPED_TEST_SUITE_P(GLES2DecoderPassthroughImmediateSizeArgCommandTest,
+                            InvalidCommand);
 
 using ES3FixedCommandTypes0 =
     ::testing::Types<BindBufferBase,
@@ -143,16 +143,16 @@
                      UniformMatrix4x2fvImmediate,
                      UniformMatrix4x3fvImmediate>;
 
-INSTANTIATE_TYPED_TEST_CASE_P(0,
-                              GLES2DecoderPassthroughFixedCommandTest,
-                              ES3FixedCommandTypes0);
-INSTANTIATE_TYPED_TEST_CASE_P(1,
-                              GLES2DecoderPassthroughFixedCommandTest,
-                              ES3FixedCommandTypes1);
-INSTANTIATE_TYPED_TEST_CASE_P(0,
-                              GLES2DecoderPassthroughImmediateNoArgCommandTest,
-                              ES3ImmediateNoArgCommandTypes0);
-INSTANTIATE_TYPED_TEST_CASE_P(
+INSTANTIATE_TYPED_TEST_SUITE_P(0,
+                               GLES2DecoderPassthroughFixedCommandTest,
+                               ES3FixedCommandTypes0);
+INSTANTIATE_TYPED_TEST_SUITE_P(1,
+                               GLES2DecoderPassthroughFixedCommandTest,
+                               ES3FixedCommandTypes1);
+INSTANTIATE_TYPED_TEST_SUITE_P(0,
+                               GLES2DecoderPassthroughImmediateNoArgCommandTest,
+                               ES3ImmediateNoArgCommandTypes0);
+INSTANTIATE_TYPED_TEST_SUITE_P(
     0,
     GLES2DecoderPassthroughImmediateSizeArgCommandTest,
     ES3ImmediateSizeArgCommandTypes0);
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
index 5bba171..e4d8050b 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest.cc
@@ -1853,31 +1853,41 @@
   SetupDefaultProgram();
 }
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES2DecoderTest, ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderWithShaderTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderWithShaderTest,
+                         ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderManualInitTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderManualInitTest,
+                         ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderRGBBackbufferTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderRGBBackbufferTest,
+                         ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderDoCommandsTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderDoCommandsTest,
+                         ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderDescheduleUntilFinishedTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderDescheduleUntilFinishedTest,
+                         ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES3DecoderTest, ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderWithShaderTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES3DecoderWithShaderTest,
+                         ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderManualInitTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES3DecoderManualInitTest,
+                         ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES3DecoderRGBBackbufferTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES3DecoderRGBBackbufferTest,
+                         ::testing::Bool());
 
 }  // namespace gles2
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc
index 9ea6081..cdbc48a 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1.cc
@@ -46,8 +46,8 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderTest1, ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderTest1, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES2DecoderTest1, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES3DecoderTest1, ::testing::Bool());
 
 template <>
 void GLES2DecoderTestBase::SpecializedSetup<cmds::GenerateMipmap, 0>(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2.cc
index e1e262e..5b9d543 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_2.cc
@@ -574,8 +574,8 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderTest2, ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderTest2, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES2DecoderTest2, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES3DecoderTest2, ::testing::Bool());
 
 template <>
 void GLES2DecoderTestBase::SpecializedSetup<cmds::GetProgramInfoLog, 0>(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3.cc
index 6bace90..26aa432 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_3.cc
@@ -49,8 +49,8 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderTest3, ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderTest3, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES2DecoderTest3, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES3DecoderTest3, ::testing::Bool());
 
 template <>
 void GLES2DecoderTestBase::SpecializedSetup<cmds::Uniform4ivImmediate, 0>(
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_4.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_4.cc
index d5b10acf..31fe2a5 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_4.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_4.cc
@@ -50,8 +50,8 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderTest4, ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(Service, GLES3DecoderTest4, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES2DecoderTest4, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES3DecoderTest4, ::testing::Bool());
 
 #include "gpu/command_buffer/service/gles2_cmd_decoder_unittest_4_autogen.h"
 
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_attribs.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_attribs.cc
index acbfc81..631e3bf 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_attribs.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_attribs.cc
@@ -407,9 +407,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderVertexArraysOESTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderVertexArraysOESTest,
+                         ::testing::Bool());
 
 class GLES2DecoderEmulatedVertexArraysOESTest
     : public GLES2DecoderVertexArraysOESTest {
@@ -430,9 +430,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderEmulatedVertexArraysOESTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderEmulatedVertexArraysOESTest,
+                         ::testing::Bool());
 
 // Test vertex array objects with native support
 TEST_P(GLES2DecoderVertexArraysOESTest, GenVertexArraysOESImmediateValidArgs) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc
index 25464810..a2df617 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_lost.cc
@@ -96,7 +96,7 @@
   EXPECT_EQ(error::kUnknown, GetContextLostReason());
 }
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2DecoderDrawOOMTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES2DecoderDrawOOMTest, ::testing::Bool());
 
 class GLES2DecoderLostContextTest : public GLES2DecoderManualInitTest {
  protected:
@@ -339,9 +339,9 @@
   ClearCurrentDecoderError();
 }
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderLostContextTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderLostContextTest,
+                         ::testing::Bool());
 
 }  // namespace gles2
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc
index 145cbcd..e9e84e7 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_context_state.cc
@@ -82,9 +82,9 @@
   void AddExpectationsForBindSampler(GLuint unit, GLuint id);
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderRestoreStateTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderRestoreStateTest,
+                         ::testing::Bool());
 
 void GLES2DecoderRestoreStateTest::AddExpectationsForActiveTexture(
     GLenum unit) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc
index 69e8df6..9e18273 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_drawing.cc
@@ -72,9 +72,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderGeometryInstancingTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderGeometryInstancingTest,
+                         ::testing::Bool());
 
 void GLES2DecoderManualInitTest::DirtyStateMaskTest(GLuint color_bits,
                                                     bool depth_mask,
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
index 015fdbb..6477d34 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_extensions.cc
@@ -30,9 +30,9 @@
  public:
   GLES2DecoderTestDisabledExtensions() = default;
 };
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderTestDisabledExtensions,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderTestDisabledExtensions,
+                         ::testing::Bool());
 
 TEST_P(GLES2DecoderTestDisabledExtensions, CHROMIUMPathRenderingDisabled) {
   const GLuint kClientPathId = 0;
@@ -477,9 +477,9 @@
   static const GLuint kServicePathId = 311;
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderTestWithCHROMIUMPathRendering,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderTestWithCHROMIUMPathRendering,
+                         ::testing::Bool());
 
 class GLES2DecoderTestWithBlendEquationAdvanced : public GLES2DecoderTest {
  public:
@@ -497,9 +497,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderTestWithBlendEquationAdvanced,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderTestWithBlendEquationAdvanced,
+                         ::testing::Bool());
 
 class GLES2DecoderTestWithEXTMultisampleCompatibility
     : public GLES2DecoderTest {
@@ -518,9 +518,9 @@
     InitDecoder(init);
   }
 };
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderTestWithEXTMultisampleCompatibility,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderTestWithEXTMultisampleCompatibility,
+                         ::testing::Bool());
 
 class GLES2DecoderTestWithBlendFuncExtended : public GLES2DecoderTest {
  public:
@@ -537,9 +537,9 @@
     InitDecoder(init);
   }
 };
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderTestWithBlendFuncExtended,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderTestWithBlendFuncExtended,
+                         ::testing::Bool());
 
 class GLES2DecoderTestWithCHROMIUMFramebufferMixedSamples
     : public GLES2DecoderTest {
@@ -558,9 +558,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderTestWithCHROMIUMFramebufferMixedSamples,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderTestWithCHROMIUMFramebufferMixedSamples,
+                         ::testing::Bool());
 
 TEST_P(GLES2DecoderTestWithCHROMIUMPathRendering, GenDeletePaths) {
   static GLuint kFirstClientID = client_path_id_ + 88;
@@ -1742,9 +1742,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderTestWithCHROMIUMRasterTransport,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderTestWithCHROMIUMRasterTransport,
+                         ::testing::Bool());
 
 class GLES3DecoderTestWithEXTWindowRectangles : public GLES3DecoderTest {
  public:
@@ -1763,9 +1763,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES3DecoderTestWithEXTWindowRectangles,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES3DecoderTestWithEXTWindowRectangles,
+                         ::testing::Bool());
 
 TEST_P(GLES3DecoderTestWithEXTWindowRectangles,
        WindowRectanglesEXTImmediateValidArgs) {
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
index ce62a0a..f9dcad3 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_framebuffers.cc
@@ -1468,7 +1468,7 @@
   FinishReadPixelsAndCheckResult(kWidth, kHeight, pixels);
 }
 
-INSTANTIATE_TEST_CASE_P(Service, GLES2ReadPixelsAsyncTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, GLES2ReadPixelsAsyncTest, ::testing::Bool());
 
 // Check that if a renderbuffer is attached and GL returns
 // GL_FRAMEBUFFER_COMPLETE that the buffer is cleared and state is restored.
@@ -2389,9 +2389,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderMultisampledRenderToTextureTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderMultisampledRenderToTextureTest,
+                         ::testing::Bool());
 
 TEST_P(GLES2DecoderMultisampledRenderToTextureTest,
        NotCompatibleWithRenderbufferStorageMultisampleCHROMIUM_EXT) {
@@ -4034,9 +4034,9 @@
   }
 }
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderTestWithDrawRectangle,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderTestWithDrawRectangle,
+                         ::testing::Bool());
 
 TEST_P(GLES2DecoderManualInitTest, MESAFramebufferFlipYExtensionEnabled) {
   InitState init;
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
index a858e10..81103a0 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_textures.cc
@@ -4227,9 +4227,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderCompressedFormatsTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderCompressedFormatsTest,
+                         ::testing::Bool());
 
 TEST_P(GLES2DecoderCompressedFormatsTest, GetCompressedTextureFormatsS3TC) {
   const GLenum formats[] = {
@@ -4406,9 +4406,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        GLES2DecoderTexStorageFormatAndTypeTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         GLES2DecoderTexStorageFormatAndTypeTest,
+                         ::testing::Bool());
 
 TEST_P(GLES2DecoderTexStorageFormatAndTypeTest, ES2) {
   InitState init;
diff --git a/gpu/command_buffer/service/gpu_tracer_unittest.cc b/gpu/command_buffer/service/gpu_tracer_unittest.cc
index 9be02124..0381d17 100644
--- a/gpu/command_buffer/service/gpu_tracer_unittest.cc
+++ b/gpu/command_buffer/service/gpu_tracer_unittest.cc
@@ -53,7 +53,7 @@
  public:
   explicit GPUTracerTester(GLES2Decoder* decoder)
       : GPUTracer(decoder), tracing_enabled_(0) {
-    gpu_timing_client_->SetCpuTimeForTesting(base::Bind(&FakeCpuTime));
+    gpu_timing_client_->SetCpuTimeForTesting(base::BindRepeating(&FakeCpuTime));
 
     // Force tracing to be dependent on our mock variable here.
     gpu_trace_srv_category = &tracing_enabled_;
@@ -100,7 +100,7 @@
       gl_fake_queries_.ExpectNoDisjointCalls(*gl_);
 
     gpu_timing_client_ = GetGLContext()->CreateGPUTimingClient();
-    gpu_timing_client_->SetCpuTimeForTesting(base::Bind(&FakeCpuTime));
+    gpu_timing_client_->SetCpuTimeForTesting(base::BindRepeating(&FakeCpuTime));
     gl_fake_queries_.Reset();
   }
 
diff --git a/gpu/command_buffer/service/program_manager_unittest.cc b/gpu/command_buffer/service/program_manager_unittest.cc
index 095a9a1..b9d24a32 100644
--- a/gpu/command_buffer/service/program_manager_unittest.cc
+++ b/gpu/command_buffer/service/program_manager_unittest.cc
@@ -2545,7 +2545,7 @@
 }
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     SupportedContexts,
     ProgramManagerWithPathRenderingTest,
     testing::Values(
@@ -2635,7 +2635,7 @@
   EXPECT_TRUE(LinkAsExpected(program, true));
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     SupportedContexts,
     ProgramManagerDualSourceBlendingES2Test,
     testing::Values(
diff --git a/gpu/command_buffer/service/query_manager_unittest.cc b/gpu/command_buffer/service/query_manager_unittest.cc
index e713551..e415682 100644
--- a/gpu/command_buffer/service/query_manager_unittest.cc
+++ b/gpu/command_buffer/service/query_manager_unittest.cc
@@ -562,7 +562,7 @@
   const base::subtle::Atomic32 kSubmitCount = 123;
   gl::GPUTimingFake fake_timing_queries;
   decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting(
-      base::Bind(&gl::GPUTimingFake::GetFakeCPUTime));
+      base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime));
 
   QueryManager::Query* query = CreateQuery(
       kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
@@ -594,7 +594,7 @@
   const base::subtle::Atomic32 kSubmitCount = 123;
   gl::GPUTimingFake fake_timing_queries;
   decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting(
-      base::Bind(&gl::GPUTimingFake::GetFakeCPUTime));
+      base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime));
 
   QueryManager::Query* query = CreateQuery(
       kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
@@ -699,7 +699,7 @@
   gl::GPUTimingFake fake_timing_queries;
 
   decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting(
-      base::Bind(&gl::GPUTimingFake::GetFakeCPUTime));
+      base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime));
 
   QueryManager::Query* query = CreateQuery(
       kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
@@ -726,7 +726,7 @@
   gl::GPUTimingFake fake_timing_queries;
 
   decoder_->GetGLContext()->CreateGPUTimingClient()->SetCpuTimeForTesting(
-      base::Bind(&gl::GPUTimingFake::GetFakeCPUTime));
+      base::BindRepeating(&gl::GPUTimingFake::GetFakeCPUTime));
 
   QueryManager::Query* query = CreateQuery(
       kTarget, kClient1Id, shared_memory_id_, kSharedMemoryOffset, 0);
diff --git a/gpu/command_buffer/service/raster_decoder.cc b/gpu/command_buffer/service/raster_decoder.cc
index f518362..1637f54c0 100644
--- a/gpu/command_buffer/service/raster_decoder.cc
+++ b/gpu/command_buffer/service/raster_decoder.cc
@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/atomic_sequence_num.h"
@@ -366,6 +367,19 @@
     return feature_info_->workarounds();
   }
 
+  void FlushToWorkAroundMacCrashes() {
+#if defined(OS_MACOSX)
+    // This function does aggressive flushes to work around crashes in the
+    // macOS OpenGL driver.
+    // https://crbug.com/906453
+    if (!flush_workaround_disabled_for_test_) {
+      if (gr_context())
+        gr_context()->flush();
+      api()->glFlushFn();
+    }
+#endif
+  }
+
   bool IsRobustnessSupported() {
     return has_robustness_extension_ &&
            shared_context_state_->context()
@@ -416,10 +430,6 @@
                                 GLsizei width,
                                 GLsizei height,
                                 const volatile GLbyte* mailboxes);
-  // If the texture has an image but that image is not bound or copied to the
-  // texture, this will first attempt to bind it, and if that fails
-  // CopyTexImage on it.
-  void DoBindOrCopyTexImageIfNeeded(gles2::Texture* texture, GLenum textarget);
   void DoLoseContextCHROMIUM(GLenum current, GLenum other) { NOTIMPLEMENTED(); }
   void DoBeginRasterCHROMIUM(GLuint sk_color,
                              GLuint msaa_sample_count,
@@ -1119,18 +1129,6 @@
   int process_pos = 0;
   CommandId command = static_cast<CommandId>(0);
 
-#if defined(OS_MACOSX)
-  if (!flush_workaround_disabled_for_test_) {
-    // Flush before and after decoding commands.
-    // TODO(ccameron): This is to determine if this high frequency flushing
-    // affects crash rates.
-    // https://crbug.com/906453
-    if (gr_context())
-      gr_context()->flush();
-    api()->glFlushFn();
-  }
-#endif
-
   while (process_pos < num_entries && result == error::kNoError &&
          commands_to_process_--) {
     const unsigned int size = cmd_data->value_header.size;
@@ -1212,13 +1210,10 @@
       cmd_data += size;
     }
 
-#if defined(OS_MACOSX)
-    if (!flush_workaround_disabled_for_test_) {
-      if (gr_context())
-        gr_context()->flush();
-      api()->glFlushFn();
-    }
-#endif
+    // Workaround for https://crbug.com/906453: Flush after every command that
+    // is not between a BeginRaster and EndRaster.
+    if (!sk_surface_)
+      FlushToWorkAroundMacCrashes();
   }
 
   *entries_processed = process_pos;
@@ -1231,14 +1226,6 @@
   if (supports_oop_raster_)
     client_->ScheduleGrContextCleanup();
 
-#if defined(OS_MACOSX)
-  if (!flush_workaround_disabled_for_test_) {
-    if (gr_context())
-      gr_context()->flush();
-    api()->glFlushFn();
-  }
-#endif
-
   return result;
 }
 
@@ -1676,33 +1663,49 @@
   DLOG_IF(ERROR, !dest_mailbox.Verify())
       << "CopySubTexture was passed an invalid mailbox";
 
+  if (source_mailbox == dest_mailbox) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCopySubTexture",
+                       "source and destination mailboxes are the same");
+    return;
+  }
+
   if (use_passthrough()) {
-    // TODO(piman): use shared image representations instead.
-    gles2::TexturePassthrough* source_texture =
-        gles2::TexturePassthrough::CheckedCast(
-            group_->mailbox_manager()->ConsumeTexture(source_mailbox));
-    gles2::TexturePassthrough* dest_texture =
-        gles2::TexturePassthrough::CheckedCast(
-            group_->mailbox_manager()->ConsumeTexture(dest_mailbox));
-    if (!source_texture || !dest_texture) {
+    std::unique_ptr<SharedImageRepresentationGLTexturePassthrough>
+        source_shared_image = group_->shared_image_representation_factory()
+                                  ->ProduceGLTexturePassthrough(source_mailbox);
+    std::unique_ptr<SharedImageRepresentationGLTexturePassthrough>
+        dest_shared_image = group_->shared_image_representation_factory()
+                                ->ProduceGLTexturePassthrough(dest_mailbox);
+    if (!source_shared_image || !dest_shared_image) {
       LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
                          "unknown mailbox");
       return;
     }
-    if (source_texture->is_bind_pending()) {
-      gl::GLImage* image =
-          source_texture->GetLevelImage(source_texture->target(), 0);
-      if (image) {
-        api()->glBindTextureFn(source_texture->target(),
-                               source_texture->service_id());
-        if (image->ShouldBindOrCopy() == gl::GLImage::BIND)
-          image->BindTexImage(source_texture->target());
-        else
-          image->CopyTexImage(source_texture->target());
-        source_texture->set_is_bind_pending(false);
-      }
+
+    SharedImageRepresentationGLTexturePassthrough::ScopedAccess source_access(
+        source_shared_image.get(), GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
+    if (!source_access.success()) {
+      LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
+                         "unable to access source for read");
+      return;
     }
 
+    SharedImageRepresentationGLTexturePassthrough::ScopedAccess dest_access(
+        dest_shared_image.get(),
+        GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
+    if (!dest_access.success()) {
+      LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
+                         "unable to access destination for write");
+      return;
+    }
+
+    gles2::TexturePassthrough* source_texture =
+        source_shared_image->GetTexturePassthrough().get();
+    gles2::TexturePassthrough* dest_texture =
+        dest_shared_image->GetTexturePassthrough().get();
+    DCHECK(!source_texture->is_bind_pending());
+    DCHECK_NE(source_texture->service_id(), dest_texture->service_id());
+
     api()->glCopySubTextureCHROMIUMFn(
         source_texture->service_id(), /*source_level=*/0,
         dest_texture->target(), dest_texture->service_id(),
@@ -1712,116 +1715,72 @@
     return;
   }
 
-  // TODO(piman): use shared image representations instead.
-  gles2::Texture* source_texture = gles2::Texture::CheckedCast(
-      group_->mailbox_manager()->ConsumeTexture(source_mailbox));
-  gles2::Texture* dest_texture = gles2::Texture::CheckedCast(
-      group_->mailbox_manager()->ConsumeTexture(dest_mailbox));
-  if (!source_texture || !dest_texture) {
+  std::unique_ptr<SharedImageRepresentationGLTexture> source_shared_image =
+      group_->shared_image_representation_factory()->ProduceGLTexture(
+          source_mailbox);
+  std::unique_ptr<SharedImageRepresentationGLTexture> dest_shared_image =
+      group_->shared_image_representation_factory()->ProduceGLTexture(
+          dest_mailbox);
+  if (!source_shared_image || !dest_shared_image) {
     LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture", "unknown mailbox");
     return;
   }
-  if (source_texture == dest_texture) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCopySubTexture",
-                       "source and destination textures are the same");
+
+  SharedImageRepresentationGLTexture::ScopedAccess source_access(
+      source_shared_image.get(), GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM);
+  if (!source_access.success()) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
+                       "unable to access source for read");
     return;
   }
+
+  gles2::Texture* source_texture = source_shared_image->GetTexture();
   GLenum source_target = source_texture->target();
-  GLenum dest_target = dest_texture->target();
-  if (!source_target || !dest_target) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCopySubTexture",
-                       "textures not initialized");
+  DCHECK(source_target);
+  GLint source_level = 0;
+  gfx::Size source_size = source_shared_image->size();
+  gfx::Rect source_rect(x, y, width, height);
+  if (!gfx::Rect(source_size).Contains(source_rect)) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
+                       "source texture bad dimensions.");
     return;
   }
 
-  GLint source_level = 0;
-  GLint dest_level = 0;
-
-  ScopedTextureBinder binder(state(), dest_target, dest_texture->service_id(),
-                             gr_context());
-  base::Optional<ScopedPixelUnpackState> pixel_unpack_state;
-
-  int source_width = 0;
-  int source_height = 0;
-  gl::GLImage* image =
-      source_texture->GetLevelImage(source_target, 0 /* level */);
-  if (image) {
-    gfx::Size size = image->GetSize();
-    source_width = size.width();
-    source_height = size.height();
-    if (source_width <= 0 || source_height <= 0) {
-      LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
-                         "invalid image size");
-      return;
-    }
-
-    // Ideally we should not need to check that the sub-texture copy rectangle
-    // is valid in two different ways, here and below. However currently there
-    // is no guarantee that a texture backed by a GLImage will have sensible
-    // level info. If this synchronization were to be enforced then this and
-    // other functions in this file could be cleaned up.
-    // See: https://crbug.com/586476
-    int32_t max_x;
-    int32_t max_y;
-    if (!base::CheckAdd(x, width).AssignIfValid(&max_x) ||
-        !base::CheckAdd(y, height).AssignIfValid(&max_y) || x < 0 || y < 0 ||
-        max_x > source_width || max_y > source_height) {
-      LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
-                         "source texture bad dimensions");
-      return;
-    }
-
-    if (image->GetType() == gl::GLImage::Type::MEMORY &&
-        shared_context_state_->need_context_state_reset()) {
-      // If the image is in shared memory, we may need upload the pixel data
-      // with SubTexImage2D, so we need reset pixel unpack state if gl context
-      // state has been touched by skia.
-      pixel_unpack_state.emplace(state(), gr_context(), group_->feature_info());
-    }
-  } else {
-    if (!source_texture->GetLevelSize(source_target, 0 /* level */,
-                                      &source_width, &source_height, nullptr)) {
-      LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
-                         "source texture has no data for level");
-      return;
-    }
-
-    // Check that this type of texture is allowed.
-    if (!texture_manager()->ValidForTarget(source_target, 0 /* level */,
-                                           source_width, source_height, 1)) {
-      LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
-                         "source texture bad dimensions");
-      return;
-    }
-
-    if (!source_texture->ValidForTexture(source_target, 0 /* level */, x, y, 0,
-                                         width, height, 1)) {
-      LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
-                         "source texture bad dimensions.");
-      return;
-    }
+  SharedImageRepresentationGLTexture::ScopedAccess dest_access(
+      dest_shared_image.get(), GL_SHARED_IMAGE_ACCESS_MODE_READWRITE_CHROMIUM);
+  if (!dest_access.success()) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
+                       "unable to access destination for write");
+    return;
   }
 
+  gles2::Texture* dest_texture = dest_shared_image->GetTexture();
+  GLenum dest_target = dest_texture->target();
+  DCHECK(dest_target);
+  GLint dest_level = 0;
+  gfx::Size dest_size = dest_shared_image->size();
+  gfx::Rect dest_rect(xoffset, yoffset, width, height);
+  if (!gfx::Rect(dest_size).Contains(dest_rect)) {
+    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
+                       "destination texture bad dimensions.");
+    return;
+  }
+
+  DCHECK_NE(source_texture->service_id(), dest_texture->service_id());
+
   GLenum source_type = 0;
   GLenum source_internal_format = 0;
-  source_texture->GetLevelType(source_target, 0 /* level */, &source_type,
+  source_texture->GetLevelType(source_target, source_level, &source_type,
                                &source_internal_format);
 
   GLenum dest_type = 0;
   GLenum dest_internal_format = 0;
   bool dest_level_defined = dest_texture->GetLevelType(
-      dest_target, 0 /* level */, &dest_type, &dest_internal_format);
-  if (!dest_level_defined) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glCopySubTexture",
-                       "destination texture is not defined");
-    return;
-  }
-  if (!dest_texture->ValidForTexture(dest_target, 0 /* level */, xoffset,
-                                     yoffset, 0, width, height, 1)) {
-    LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "glCopySubTexture",
-                       "destination texture bad dimensions.");
-    return;
-  }
+      dest_target, dest_level, &dest_type, &dest_internal_format);
+  DCHECK(dest_level_defined);
+
+  // TODO(piman): Do we need this check? It might always be true by
+  // construction.
   std::string output_error_msg;
   if (!ValidateCopyTextureCHROMIUMInternalFormats(
           GetFeatureInfo(), source_internal_format, dest_internal_format,
@@ -1831,15 +1790,6 @@
     return;
   }
 
-  if (feature_info_->feature_flags().desktop_srgb_support) {
-    bool enable_framebuffer_srgb =
-        gles2::GLES2Util::GetColorEncodingFromInternalFormat(
-            source_internal_format) == GL_SRGB ||
-        gles2::GLES2Util::GetColorEncodingFromInternalFormat(
-            dest_internal_format) == GL_SRGB;
-    state()->EnableDisableFramebufferSRGB(enable_framebuffer_srgb);
-  }
-
   // Clear the source texture if necessary.
   if (!texture_manager()->ClearTextureLevel(this, source_texture, source_target,
                                             0 /* level */)) {
@@ -1848,49 +1798,72 @@
     return;
   }
 
-  int dest_width = 0;
-  int dest_height = 0;
-  bool ok = dest_texture->GetLevelSize(dest_target, dest_level, &dest_width,
-                                       &dest_height, nullptr);
-  DCHECK(ok);
-  if (xoffset != 0 || yoffset != 0 || width != dest_width ||
-      height != dest_height) {
-    gfx::Rect cleared_rect;
-    if (gles2::TextureManager::CombineAdjacentRects(
-            dest_texture->GetLevelClearedRect(dest_target, dest_level),
-            gfx::Rect(xoffset, yoffset, width, height), &cleared_rect)) {
-      DCHECK_GE(cleared_rect.size().GetArea(),
-                dest_texture->GetLevelClearedRect(dest_target, dest_level)
-                    .size()
-                    .GetArea());
-      dest_texture->SetLevelClearedRect(dest_target, dest_level, cleared_rect);
-    } else {
-      // Otherwise clear part of texture level that is not already cleared.
-      if (!texture_manager()->ClearTextureLevel(this, dest_texture, dest_target,
-                                                dest_level)) {
-        LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glCopySubTexture",
-                           "destination texture dimensions too big");
+  gfx::Rect new_cleared_rect;
+  gfx::Rect old_cleared_rect =
+      dest_texture->GetLevelClearedRect(dest_target, dest_level);
+  if (gles2::TextureManager::CombineAdjacentRects(
+          dest_texture->GetLevelClearedRect(dest_target, dest_level), dest_rect,
+          &new_cleared_rect)) {
+    DCHECK(old_cleared_rect.IsEmpty() ||
+           new_cleared_rect.Contains(old_cleared_rect));
+  } else {
+    // Otherwise clear part of texture level that is not already cleared.
+    if (!texture_manager()->ClearTextureLevel(this, dest_texture, dest_target,
+                                              dest_level)) {
+      LOCAL_SET_GL_ERROR(GL_OUT_OF_MEMORY, "glCopySubTexture",
+                         "destination texture dimensions too big");
+      return;
+    }
+    new_cleared_rect = gfx::Rect(dest_size);
+  }
+
+  ScopedTextureBinder binder(state(), dest_target, dest_texture->service_id(),
+                             gr_context());
+
+  gles2::Texture::ImageState image_state;
+  gl::GLImage* image =
+      source_texture->GetLevelImage(source_target, 0, &image_state);
+  if (image) {
+    base::Optional<ScopedPixelUnpackState> pixel_unpack_state;
+    if (image->GetType() == gl::GLImage::Type::MEMORY &&
+        shared_context_state_->need_context_state_reset()) {
+      // If the image is in shared memory, we may need upload the pixel data
+      // with SubTexImage2D, so we need reset pixel unpack state if gl context
+      // state has been touched by skia.
+      pixel_unpack_state.emplace(state(), gr_context(), group_->feature_info());
+    }
+
+    // Try to copy by uploading to the destination texture.
+    if (dest_internal_format == source_internal_format) {
+      if (image->CopyTexSubImage(dest_target, gfx::Point(xoffset, yoffset),
+                                 gfx::Rect(x, y, width, height))) {
+        dest_texture->SetLevelClearedRect(dest_target, dest_level,
+                                          new_cleared_rect);
         return;
       }
     }
-  } else {
-    dest_texture->SetLevelCleared(dest_target, dest_level, true);
-  }
 
-  // TODO(qiankun.miao@intel.com): Support level > 0 for CopyTexSubImage.
-  if (image && dest_internal_format == source_internal_format &&
-      dest_level == 0) {
-    if (image->CopyTexSubImage(dest_target, gfx::Point(xoffset, yoffset),
-                               gfx::Rect(x, y, width, height))) {
-      return;
+    // Otherwise, update the source if needed.
+    if (image_state == gles2::Texture::UNBOUND) {
+      ScopedGLErrorSuppressor suppressor(
+          "RasterDecoderImpl::DoCopySubTextureINTERNAL", error_state_.get());
+      api()->glBindTextureFn(source_target, source_texture->service_id());
+      if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
+        bool rv = image->BindTexImage(source_target);
+        DCHECK(rv) << "BindTexImage() failed";
+        image_state = gles2::Texture::BOUND;
+      } else {
+        bool rv = image->CopyTexImage(source_target);
+        DCHECK(rv) << "CopyTexImage() failed";
+        image_state = gles2::Texture::COPIED;
+      }
+      source_texture->SetLevelImageState(source_target, 0, image_state);
     }
   }
 
   if (!InitializeCopyTextureCHROMIUM())
     return;
 
-  DoBindOrCopyTexImageIfNeeded(source_texture, source_target);
-
   // GL_TEXTURE_EXTERNAL_OES texture requires apply a transform matrix
   // before presenting.
   if (source_target == GL_TEXTURE_EXTERNAL_OES) {
@@ -1904,10 +1877,13 @@
           this, source_target, source_texture->service_id(), source_level,
           source_internal_format, dest_target, dest_texture->service_id(),
           dest_level, dest_internal_format, xoffset, yoffset, x, y, width,
-          height, dest_width, dest_height, source_width, source_height,
-          false /* unpack_flip_y */, false /* unpack_premultiply_alpha */,
+          height, dest_size.width(), dest_size.height(), source_size.width(),
+          source_size.height(), false /* unpack_flip_y */,
+          false /* unpack_premultiply_alpha */,
           false /* unpack_unmultiply_alpha */, false /* dither */,
           transform_matrix, copy_tex_image_blit_.get());
+      dest_texture->SetLevelClearedRect(dest_target, dest_level,
+                                        new_cleared_rect);
       return;
     }
   }
@@ -1935,10 +1911,11 @@
       this, source_target, source_texture->service_id(), source_level,
       source_internal_format, dest_target, dest_texture->service_id(),
       dest_level, dest_internal_format, xoffset, yoffset, x, y, width, height,
-      dest_width, dest_height, source_width, source_height,
-      false /* unpack_flip_y */, false /* unpack_premultiply_alpha */,
-      false /* unpack_unmultiply_alpha */, false /* dither */, method,
-      copy_tex_image_blit_.get());
+      dest_size.width(), dest_size.height(), source_size.width(),
+      source_size.height(), false /* unpack_flip_y */,
+      false /* unpack_premultiply_alpha */, false /* unpack_unmultiply_alpha */,
+      false /* dither */, method, copy_tex_image_blit_.get());
+  dest_texture->SetLevelClearedRect(dest_target, dest_level, new_cleared_rect);
   in_copy_sub_texture_ = false;
   if (reset_texture_state_) {
     reset_texture_state_ = false;
@@ -1956,33 +1933,6 @@
   }
 }
 
-void RasterDecoderImpl::DoBindOrCopyTexImageIfNeeded(gles2::Texture* texture,
-                                                     GLenum textarget) {
-  // Image is already in use if texture is attached to a framebuffer.
-  if (texture && !texture->IsAttachedToFramebuffer()) {
-    gles2::Texture::ImageState image_state;
-    gl::GLImage* image = texture->GetLevelImage(textarget, 0, &image_state);
-    if (image && image_state == gles2::Texture::UNBOUND) {
-      ScopedGLErrorSuppressor suppressor(
-          "RasterDecoderImpl::DoBindOrCopyTexImageIfNeeded",
-          error_state_.get());
-      api()->glBindTextureFn(textarget, texture->service_id());
-      if (image->ShouldBindOrCopy() == gl::GLImage::BIND) {
-        bool rv = image->BindTexImage(textarget);
-        DCHECK(rv) << "BindTexImage() failed";
-      } else {
-        // Note: We update the state to COPIED prior to calling CopyTexImage()
-        // as that allows the GLImage implemenatation to set it back to
-        // UNBOUND and ensure that CopyTexImage() is called each time the
-        // texture is used.
-        texture->SetLevelImageState(textarget, 0, gles2::Texture::COPIED);
-        bool rv = image->CopyTexImage(textarget);
-        DCHECK(rv) << "CopyTexImage() failed";
-      }
-    }
-  }
-}
-
 namespace {
 
 // Helper to read client data from transfer cache.
@@ -2064,6 +2014,10 @@
     GLboolean can_use_lcd_text,
     GLuint color_space_transfer_cache_id,
     const volatile GLbyte* key) {
+  // Workaround for https://crbug.com/906453: Flush before BeginRaster (the
+  // commands between BeginRaster and EndRaster will not flush).
+  FlushToWorkAroundMacCrashes();
+
   if (!gr_context()) {
     LOCAL_SET_GL_ERROR(GL_INVALID_OPERATION, "glBeginRasterCHROMIUM",
                        "chromium_raster_transport not enabled via attribs");
diff --git a/gpu/command_buffer/service/raster_decoder_unittest.cc b/gpu/command_buffer/service/raster_decoder_unittest.cc
index 275f7479..c05ba1f7 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest.cc
@@ -5,6 +5,9 @@
 #include "gpu/command_buffer/service/raster_decoder.h"
 
 #include <limits>
+#include <memory>
+#include <string>
+#include <utility>
 
 #include "base/bind_helpers.h"
 #include "base/command_line.h"
@@ -18,6 +21,7 @@
 #include "gpu/command_buffer/service/query_manager.h"
 #include "gpu/command_buffer/service/raster_decoder_unittest_base.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
+#include "gpu/command_buffer/service/shared_image_factory.h"
 #include "gpu/command_buffer/service/shared_image_manager.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -52,10 +56,10 @@
   RasterDecoderTest() = default;
 };
 
-INSTANTIATE_TEST_CASE_P(Service, RasterDecoderTest, ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(Service,
-                        RasterDecoderManualInitTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, RasterDecoderTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         RasterDecoderManualInitTest,
+                         ::testing::Bool());
 
 const GLsync kGlSync = reinterpret_cast<GLsync>(0xdeadbeef);
 
@@ -148,6 +152,53 @@
   EXPECT_FALSE(query->IsPending());
 }
 
+TEST_P(RasterDecoderTest, CopyTexSubImage2DSizeMismatch) {
+  shared_context_state_->set_need_context_state_reset(true);
+  // Create uninitialized source texture.
+  gpu::Mailbox source_texture_mailbox =
+      CreateFakeTexture(kNewServiceId, viz::ResourceFormat::RGBA_8888,
+                        /*width=*/1, /*height=*/1,
+                        /*cleared=*/true);
+  GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
+  CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_);
+
+  auto representation =
+      group().shared_image_representation_factory()->ProduceGLTexture(
+          client_texture_mailbox_);
+  gles2::Texture* dest_texture = representation->GetTexture();
+
+  {
+    // This will initialize the bottom right corner of destination.
+    SetScopedTextureBinderExpectations(GL_TEXTURE_2D);
+    auto& cmd = *GetImmediateAs<CopySubTextureINTERNALImmediate>();
+    cmd.Init(1, 1, 0, 0, 1, 1, mailboxes);
+    EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
+    EXPECT_EQ(GL_NO_ERROR, GetGLError());
+    EXPECT_EQ(dest_texture->GetLevelClearedRect(GL_TEXTURE_2D, 0),
+              gfx::Rect(1, 1, 1, 1));
+  }
+
+  {
+    // Dest rect outside of dest bounds
+    auto& cmd = *GetImmediateAs<CopySubTextureINTERNALImmediate>();
+    cmd.Init(2, 2, 0, 0, 1, 1, mailboxes);
+    EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
+    EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+    EXPECT_EQ(dest_texture->GetLevelClearedRect(GL_TEXTURE_2D, 0),
+              gfx::Rect(1, 1, 1, 1));
+  }
+
+  {
+    // Source rect outside of source bounds
+    auto& cmd = *GetImmediateAs<CopySubTextureINTERNALImmediate>();
+    cmd.Init(0, 0, 0, 0, 2, 2, mailboxes);
+    EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
+    EXPECT_EQ(GL_INVALID_VALUE, GetGLError());
+    EXPECT_EQ(dest_texture->GetLevelClearedRect(GL_TEXTURE_2D, 0),
+              gfx::Rect(1, 1, 1, 1));
+  }
+}
+
 TEST_P(RasterDecoderTest, CopyTexSubImage2DTwiceClearsUnclearedTexture) {
   shared_context_state_->set_need_context_state_reset(true);
   // Create uninitialized source texture.
@@ -155,6 +206,8 @@
       CreateFakeTexture(kNewServiceId, viz::ResourceFormat::RGBA_8888,
                         /*width=*/2, /*height=*/2,
                         /*cleared=*/false);
+  GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
+  CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_);
 
   // This will initialize the top half of destination.
   {
@@ -165,8 +218,6 @@
                                   GL_UNSIGNED_BYTE, 0, 0, 2, 2, 0);
     SetScopedTextureBinderExpectations(GL_TEXTURE_2D);
     auto& cmd = *GetImmediateAs<CopySubTextureINTERNALImmediate>();
-    GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
-    CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_);
     cmd.Init(0, 0, 0, 0, 2, 1, mailboxes);
     EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
   }
@@ -180,15 +231,14 @@
                                   GL_UNSIGNED_BYTE, 0, 1, 2, 1, 0);
     SetScopedTextureBinderExpectations(GL_TEXTURE_2D);
     auto& cmd = *GetImmediateAs<CopySubTextureINTERNALImmediate>();
-    GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
-    CopyMailboxes(mailboxes, source_texture_mailbox, client_texture_mailbox_);
     cmd.Init(1, 1, 0, 0, 1, 1, mailboxes);
     EXPECT_EQ(error::kNoError, ExecuteImmediateCmd(cmd, sizeof(mailboxes)));
   }
 
-  auto* texture = gles2::Texture::CheckedCast(
-      group().mailbox_manager()->ConsumeTexture(client_texture_mailbox_));
-  EXPECT_TRUE(texture->SafeToRenderFrom());
+  auto representation =
+      group().shared_image_representation_factory()->ProduceGLTexture(
+          client_texture_mailbox_);
+  EXPECT_TRUE(representation->GetTexture()->SafeToRenderFrom());
 }
 
 TEST_P(RasterDecoderManualInitTest, CopyTexSubImage2DValidateColorFormat) {
@@ -202,7 +252,6 @@
       CreateFakeTexture(kNewServiceId, viz::ResourceFormat::RED_8,
                         /*width=*/2, /*height=*/2, /*cleared=*/true);
 
-  SetScopedTextureBinderExpectations(GL_TEXTURE_2D);
   auto& copy_cmd = *GetImmediateAs<CopySubTextureINTERNALImmediate>();
   GLbyte mailboxes[sizeof(gpu::Mailbox) * 2];
   CopyMailboxes(mailboxes, client_texture_mailbox_, dest_texture_mailbox);
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_1.cc b/gpu/command_buffer/service/raster_decoder_unittest_1.cc
index cedfca9e..717f5f5 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_1.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_1.cc
@@ -33,7 +33,7 @@
   RasterDecoderTest1() = default;
 };
 
-INSTANTIATE_TEST_CASE_P(Service, RasterDecoderTest1, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, RasterDecoderTest1, ::testing::Bool());
 
 #include "gpu/command_buffer/service/raster_decoder_unittest_1_autogen.h"
 
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.cc b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
index d8a4c8e..e983bb9 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.cc
@@ -10,6 +10,7 @@
 #include <algorithm>
 #include <memory>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "base/bind_helpers.h"
@@ -19,6 +20,7 @@
 #include "components/viz/common/resources/resource_format_utils.h"
 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
 #include "gpu/command_buffer/common/raster_cmd_format.h"
+#include "gpu/command_buffer/common/shared_image_usage.h"
 #include "gpu/command_buffer/service/context_group.h"
 #include "gpu/command_buffer/service/copy_texture_chromium_mock.h"
 #include "gpu/command_buffer/service/gpu_switches.h"
@@ -27,6 +29,7 @@
 #include "gpu/command_buffer/service/program_manager.h"
 #include "gpu/command_buffer/service/service_utils.h"
 #include "gpu/command_buffer/service/shared_context_state.h"
+#include "gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "gpu/command_buffer/service/vertex_attrib_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -61,6 +64,7 @@
       shared_memory_base_(nullptr),
       ignore_cached_state_for_test_(GetParam()),
       shader_translator_cache_(gpu_preferences_),
+      memory_tracker_(nullptr),
       copy_texture_manager_(nullptr) {
   memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_));
 }
@@ -148,19 +152,13 @@
     GLsizei width,
     GLsizei height,
     bool cleared) {
-  // Create texture and temporary ref.
-  const GLuint kTempClientId = next_fake_texture_client_id_++;
-  auto* temp_ref =
-      group_->texture_manager()->CreateTexture(kTempClientId, service_id);
-  group_->texture_manager()->SetTarget(temp_ref, GL_TEXTURE_2D);
-  group_->texture_manager()->SetLevelInfo(
-      temp_ref, GL_TEXTURE_2D, 0, viz::GLInternalFormat(resource_format),
-      /*width=*/width, /*height=*/height, 1, 0,
-      viz::GLDataFormat(resource_format), viz::GLDataType(resource_format),
-      cleared ? gfx::Rect(width, height) : gfx::Rect());
-  gpu::Mailbox mailbox = gpu::Mailbox::Generate();
-  group_->mailbox_manager()->ProduceTexture(mailbox, temp_ref->texture());
-  EXPECT_EQ(GL_NO_ERROR, GetGLError());
+  gpu::Mailbox mailbox = gpu::Mailbox::GenerateForSharedImage();
+  std::unique_ptr<SharedImageBacking> backing =
+      SharedImageBackingFactoryGLTexture::CreateSharedImageForTest(
+          mailbox, GL_TEXTURE_2D, service_id, cleared, resource_format,
+          gfx::Size(width, height), SHARED_IMAGE_USAGE_RASTER);
+  shared_images_.push_back(
+      shared_image_manager_.Register(std::move(backing), &memory_tracker_));
   return mailbox;
 }
 
@@ -295,6 +293,9 @@
   group_->Destroy(mock_decoder_.get(), false);
   command_buffer_service_.reset();
   command_buffer_service_for_mock_decoder_.reset();
+  for (auto& image : shared_images_)
+    image->OnContextLost();
+  shared_images_.clear();
   ::gl::MockGLInterface::SetGLInterface(nullptr);
   gl_.reset();
   gl::init::ShutdownGL(false);
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_base.h b/gpu/command_buffer/service/raster_decoder_unittest_base.h
index 6ac4cf08..09bb7967 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_base.h
+++ b/gpu/command_buffer/service/raster_decoder_unittest_base.h
@@ -32,6 +32,7 @@
 #include "gpu/command_buffer/service/service_discardable_manager.h"
 #include "gpu/command_buffer/service/shader_manager.h"
 #include "gpu/command_buffer/service/shared_image_manager.h"
+#include "gpu/command_buffer/service/shared_image_representation.h"
 #include "gpu/command_buffer/service/test_helper.h"
 #include "gpu/command_buffer/service/texture_manager.h"
 #include "gpu/config/gpu_driver_bug_workarounds.h"
@@ -270,6 +271,9 @@
   ServiceDiscardableManager discardable_manager_;
   SharedImageManager shared_image_manager_;
   scoped_refptr<gles2::ContextGroup> group_;
+  MemoryTypeTracker memory_tracker_;
+  std::vector<std::unique_ptr<SharedImageRepresentationFactoryRef>>
+      shared_images_;
   base::MessageLoop message_loop_;
   gles2::MockCopyTextureResourceManager* copy_texture_manager_;  // not owned
   GLuint next_fake_texture_client_id_ = 271828;
diff --git a/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc b/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc
index 3c47d68..8d92022 100644
--- a/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc
+++ b/gpu/command_buffer/service/raster_decoder_unittest_context_lost.cc
@@ -97,7 +97,7 @@
   EXPECT_EQ(error::kUnknown, GetContextLostReason());
 }
 
-INSTANTIATE_TEST_CASE_P(Service, RasterDecoderOOMTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service, RasterDecoderOOMTest, ::testing::Bool());
 
 class RasterDecoderLostContextTest : public RasterDecoderManualInitTest {
  protected:
@@ -173,29 +173,6 @@
   ClearCurrentDecoderError();
 }
 
-TEST_P(RasterDecoderLostContextTest, TextureDestroyAfterLostFromMakeCurrent) {
-  Init(/*has_robustness=*/true);
-
-  CreateFakeTexture(kNewServiceId, viz::ResourceFormat::RGBA_8888,
-                    /*width=*/2, /*height=*/2,
-                    /*cleared=*/false);
-
-  // The texture should never be deleted at the GL level.
-  EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(kNewServiceId)))
-      .Times(0)
-      .RetiresOnSaturation();
-
-  // Force context lost for MakeCurrent().
-  EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(false));
-  // Expect the group to be lost.
-  EXPECT_CALL(*mock_decoder_, MarkContextLost(error::kUnknown)).Times(1);
-
-  decoder_->MakeCurrent();
-  EXPECT_TRUE(decoder_->WasContextLost());
-  EXPECT_EQ(error::kMakeCurrentFailed, GetContextLostReason());
-  ClearCurrentDecoderError();
-}
-
 TEST_P(RasterDecoderLostContextTest, QueryDestroyAfterLostFromMakeCurrent) {
   Init(/*has_robustness=*/false);
 
@@ -302,9 +279,9 @@
   ClearCurrentDecoderError();
 }
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        RasterDecoderLostContextTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         RasterDecoderLostContextTest,
+                         ::testing::Bool());
 
 }  // namespace raster
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shader_translator_unittest.cc b/gpu/command_buffer/service/shader_translator_unittest.cc
index 214725c8..c97fd52 100644
--- a/gpu/command_buffer/service/shader_translator_unittest.cc
+++ b/gpu/command_buffer/service/shader_translator_unittest.cc
@@ -550,7 +550,7 @@
 // certain version of GLSL to be guaranteed to be supported. Test
 // that ShaderTranslator produces a GLSL shader with the exact
 // specified GLSL version for each known OpenGL version.
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     KnownOpenGLContexts,
     ShaderTranslatorOutputVersionTest,
     testing::Values(make_gl_glsl_tuple("4.5", "#version 450\n"),
@@ -570,16 +570,16 @@
 // similar shader. We do not expect that future 3.3+ specs contain
 // the "all eariler GLSL versions" clause, since 3.3 did not contain
 // it either.
-INSTANTIATE_TEST_CASE_P(OldOrUnknownOpenGLContexts,
-                        ShaderTranslatorOutputVersionTest,
-                        testing::Values(make_gl_glsl_tuple("3.4", ""),
-                                        make_gl_glsl_tuple("2.0", "")));
+INSTANTIATE_TEST_SUITE_P(OldOrUnknownOpenGLContexts,
+                         ShaderTranslatorOutputVersionTest,
+                         testing::Values(make_gl_glsl_tuple("3.4", ""),
+                                         make_gl_glsl_tuple("2.0", "")));
 
 // Test data for the above test. Cases for the future OpenGL versions. The
 // code assumes that the future OpenGL specs specify the clause that all
 // earlier GLSL versions are supported. We select the highest GLSL
 // version known at the time of writing.
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     BackwardsCompatibleFutureOpenGLContexts,
     ShaderTranslatorOutputVersionTest,
     testing::Values(make_gl_glsl_tuple("5.0", "#version 450\n"),
@@ -589,13 +589,13 @@
 // contexts, the shader is such that GLSL 1.0 is used. The translator
 // selects GLSL 1.0 by not output any version at the moment, though we
 // do not know if that would be correct for the future OpenGL ES specs.
-INSTANTIATE_TEST_CASE_P(OpenGLESContexts,
-                        ShaderTranslatorOutputVersionTest,
-                        testing::Values(make_gl_glsl_tuple("opengl es 2.0", ""),
-                                        make_gl_glsl_tuple("opengl es 3.0", ""),
-                                        make_gl_glsl_tuple("opengl es 3.1", ""),
-                                        make_gl_glsl_tuple("opengl es 3.2",
-                                                           "")));
+INSTANTIATE_TEST_SUITE_P(
+    OpenGLESContexts,
+    ShaderTranslatorOutputVersionTest,
+    testing::Values(make_gl_glsl_tuple("opengl es 2.0", ""),
+                    make_gl_glsl_tuple("opengl es 3.0", ""),
+                    make_gl_glsl_tuple("opengl es 3.1", ""),
+                    make_gl_glsl_tuple("opengl es 3.2", "")));
 
 }  // namespace gles2
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
index aad479c..d7a1e7b8 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
@@ -6,6 +6,10 @@
 
 #include <sync/sync.h>
 
+#include <algorithm>
+#include <memory>
+#include <utility>
+
 #include "base/android/android_hardware_buffer_compat.h"
 #include "base/android/scoped_hardware_buffer_handle.h"
 #include "base/logging.h"
@@ -716,6 +720,10 @@
     max_gl_texture_size_ =
         std::min(max_gl_texture_size_, workarounds.max_texture_size);
   }
+  // Ensure max_texture_size_ is less than INT_MAX so that gfx::Rect and friends
+  // can be used to accurately represent all valid sub-rects, with overflow
+  // cases, clamped to INT_MAX, always invalid.
+  max_gl_texture_size_ = std::min(max_gl_texture_size_, INT_MAX - 1);
 }
 
 SharedImageBackingFactoryAHB::~SharedImageBackingFactoryAHB() = default;
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
index 321836d..98f6df2 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.cc
@@ -4,6 +4,10 @@
 
 #include "gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h"
 
+#include <algorithm>
+#include <string>
+#include <utility>
+
 #include "base/feature_list.h"
 #include "base/trace_event/memory_dump_manager.h"
 #include "base/trace_event/trace_event.h"
@@ -532,6 +536,10 @@
     max_texture_size_ =
         std::min(max_texture_size_, workarounds.max_texture_size);
   }
+  // Ensure max_texture_size_ is less than INT_MAX so that gfx::Rect and friends
+  // can be used to accurately represent all valid sub-rects, with overflow
+  // cases, clamped to INT_MAX, always invalid.
+  max_texture_size_ = std::min(max_texture_size_, INT_MAX - 1);
 
   // TODO(piman): Can we extract the logic out of FeatureInfo?
   scoped_refptr<gles2::FeatureInfo> feature_info =
@@ -769,11 +777,11 @@
                            pixel_data.data());
   }
 
-  return MakeBacking(mailbox, target, service_id, image, gles2::Texture::BOUND,
-                     level_info_internal_format, format_info.gl_format,
-                     format_info.gl_type, format_info.swizzle,
-                     pixel_data.empty() ? is_cleared : true, format, size,
-                     color_space, usage);
+  return MakeBacking(
+      use_passthrough_, mailbox, target, service_id, image,
+      gles2::Texture::BOUND, level_info_internal_format, format_info.gl_format,
+      format_info.gl_type, format_info.swizzle,
+      pixel_data.empty() ? is_cleared : true, format, size, color_space, usage);
 }
 
 std::unique_ptr<SharedImageBacking>
@@ -857,9 +865,25 @@
   GLenum gl_type =
       gles2::TextureManager::ExtractTypeFromStorageFormat(internal_format);
 
-  return MakeBacking(mailbox, target, service_id, image, image_state,
-                     internal_format, gl_format, gl_type, nullptr, true, format,
-                     size, color_space, usage);
+  return MakeBacking(use_passthrough_, mailbox, target, service_id, image,
+                     image_state, internal_format, gl_format, gl_type, nullptr,
+                     true, format, size, color_space, usage);
+}
+
+std::unique_ptr<SharedImageBacking>
+SharedImageBackingFactoryGLTexture::CreateSharedImageForTest(
+    const Mailbox& mailbox,
+    GLenum target,
+    GLuint service_id,
+    bool is_cleared,
+    viz::ResourceFormat format,
+    const gfx::Size& size,
+    uint32_t usage) {
+  return MakeBacking(false, mailbox, target, service_id, nullptr,
+                     gles2::Texture::UNBOUND, viz::GLInternalFormat(format),
+                     viz::GLDataFormat(format), viz::GLDataType(format),
+                     nullptr, is_cleared, format, size, gfx::ColorSpace(),
+                     usage);
 }
 
 scoped_refptr<gl::GLImage> SharedImageBackingFactoryGLTexture::MakeGLImage(
@@ -889,6 +913,7 @@
 
 std::unique_ptr<SharedImageBacking>
 SharedImageBackingFactoryGLTexture::MakeBacking(
+    bool passthrough,
     const Mailbox& mailbox,
     GLenum target,
     GLuint service_id,
@@ -903,7 +928,7 @@
     const gfx::Size& size,
     const gfx::ColorSpace& color_space,
     uint32_t usage) {
-  if (use_passthrough_) {
+  if (passthrough) {
     scoped_refptr<gles2::TexturePassthrough> passthrough_texture =
         base::MakeRefCounted<gles2::TexturePassthrough>(service_id, target);
     if (image)
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
index 5705b92..31818fa 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture.h
@@ -65,13 +65,23 @@
       const gfx::ColorSpace& color_space,
       uint32_t usage) override;
 
+  static std::unique_ptr<SharedImageBacking> CreateSharedImageForTest(
+      const Mailbox& mailbox,
+      GLenum target,
+      GLuint service_id,
+      bool is_cleared,
+      viz::ResourceFormat format,
+      const gfx::Size& size,
+      uint32_t usage);
+
  private:
   scoped_refptr<gl::GLImage> MakeGLImage(int client_id,
                                          gfx::GpuMemoryBufferHandle handle,
                                          gfx::BufferFormat format,
                                          SurfaceHandle surface_handle,
                                          const gfx::Size& size);
-  std::unique_ptr<SharedImageBacking> MakeBacking(
+  static std::unique_ptr<SharedImageBacking> MakeBacking(
+      bool passthrough,
       const Mailbox& mailbox,
       GLenum target,
       GLuint service_id,
@@ -86,6 +96,7 @@
       const gfx::Size& size,
       const gfx::ColorSpace& color_space,
       uint32_t usage);
+
   struct FormatInfo {
     FormatInfo();
     ~FormatInfo();
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
index dabbe757..dc32c55 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_gl_texture_unittest.cc
@@ -666,12 +666,12 @@
   EXPECT_EQ(format, shm_image->format());
 }
 
-INSTANTIATE_TEST_CASE_P(Service,
-                        SharedImageBackingFactoryGLTextureTest,
-                        ::testing::Bool());
-INSTANTIATE_TEST_CASE_P(Service,
-                        SharedImageBackingFactoryGLTextureWithGMBTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         SharedImageBackingFactoryGLTextureTest,
+                         ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(Service,
+                         SharedImageBackingFactoryGLTextureWithGMBTest,
+                         ::testing::Bool());
 
 }  // anonymous namespace
 }  // namespace gpu
diff --git a/gpu/command_buffer/service/shared_image_representation.h b/gpu/command_buffer/service/shared_image_representation.h
index d9a3464..9f58a8f 100644
--- a/gpu/command_buffer/service/shared_image_representation.h
+++ b/gpu/command_buffer/service/shared_image_representation.h
@@ -71,6 +71,24 @@
 class GPU_GLES2_EXPORT SharedImageRepresentationGLTexture
     : public SharedImageRepresentation {
  public:
+  class ScopedAccess {
+   public:
+    ScopedAccess(SharedImageRepresentationGLTexture* representation,
+                 GLenum mode)
+        : representation_(representation),
+          success_(representation_->BeginAccess(mode)) {}
+    ~ScopedAccess() {
+      if (success_)
+        representation_->EndAccess();
+    }
+
+    bool success() const { return success_; }
+
+   private:
+    SharedImageRepresentationGLTexture* representation_;
+    bool success_;
+  };
+
   SharedImageRepresentationGLTexture(SharedImageManager* manager,
                                      SharedImageBacking* backing,
                                      MemoryTypeTracker* tracker)
@@ -87,6 +105,24 @@
 class GPU_GLES2_EXPORT SharedImageRepresentationGLTexturePassthrough
     : public SharedImageRepresentation {
  public:
+  class ScopedAccess {
+   public:
+    ScopedAccess(SharedImageRepresentationGLTexturePassthrough* representation,
+                 GLenum mode)
+        : representation_(representation),
+          success_(representation_->BeginAccess(mode)) {}
+    ~ScopedAccess() {
+      if (success_)
+        representation_->EndAccess();
+    }
+
+    bool success() const { return success_; }
+
+   private:
+    SharedImageRepresentationGLTexturePassthrough* representation_;
+    bool success_;
+  };
+
   SharedImageRepresentationGLTexturePassthrough(SharedImageManager* manager,
                                                 SharedImageBacking* backing,
                                                 MemoryTypeTracker* tracker)
diff --git a/gpu/command_buffer/service/texture_manager_unittest.cc b/gpu/command_buffer/service/texture_manager_unittest.cc
index 7a61c07..032b734 100644
--- a/gpu/command_buffer/service/texture_manager_unittest.cc
+++ b/gpu/command_buffer/service/texture_manager_unittest.cc
@@ -2134,9 +2134,9 @@
 static const GLenum kTextureTargets[] = {GL_TEXTURE_2D, GL_TEXTURE_EXTERNAL_OES,
                                          GL_TEXTURE_RECTANGLE_ARB, };
 
-INSTANTIATE_TEST_CASE_P(Target,
-                        ProduceConsumeTextureTest,
-                        ::testing::ValuesIn(kTextureTargets));
+INSTANTIATE_TEST_SUITE_P(Target,
+                         ProduceConsumeTextureTest,
+                         ::testing::ValuesIn(kTextureTargets));
 
 TEST_F(ProduceConsumeTextureTest, ProduceConsumeCube) {
   manager_->SetTarget(texture_ref_.get(), GL_TEXTURE_CUBE_MAP);
diff --git a/gpu/command_buffer/tests/compressed_texture_test.cc b/gpu/command_buffer/tests/compressed_texture_test.cc
index b941fb6..c21bef2 100644
--- a/gpu/command_buffer/tests/compressed_texture_test.cc
+++ b/gpu/command_buffer/tests/compressed_texture_test.cc
@@ -246,8 +246,8 @@
   GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
   GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
 };
-INSTANTIATE_TEST_CASE_P(Format,
-                        CompressedTextureTest,
-                        ::testing::ValuesIn(kFormats));
+INSTANTIATE_TEST_SUITE_P(Format,
+                         CompressedTextureTest,
+                         ::testing::ValuesIn(kFormats));
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_apply_screen_space_antialiasing_CHROMIUM_unittest.cc b/gpu/command_buffer/tests/gl_apply_screen_space_antialiasing_CHROMIUM_unittest.cc
index b943e111..2c1c95d6 100644
--- a/gpu/command_buffer/tests/gl_apply_screen_space_antialiasing_CHROMIUM_unittest.cc
+++ b/gpu/command_buffer/tests/gl_apply_screen_space_antialiasing_CHROMIUM_unittest.cc
@@ -50,6 +50,15 @@
       return;
     }
 
+    // Antialiasing won't be enabled if framebuffer CMAA is disabled via GPU
+    // driver workarounds 'disable_framebuffer_cmaa'.
+    available_ = !gl_.workarounds().disable_framebuffer_cmaa;
+    if (!available_) {
+      LOG(INFO) << "'disable_framebuffer_cmaa' workaround applied. "
+                   "Skipping test...";
+      return;
+    }
+
     CreateAndBindDestinationTextureAndFBO(GL_TEXTURE_2D);
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                  nullptr);
diff --git a/gpu/command_buffer/tests/gl_bind_uniform_location_unittest.cc b/gpu/command_buffer/tests/gl_bind_uniform_location_unittest.cc
index 7c02ade..daa555c 100644
--- a/gpu/command_buffer/tests/gl_bind_uniform_location_unittest.cc
+++ b/gpu/command_buffer/tests/gl_bind_uniform_location_unittest.cc
@@ -402,9 +402,9 @@
   EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
 }
 
-INSTANTIATE_TEST_CASE_P(WithAndWithoutShaderNameMapping,
-                        BindUniformLocationTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutShaderNameMapping,
+                         BindUniformLocationTest,
+                         ::testing::Bool());
 
 }  // namespace gpu
 
diff --git a/gpu/command_buffer/tests/gl_chromium_path_rendering_unittest.cc b/gpu/command_buffer/tests/gl_chromium_path_rendering_unittest.cc
index 375b568..9c233d2 100644
--- a/gpu/command_buffer/tests/gl_chromium_path_rendering_unittest.cc
+++ b/gpu/command_buffer/tests/gl_chromium_path_rendering_unittest.cc
@@ -1779,8 +1779,8 @@
   EXPECT_EQ(static_cast<GLenum>(GL_INVALID_OPERATION), glGetError());
 }
 
-INSTANTIATE_TEST_CASE_P(WithAndWithoutShaderNameMapping,
-                        CHROMIUMPathRenderingWithTexturingTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(WithAndWithoutShaderNameMapping,
+                         CHROMIUMPathRenderingWithTexturingTest,
+                         ::testing::Bool());
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_clear_framebuffer_unittest.cc b/gpu/command_buffer/tests/gl_clear_framebuffer_unittest.cc
index a12953a5..1857394 100644
--- a/gpu/command_buffer/tests/gl_clear_framebuffer_unittest.cc
+++ b/gpu/command_buffer/tests/gl_clear_framebuffer_unittest.cc
@@ -112,9 +112,9 @@
   glDrawArrays(GL_TRIANGLES, 0, 6);
 }
 
-INSTANTIATE_TEST_CASE_P(GLClearFramebufferTestWithParam,
-                        GLClearFramebufferTest,
-                        ::testing::Values(true, false));
+INSTANTIATE_TEST_SUITE_P(GLClearFramebufferTestWithParam,
+                         GLClearFramebufferTest,
+                         ::testing::Values(true, false));
 
 TEST_P(GLClearFramebufferTest, ClearColor) {
   if (!IsApplicable()) {
diff --git a/gpu/command_buffer/tests/gl_copy_tex_image_2d_workaround_unittest.cc b/gpu/command_buffer/tests/gl_copy_tex_image_2d_workaround_unittest.cc
index f61f1887..4c6c6c7 100644
--- a/gpu/command_buffer/tests/gl_copy_tex_image_2d_workaround_unittest.cc
+++ b/gpu/command_buffer/tests/gl_copy_tex_image_2d_workaround_unittest.cc
@@ -99,9 +99,9 @@
   GLManager gl_;
 };
 
-INSTANTIATE_TEST_CASE_P(GLCopyTexImage2DWorkaroundTestWithParam,
-                        GLCopyTexImage2DWorkaroundTest,
-                        ::testing::Values(GL_RGBA));
+INSTANTIATE_TEST_SUITE_P(GLCopyTexImage2DWorkaroundTestWithParam,
+                         GLCopyTexImage2DWorkaroundTest,
+                         ::testing::Values(GL_RGBA));
 
 TEST_P(GLCopyTexImage2DWorkaroundTest, UseIntermediaryTexture) {
   int width = 1;
diff --git a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
index 92a1da40..74b7dde 100644
--- a/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
+++ b/gpu/command_buffer/tests/gl_copy_texture_CHROMIUM_unittest.cc
@@ -618,13 +618,13 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(CopyType,
-                        GLCopyTextureCHROMIUMTest,
-                        ::testing::ValuesIn(kCopyTypes));
+INSTANTIATE_TEST_SUITE_P(CopyType,
+                         GLCopyTextureCHROMIUMTest,
+                         ::testing::ValuesIn(kCopyTypes));
 
-INSTANTIATE_TEST_CASE_P(CopyType,
-                        GLCopyTextureCHROMIUMES3Test,
-                        ::testing::ValuesIn(kCopyTypes));
+INSTANTIATE_TEST_SUITE_P(CopyType,
+                         GLCopyTextureCHROMIUMES3Test,
+                         ::testing::ValuesIn(kCopyTypes));
 
 // Test to ensure that the basic functionality of the extension works.
 TEST_P(GLCopyTextureCHROMIUMTest, Basic) {
diff --git a/gpu/command_buffer/tests/gl_cube_map_texture_unittest.cc b/gpu/command_buffer/tests/gl_cube_map_texture_unittest.cc
index 6c11b55..2fde0c5c 100644
--- a/gpu/command_buffer/tests/gl_cube_map_texture_unittest.cc
+++ b/gpu/command_buffer/tests/gl_cube_map_texture_unittest.cc
@@ -65,9 +65,9 @@
   GLuint framebuffer_id_;
 };
 
-INSTANTIATE_TEST_CASE_P(GLCubeMapTextureTests,
-                        GLCubeMapTextureTest,
-                        ::testing::ValuesIn(kCubeMapTextureTargets));
+INSTANTIATE_TEST_SUITE_P(GLCubeMapTextureTests,
+                         GLCubeMapTextureTest,
+                         ::testing::ValuesIn(kCubeMapTextureTargets));
 
 TEST_P(GLCubeMapTextureTest, TexImage2DAfterFBOBinding) {
   GLenum cube_map_target = GetParam();
diff --git a/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc b/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc
index c8585d48..be7548a 100644
--- a/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc
+++ b/gpu/command_buffer/tests/gl_ext_blend_func_extended_unittest.cc
@@ -684,12 +684,12 @@
   EXPECT_TRUE(LinkProgram());
 }
 
-INSTANTIATE_TEST_CASE_P(TranslatorVariants,
-                        EXTBlendFuncExtendedDrawTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(TranslatorVariants,
+                         EXTBlendFuncExtendedDrawTest,
+                         ::testing::Bool());
 
-INSTANTIATE_TEST_CASE_P(TranslatorVariants,
-                        EXTBlendFuncExtendedES3DrawTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(TranslatorVariants,
+                         EXTBlendFuncExtendedES3DrawTest,
+                         ::testing::Bool());
 
 }  // namespace gpu
diff --git a/gpu/command_buffer/tests/gl_request_extension_unittest.cc b/gpu/command_buffer/tests/gl_request_extension_unittest.cc
index ddf8f39..663c954 100644
--- a/gpu/command_buffer/tests/gl_request_extension_unittest.cc
+++ b/gpu/command_buffer/tests/gl_request_extension_unittest.cc
@@ -92,10 +92,10 @@
     EXPECT_GE(extensions.size(), extensions_size_before_request);
   }
 }
-INSTANTIATE_TEST_CASE_P(WithContextTypes,
-                        RequestExtensionCHROMIUMTest,
-                        ::testing::Values(CONTEXT_TYPE_WEBGL1,
-                                          CONTEXT_TYPE_WEBGL2,
-                                          CONTEXT_TYPE_OPENGLES2,
-                                          CONTEXT_TYPE_OPENGLES3));
+INSTANTIATE_TEST_SUITE_P(WithContextTypes,
+                         RequestExtensionCHROMIUMTest,
+                         ::testing::Values(CONTEXT_TYPE_WEBGL1,
+                                           CONTEXT_TYPE_WEBGL2,
+                                           CONTEXT_TYPE_OPENGLES2,
+                                           CONTEXT_TYPE_OPENGLES3));
 }
diff --git a/gpu/command_buffer/tests/gl_virtual_contexts_unittest.cc b/gpu/command_buffer/tests/gl_virtual_contexts_unittest.cc
index d1a38861..4abbd507 100644
--- a/gpu/command_buffer/tests/gl_virtual_contexts_unittest.cc
+++ b/gpu/command_buffer/tests/gl_virtual_contexts_unittest.cc
@@ -439,9 +439,9 @@
 
 };
 
-INSTANTIATE_TEST_CASE_P(WithWorkarounds,
-                        GLVirtualContextsTest,
-                        ::testing::ValuesIn(workarounds_cases));
+INSTANTIATE_TEST_SUITE_P(WithWorkarounds,
+                         GLVirtualContextsTest,
+                         ::testing::ValuesIn(workarounds_cases));
 
 }  // namespace gpu
 
diff --git a/gpu/config/gpu_finch_features.cc b/gpu/config/gpu_finch_features.cc
index a874806..087c594 100644
--- a/gpu/config/gpu_finch_features.cc
+++ b/gpu/config/gpu_finch_features.cc
@@ -44,6 +44,12 @@
 const base::Feature kDirectCompositionPreferNV12Overlays{
     "DirectCompositionPreferNV12Overlays", base::FEATURE_ENABLED_BY_DEFAULT};
 
+// Allow putting a video swapchain underneath the main swapchain, so overlays
+// can be used even if there are controls on top of the video. It can be
+// enabled only when overlay is supported.
+const base::Feature kDirectCompositionUnderlays{
+    "DirectCompositionUnderlays", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Causes us to use the SharedImageManager, removing support for the old
 // mailbox system. Any consumers of the GPU process using the old mailbox
 // system will experience undefined results.
diff --git a/gpu/config/gpu_finch_features.h b/gpu/config/gpu_finch_features.h
index 833ec88c..68efa19ae 100644
--- a/gpu/config/gpu_finch_features.h
+++ b/gpu/config/gpu_finch_features.h
@@ -29,6 +29,8 @@
 
 GPU_EXPORT extern const base::Feature kDirectCompositionPreferNV12Overlays;
 
+GPU_EXPORT extern const base::Feature kDirectCompositionUnderlays;
+
 GPU_EXPORT extern const base::Feature kSharedImageManager;
 
 GPU_EXPORT extern const base::Feature kUseDCOverlaysForSoftwareProtectedVideo;
diff --git a/gpu/ipc/host/shader_disk_cache.cc b/gpu/ipc/host/shader_disk_cache.cc
index b758ce8f..339c8ce6 100644
--- a/gpu/ipc/host/shader_disk_cache.cc
+++ b/gpu/ipc/host/shader_disk_cache.cc
@@ -263,8 +263,8 @@
   op_type_ = WRITE_DATA;
   auto io_buf = base::MakeRefCounted<net::StringIOBuffer>(shader_);
   return entry_->WriteData(1, 0, io_buf.get(), shader_.length(),
-                           base::Bind(&ShaderDiskCacheEntry::OnOpComplete,
-                                      weak_ptr_factory_.GetWeakPtr()),
+                           base::BindOnce(&ShaderDiskCacheEntry::OnOpComplete,
+                                          weak_ptr_factory_.GetWeakPtr()),
                            false);
 }
 
@@ -357,8 +357,8 @@
   op_type_ = READ_COMPLETE;
   buf_ = base::MakeRefCounted<net::IOBufferWithSize>(entry_->GetDataSize(1));
   return entry_->ReadData(1, 0, buf_.get(), buf_->size(),
-                          base::Bind(&ShaderDiskReadHelper::OnOpComplete,
-                                     weak_ptr_factory_.GetWeakPtr()));
+                          base::BindOnce(&ShaderDiskReadHelper::OnOpComplete,
+                                         weak_ptr_factory_.GetWeakPtr()));
 }
 
 int ShaderDiskReadHelper::ReadComplete(int rv) {
@@ -416,14 +416,15 @@
     switch (op_type_) {
       case VERIFY_CACHE_SETUP:
         rv = cache_->SetAvailableCallback(
-            base::Bind(&ShaderClearHelper::DoClearShaderCache,
-                       weak_ptr_factory_.GetWeakPtr()));
+            base::BindRepeating(&ShaderClearHelper::DoClearShaderCache,
+                                weak_ptr_factory_.GetWeakPtr()));
         op_type_ = DELETE_CACHE;
         break;
       case DELETE_CACHE:
-        rv = cache_->Clear(delete_begin_, delete_end_,
-                           base::Bind(&ShaderClearHelper::DoClearShaderCache,
-                                      weak_ptr_factory_.GetWeakPtr()));
+        rv = cache_->Clear(
+            delete_begin_, delete_end_,
+            base::BindRepeating(&ShaderClearHelper::DoClearShaderCache,
+                                weak_ptr_factory_.GetWeakPtr()));
         op_type_ = TERMINATE;
         break;
       case TERMINATE:
@@ -575,7 +576,7 @@
   int rv = disk_cache::CreateCacheBackend(
       net::SHADER_CACHE, net::CACHE_BACKEND_DEFAULT,
       cache_path_.Append(kGpuCachePath), CacheSizeBytes(), true, nullptr,
-      &backend_, base::Bind(&ShaderDiskCache::CacheCreatedCallback, this));
+      &backend_, base::BindOnce(&ShaderDiskCache::CacheCreatedCallback, this));
 
   if (rv == net::OK)
     cache_available_ = true;
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc
index be3b81a..6489ebd2 100644
--- a/gpu/ipc/service/direct_composition_surface_win.cc
+++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -1238,9 +1238,10 @@
     return false;
   }
 
-  UMA_HISTOGRAM_ENUMERATION(
-      "GPU.DirectComposition.SwapChainFormat2",
-      is_yuv_swapchain_ ? g_overlay_format_used : OverlayFormat::kBGRA);
+  OverlayFormat swap_chain_format =
+      is_yuv_swapchain_ ? g_overlay_format_used : OverlayFormat::kBGRA;
+  UMA_HISTOGRAM_ENUMERATION("GPU.DirectComposition.SwapChainFormat2",
+                            swap_chain_format);
 
   frames_since_color_space_change_++;
 
@@ -1248,10 +1249,25 @@
   if (SUCCEEDED(swap_chain_.CopyTo(swap_chain_media.GetAddressOf()))) {
     DCHECK(swap_chain_media);
     DXGI_FRAME_STATISTICS_MEDIA stats = {};
-    if (SUCCEEDED(swap_chain_media->GetFrameStatisticsMedia(&stats))) {
+    // GetFrameStatisticsMedia fails with DXGI_ERROR_FRAME_STATISTICS_DISJOINT
+    // sometimes, which means an event (such as power cycle) interrupted the
+    // gathering of presentation statistics. In this situation, calling the
+    // function again succeeds but returns with CompositionMode = NONE.
+    // Waiting for the DXGI adapter to finish presenting before calling the
+    // function doesn't get rid of the failure.
+    HRESULT hr = swap_chain_media->GetFrameStatisticsMedia(&stats);
+    if (SUCCEEDED(hr)) {
       base::UmaHistogramSparse("GPU.DirectComposition.CompositionMode",
                                stats.CompositionMode);
       presentation_history_.AddSample(stats.CompositionMode);
+      TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("gpu.service"),
+                           "SwapChainFrameInfo", TRACE_EVENT_SCOPE_THREAD,
+                           "SwapChain.PresentationMode", stats.CompositionMode,
+                           "SwapChain.PixelFormat", swap_chain_format);
+    } else {
+      TRACE_EVENT_INSTANT1(
+          TRACE_DISABLED_BY_DEFAULT("gpu.service"), "SwapChainFrameInfoInvalid",
+          TRACE_EVENT_SCOPE_THREAD, "ErrorCode", static_cast<uint32_t>(hr));
     }
   }
   return true;
diff --git a/headless/BUILD.gn b/headless/BUILD.gn
index 01b8e67..e09816c 100644
--- a/headless/BUILD.gn
+++ b/headless/BUILD.gn
@@ -508,6 +508,10 @@
       "lib/renderer/headless_content_renderer_client.h",
     ]
 
+    if (headless_use_embedded_resources) {
+      defines = [ "HEADLESS_USE_EMBEDDED_RESOURCES" ]
+    }
+
     if (enable_basic_printing) {
       sources += [
         "lib/renderer/headless_print_render_frame_helper_delegate.cc",
diff --git a/headless/lib/browser/headless_request_context_manager.cc b/headless/lib/browser/headless_request_context_manager.cc
index 5483eef..612a1485 100644
--- a/headless/lib/browser/headless_request_context_manager.cc
+++ b/headless/lib/browser/headless_request_context_manager.cc
@@ -23,8 +23,6 @@
 #include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_auth_scheme.h"
 #include "net/http/http_transaction_factory.h"
-#include "net/ssl/channel_id_service.h"
-#include "net/ssl/default_channel_id_store.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_builder.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -404,8 +402,6 @@
     context_params->enable_encrypted_cookies = cookie_encryption_enabled_;
     context_params->cookie_path =
         user_data_path_.Append(FILE_PATH_LITERAL("Cookies"));
-    context_params->channel_id_path =
-        user_data_path_.Append(FILE_PATH_LITERAL("Origin Bound Certs"));
   }
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   if (command_line->HasSwitch(switches::kDiskCacheDir)) {
diff --git a/headless/lib/headless_content_main_delegate.cc b/headless/lib/headless_content_main_delegate.cc
index 60099fa..7809413 100644
--- a/headless/lib/headless_content_main_delegate.cc
+++ b/headless/lib/headless_content_main_delegate.cc
@@ -21,7 +21,6 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "cc/base/switches.h"
-#include "components/crash/content/app/breakpad_linux.h"
 #include "components/crash/core/common/crash_key.h"
 #include "components/viz/common/switches.h"
 #include "content/public/browser/browser_main_runner.h"
@@ -48,6 +47,10 @@
 #include "components/crash/content/app/crashpad.h"
 #endif
 
+#if defined(OS_LINUX)
+#include "components/crash/content/app/breakpad_linux.h"
+#endif
+
 #if !defined(CHROME_MULTIPLE_DLL_BROWSER)
 #include "headless/lib/renderer/headless_content_renderer_client.h"
 #endif
diff --git a/headless/test/data/protocol/emulation/compositor-basic-raf-expected.txt b/headless/test/data/protocol/emulation/compositor-basic-raf-expected.txt
index c6e71e81..44c6beb 100644
--- a/headless/test/data/protocol/emulation/compositor-basic-raf-expected.txt
+++ b/headless/test/data/protocol/emulation/compositor-basic-raf-expected.txt
@@ -1,20 +1,14 @@
 Tests compositor basic rAF operation.
-Advanced to 10ms
-Advanced to 1000ms
-Elasped time: 1000
+Elapsed time: 1000
 Requesting first animation frame
 Animation frame callback #1
-Advanced to 1100ms
-Elasped time: 1100
+Elapsed time: 1100
 Animation frame callback #2
-Advanced to 1200ms
-Elasped time: 1200
+Elapsed time: 1200
 Animation frame callback #3
-Advanced to 1300ms
-Elasped time: 1300
+Elapsed time: 1300
 Animation frame callback #4
-Advanced to 1400ms
-Elasped time: 1400
+Elapsed time: 1400
 Animation frame count: 4
 
 Animation frame callback #5
diff --git a/headless/test/data/protocol/emulation/compositor-basic-raf.js b/headless/test/data/protocol/emulation/compositor-basic-raf.js
index 7433842..0c726870 100644
--- a/headless/test/data/protocol/emulation/compositor-basic-raf.js
+++ b/headless/test/data/protocol/emulation/compositor-basic-raf.js
@@ -14,25 +14,11 @@
     testRunner.log(text);
   });
 
-  dp.Emulation.onVirtualTimeAdvanced(data => {
-    // Debug chrome schedules stray tasks that break this test.
-    // Our numbers are round, so we prevent this flake 999 times of 1000.
-    const time = data.params.virtualTimeElapsed;
-    if (time !== Math.round(time))
-      return;
-    testRunner.log(`Advanced to ${time}ms`);
-  });
-
   let virtualTimeBase = 0;
   let totalElapsedTime = 0;
   let frameTimeTicks = 0;
   let lastGrantedChunk = 0;
 
-  dp.Emulation.onVirtualTimePaused(data => {
-    // Remember the base time for frame time calculation.
-    virtualTimeBase = data.params.virtualTimeElapsed;
-  });
-
   await dp.Emulation.setVirtualTimePolicy({policy: 'pause'});
   lastGrantedChunk = 1000;
   await dp.Emulation.setVirtualTimePolicy({
@@ -70,7 +56,7 @@
   async function AdvanceTime() {
     await dp.Emulation.onceVirtualTimeBudgetExpired();
     totalElapsedTime += lastGrantedChunk;
-    testRunner.log(`Elasped time: ${totalElapsedTime}`);
+    testRunner.log(`Elapsed time: ${totalElapsedTime}`);
     frameTimeTicks = virtualTimeBase + totalElapsedTime;
   }
 
diff --git a/headless/test/data/protocol/emulation/resources/virtual-time-advance.html b/headless/test/data/protocol/emulation/resources/virtual-time-advance.html
deleted file mode 100644
index b6fab932..0000000
--- a/headless/test/data/protocol/emulation/resources/virtual-time-advance.html
+++ /dev/null
@@ -1,33 +0,0 @@
-<html>
-<body onload="step1()">
-<script>
-  function step1() {
-    console.log('step1');
-    // Step 1 - create some history by navigating forward.  Note that this
-    // doesn't cause a load.
-    history.pushState({}, '', '/foo');
-    setTimeout(step2, 100);
-  }
-  function step2() {
-    console.log('step2');
-    if (!location.href.endsWith('/foo'))
-      console.log('pushState failed. ' + location.href);
-    history.back();
-    setTimeout(step3, 100);
-  }
-  function step3() {
-    console.log('step3');
-    if (location.href.endsWith('/foo'))
-      console.log('Backward navigation failed. ' + location.href);
-    history.forward();
-    setTimeout(step4, 100);
-  }
-  function step4() {
-    console.log('step4');
-    if (!location.href.endsWith('/foo'))
-      console.log ('Forward navigation failed. ' + location.href);
-    console.log('pass');
-  }
-</script>
-</body>
-</html>
diff --git a/headless/test/data/protocol/emulation/virtual-time-advance-expected.txt b/headless/test/data/protocol/emulation/virtual-time-advance-expected.txt
deleted file mode 100644
index 0cb06a41..0000000
--- a/headless/test/data/protocol/emulation/virtual-time-advance-expected.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-Tests that virtual time advances.
-Paused @ 0ms
-Paused @ 0ms
-Advanced to 10ms
-step1
-Advanced to 110ms
-step2
-Paused @ 110ms
-Advanced to 210ms
-step3
-Paused @ 210ms
-Advanced to 310ms
-step4
-pass
\ No newline at end of file
diff --git a/headless/test/data/protocol/emulation/virtual-time-advance.js b/headless/test/data/protocol/emulation/virtual-time-advance.js
deleted file mode 100644
index cf63a844..0000000
--- a/headless/test/data/protocol/emulation/virtual-time-advance.js
+++ /dev/null
@@ -1,33 +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.
-
-(async function(testRunner) {
-  var {page, session, dp} = await testRunner.startBlank(`Tests that virtual time advances.`);
-  await dp.Page.enable();
-  await dp.Runtime.enable();
-
-  dp.Emulation.onVirtualTimePaused(data =>
-      testRunner.log(`Paused @ ${data.params.virtualTimeElapsed}ms`));
-  dp.Emulation.onVirtualTimeAdvanced(data => {
-    // Debug chrome schedules stray tasks that break this test.
-    // Our numbers are round, so we prevent this flake 999 times of 1000.
-    const time = data.params.virtualTimeElapsed;
-    if (time !== Math.round(time))
-      return;
-    testRunner.log(`Advanced to ${time}ms`);
-  });
-
-  dp.Runtime.onConsoleAPICalled(data => {
-    const text = data.params.args[0].value;
-    testRunner.log(text);
-    if (text === 'pass')
-      testRunner.completeTest();
-  });
-
-  await dp.Emulation.setVirtualTimePolicy({policy: 'pause'});
-  await dp.Emulation.setVirtualTimePolicy({
-      policy: 'pauseIfNetworkFetchesPending', budget: 5000, waitForNavigation: true});
-  dp.Page.navigate({url: testRunner.url('resources/virtual-time-advance.html')});
-})
-
diff --git a/headless/test/data/protocol/emulation/virtual-time-cross-process-navigation-expected.txt b/headless/test/data/protocol/emulation/virtual-time-cross-process-navigation-expected.txt
index b4562fa..512f257c 100644
--- a/headless/test/data/protocol/emulation/virtual-time-cross-process-navigation-expected.txt
+++ b/headless/test/data/protocol/emulation/virtual-time-cross-process-navigation-expected.txt
@@ -1,5 +1,5 @@
 Tests that virtual time survives cross-process navigation.
 url: http://a.com/ @ 0
-url: http://b.com/ @ 1010
-url: http://c.com/ @ 2020
-url: http://d.com/ @ 3030
\ No newline at end of file
+url: http://b.com/ @ 1000
+url: http://c.com/ @ 2000
+url: http://d.com/ @ 3000
\ No newline at end of file
diff --git a/headless/test/data/protocol/emulation/virtual-time-cross-process-navigation.js b/headless/test/data/protocol/emulation/virtual-time-cross-process-navigation.js
index c368761..291ad5b8 100644
--- a/headless/test/data/protocol/emulation/virtual-time-cross-process-navigation.js
+++ b/headless/test/data/protocol/emulation/virtual-time-cross-process-navigation.js
@@ -31,6 +31,8 @@
   ['http://c.com/', siteC],
   ['http://d.com/', siteD]]);
 
+const virtualTimeBudget = 100;
+
 (async function(testRunner) {
   var {page, session, dp} = await testRunner.startBlank(
       `Tests that virtual time survives cross-process navigation.`);
@@ -38,9 +40,6 @@
   await dp.Network.setRequestInterception({ patterns: [{ urlPattern: '*' }] });
 
   let virtualTime = 0;
-  dp.Emulation.onVirtualTimeAdvanced(data => {
-    virtualTime = data.params.virtualTimeElapsed;
-  });
 
   dp.Network.onRequestIntercepted(event => {
     let url = event.params.request.url;
@@ -54,9 +53,10 @@
 
   let count = 0;
   dp.Emulation.onVirtualTimeBudgetExpired(data => {
+    virtualTime += virtualTimeBudget;
     if (++count < 50) {
       dp.Emulation.setVirtualTimePolicy({
-          policy: 'pauseIfNetworkFetchesPending', budget: 100});
+          policy: 'pauseIfNetworkFetchesPending', budget: virtualTimeBudget});
     } else {
       testRunner.completeTest();
     }
@@ -64,7 +64,7 @@
 
   await dp.Emulation.setVirtualTimePolicy({policy: 'pause'});
   await dp.Emulation.setVirtualTimePolicy({
-      policy: 'pauseIfNetworkFetchesPending', budget: 100,
+      policy: 'pauseIfNetworkFetchesPending', budget: virtualTimeBudget,
       waitForNavigation: true});
   dp.Page.navigate({url: 'http://a.com'});
 })
diff --git a/headless/test/headless_protocol_browsertest.cc b/headless/test/headless_protocol_browsertest.cc
index 1bd21fa..c07584f 100644
--- a/headless/test/headless_protocol_browsertest.cc
+++ b/headless/test/headless_protocol_browsertest.cc
@@ -205,7 +205,6 @@
   }
 
 // Headless-specific tests
-HEADLESS_PROTOCOL_TEST(VirtualTimeAdvance, "emulation/virtual-time-advance.js");
 HEADLESS_PROTOCOL_TEST(VirtualTimeBasics, "emulation/virtual-time-basics.js");
 HEADLESS_PROTOCOL_TEST(VirtualTimeInterrupt,
                        "emulation/virtual-time-interrupt.js");
diff --git a/infra/config/global/PRESUBMIT.py b/infra/config/global/PRESUBMIT.py
index a18b6b1..1df9ce7a 100644
--- a/infra/config/global/PRESUBMIT.py
+++ b/infra/config/global/PRESUBMIT.py
@@ -19,7 +19,8 @@
           name='lint-luci-milo',
           cmd=[input_api.python_executable, 'lint-luci-milo.py'],
           kwargs={},
-          message=output_api.PresubmitError),
+          message=output_api.PresubmitError))
+    commands.append(
       # Technically doesn't rely on lint-luci-milo.py, but is a lightweight
       # enough check it should be fine to trigger.
       input_api.Command(
diff --git a/infra/config/global/cr-buildbucket.cfg b/infra/config/global/cr-buildbucket.cfg
index b8e06419..104a7480a 100644
--- a/infra/config/global/cr-buildbucket.cfg
+++ b/infra/config/global/cr-buildbucket.cfg
@@ -1894,17 +1894,14 @@
     builders {
       name: "Win7 FYI Release (NVIDIA)"
       mixins: "win-gpu-fyi-ci"
-      execution_timeout_secs: 10800  # 3h
     }
     builders {
       name: "Win7 FYI x64 dEQP Release (NVIDIA)"
       mixins: "win-gpu-fyi-ci"
-      execution_timeout_secs: 10800  # 3h
     }
     builders {
       name: "Win7 FYI x64 Release (NVIDIA)"
       mixins: "win-gpu-fyi-ci"
-      execution_timeout_secs: 10800  # 3h
     }
 
     # Code coverage reports generation bots.
diff --git a/ios/build/bots/scripts/test_runner.py b/ios/build/bots/scripts/test_runner.py
index 09052c9..88307b1f 100644
--- a/ios/build/bots/scripts/test_runner.py
+++ b/ios/build/bots/scripts/test_runner.py
@@ -139,7 +139,8 @@
 class ShardingDisabledError(TestRunnerError):
   """Temporary error indicating that sharding is not yet implemented."""
   def __init__(self):
-    super(ShardingDisabledError, self).__init__('Sharding has not been implemented!')
+    super(ShardingDisabledError, self).__init__(
+      'Sharding has not been implemented!')
 
 
 def get_kif_test_filter(tests, invert=False):
@@ -456,6 +457,31 @@
     """
     raise NotImplementedError
 
+  def set_sigterm_handler(self, handler):
+    """Sets the SIGTERM handler for the test runner.
+
+    This is its own separate function so it can be mocked in tests.
+
+    Args:
+      handler: The handler to be called when a SIGTERM is caught
+
+    Returns:
+      The previous SIGTERM handler for the test runner.
+    """
+    return signal.signal(signal.SIGTERM, handler)
+
+  def handle_sigterm(self, proc):
+    """Handles a SIGTERM sent while a test command is executing.
+
+    Will SIGKILL the currently executing test process, then
+    attempt to exit gracefully.
+
+    Args:
+      proc: The currently executing test process.
+    """
+    print "Sigterm caught during test run. Killing test process."
+    proc.kill()
+
   def _run(self, cmd, shards=1):
     """Runs the specified command, parsing GTest output.
 
@@ -497,6 +523,9 @@
           stdout=subprocess.PIPE,
           stderr=subprocess.STDOUT,
       )
+      old_handler = self.set_sigterm_handler(
+        lambda _signum, _frame: self.handle_sigterm(proc))
+
       while True:
         line = proc.stdout.readline()
         if not line:
@@ -506,7 +535,10 @@
         print line
         sys.stdout.flush()
 
+      print "Waiting for test process to terminate."
       proc.wait()
+      print "Test process terminated."
+      self.set_sigterm_handler(old_handler)
       sys.stdout.flush()
 
       returncode = proc.returncode
@@ -1119,6 +1151,8 @@
         stdout=subprocess.PIPE,
         stderr=subprocess.STDOUT,
     )
+    old_handler = self.set_sigterm_handler(
+      lambda _signum, _frame: self.handle_sigterm(proc))
 
     if self.xctest_path:
       parser = xctest_utils.XCTestLogParser()
@@ -1135,6 +1169,7 @@
       sys.stdout.flush()
 
     proc.wait()
+    self.set_sigterm_handler(old_handler)
     sys.stdout.flush()
 
     self.wprgo_stop()
diff --git a/ios/build/bots/scripts/test_runner_test.py b/ios/build/bots/scripts/test_runner_test.py
index 446c43fc..1da6292 100755
--- a/ios/build/bots/scripts/test_runner_test.py
+++ b/ios/build/bots/scripts/test_runner_test.py
@@ -127,6 +127,8 @@
               lambda _: 'fake-bundle-id')
     self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
     self.mock(os.path, 'exists', lambda _: True)
+    self.mock(test_runner.TestRunner, 'set_sigterm_handler',
+      lambda self, handler: 0)
 
   def test_app_not_found(self):
     """Ensures AppNotFoundError is raised."""
@@ -361,9 +363,14 @@
               lambda _: 'fake-bundle-id')
     self.mock(os.path, 'abspath', lambda path: '/abs/path/to/%s' % path)
     self.mock(os.path, 'exists', lambda _: True)
-    self.mock(test_runner.SimulatorTestRunner, 'getSimulator', lambda _: 'fake-id')
-    self.mock(test_runner.SimulatorTestRunner, 'deleteSimulator', lambda a, b: True)
-    self.mock(test_runner.WprProxySimulatorTestRunner, 'copy_trusted_certificate', lambda a, b: True)
+    self.mock(test_runner.TestRunner, 'set_sigterm_handler',
+      lambda self, handler: 0)
+    self.mock(test_runner.SimulatorTestRunner, 'getSimulator',
+      lambda _: 'fake-id')
+    self.mock(test_runner.SimulatorTestRunner, 'deleteSimulator',
+      lambda a, b: True)
+    self.mock(test_runner.WprProxySimulatorTestRunner,
+      'copy_trusted_certificate', lambda a, b: True)
 
   def test_replay_path_not_found(self):
     """Ensures ReplayPathNotFoundError is raised."""
@@ -466,8 +473,10 @@
         'xcode-build',
         'out-dir',
     )
-    self.mock(test_runner.WprProxySimulatorTestRunner, 'wprgo_start', lambda a,b: None)
-    self.mock(test_runner.WprProxySimulatorTestRunner, 'wprgo_stop', lambda _: None)
+    self.mock(test_runner.WprProxySimulatorTestRunner, 'wprgo_start',
+      lambda a,b: None)
+    self.mock(test_runner.WprProxySimulatorTestRunner, 'wprgo_stop',
+      lambda _: None)
 
     self.mock(os.path, 'isfile', lambda _: True)
     self.mock(glob, 'glob', lambda _: ["file1", "file2"])
diff --git a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h
index 31bf519c..e986f7716 100644
--- a/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h
+++ b/ios/chrome/browser/browser_state/chrome_browser_state_impl_io_data.h
@@ -101,7 +101,6 @@
 
     // All of these parameters are intended to be read on the IO thread.
     base::FilePath cookie_path;
-    base::FilePath channel_id_path;
     base::FilePath cache_path;
     int cache_max_size;
   };
diff --git a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h
index 36fa16c..a24e375 100644
--- a/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h
+++ b/ios/chrome/browser/browser_state/off_the_record_chrome_browser_state_io_data.h
@@ -110,7 +110,6 @@
 
   // Server bound certificates and cookies are persisted to the disk on iOS.
   base::FilePath cookie_path_;
-  base::FilePath channel_id_path_;
 
   DISALLOW_COPY_AND_ASSIGN(OffTheRecordChromeBrowserStateIOData);
 };
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.h b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.h
index 71c7a92..fcf95921 100644
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.h
+++ b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.h
@@ -10,7 +10,6 @@
 #include "ui/base/page_transition_types.h"
 
 class GURL;
-@class Tab;
 
 namespace ios {
 class ChromeBrowserState;
@@ -18,6 +17,8 @@
 
 namespace web {
 class NavigationItem;
+class WebState;
+class BrowserState;
 }
 
 // Manages using the current device location for omnibox search queries.
@@ -47,11 +48,13 @@
 // query that's eligible for location. Returns |YES| if the current device
 // location was added to |item|; returns |NO| otherwise.
 - (BOOL)addLocationToNavigationItem:(web::NavigationItem*)item
-                       browserState:(ios::ChromeBrowserState*)browserState;
+                       browserState:(web::BrowserState*)browserState;
 
-// Notifies the receiver that the browser finished loading the page for |tab|.
-// |loadSuccess| whether the tab loaded successfully
-- (void)finishPageLoadForTab:(Tab*)tab loadSuccess:(BOOL)loadSuccess;
+// Notifies the receiver that the browser finished loading the page for
+// |webState|. |loadSuccess| whether the web state loaded successfully.
+// |webState| can't be null.
+- (void)finishPageLoadForWebState:(web::WebState*)webState
+                      loadSuccess:(BOOL)loadSuccess;
 
 @end
 
diff --git a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
index 861eb6a..2bf93d3c 100644
--- a/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
+++ b/ios/chrome/browser/geolocation/omnibox_geolocation_controller.mm
@@ -22,10 +22,11 @@
 #import "ios/chrome/browser/geolocation/omnibox_geolocation_config.h"
 #import "ios/chrome/browser/geolocation/omnibox_geolocation_controller+Testing.h"
 #import "ios/chrome/browser/geolocation/omnibox_geolocation_local_state.h"
-#import "ios/chrome/browser/tabs/tab.h"
+#import "ios/web/public/browser_state.h"
 #include "ios/web/public/navigation_item.h"
 #import "ios/web/public/navigation_manager.h"
 #import "ios/web/public/web_state/web_state.h"
+#import "ios/web/public/web_state/web_state_observer_bridge.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -97,13 +98,16 @@
 
 }  // anonymous namespace
 
-@interface OmniboxGeolocationController ()<
+@interface OmniboxGeolocationController () <
+    CRWWebStateObserver,
     LocationManagerDelegate,
     OmniboxGeolocationAuthorizationAlertDelegate> {
   OmniboxGeolocationLocalState* localState_;
   LocationManager* locationManager_;
   OmniboxGeolocationAuthorizationAlert* authorizationAlert_;
-  __weak Tab* weakTabToReload_;
+
+  // Bridge to observe the web state from Objective-C.
+  std::unique_ptr<web::WebStateObserverBridge> webStateObserverBridge_;
 
   // Records whether we have deliberately presented the system prompt, so that
   // we can record the user's action in
@@ -126,6 +130,11 @@
 // Convenience property lazily initializes |locationManager_|.
 @property(nonatomic, readonly) LocationManager* locationManager;
 
+// A pointer to the current active webState to reload in case of authorization
+// changes. This WebState will be observed and the pointer will be set to null
+// in webStateDestroyed.
+@property(nonatomic) web::WebState* webStateToReload;
+
 // Returns YES if and only if |url| and |transition| specify an Omnibox query
 // that is eligible for geolocation.
 - (BOOL)URLIsEligibleQueryURL:(const GURL&)url
@@ -149,16 +158,16 @@
 // Stops updating device location.
 - (void)stopUpdatingLocation;
 // If the current location is not stale, then adds the current location to the
-// current session entry for |tab| and reloads |tab|. If the current location
-// is stale, then does nothing.
-- (void)addLocationAndReloadTab:(Tab*)tab;
+// current session entry for |webState| and reloads |webState|. If the current
+// location is stale, then does nothing.
+- (void)addLocationAndReloadWebState:(web::WebState*)webState;
 // Returns YES if and only if we should show an alert that prompts the user to
 // authorize using geolocation for Omnibox queries.
 - (BOOL)shouldShowAuthorizationAlert;
 // Shows an alert that prompts the user to authorize using geolocation for
-// Omnibox queries. Sets |weakTabToReload_| from |tab|, so that we can reload
-// |tab| if the user authorizes using geolocation.
-- (void)showAuthorizationAlertForTab:(Tab*)tab;
+// Omnibox queries. Sets |webStateToReload| from |webState|, so that we can
+// reload |webState| if the user authorizes using geolocation.
+- (void)showAuthorizationAlertForWebState:(web::WebState*)webState;
 // Records |headerState| for the |kGeolocationHeaderSentOrNotHistogram|
 // histogram.
 - (void)recordHeaderState:(HeaderState)headerState;
@@ -192,8 +201,7 @@
 
     // Turn on location updates, so that iOS will prompt the user.
     [self startUpdatingLocation];
-
-    weakTabToReload_ = nil;
+    self.webStateToReload = nullptr;
     newUser_ = newUser;
   }
 }
@@ -219,7 +227,7 @@
 }
 
 - (BOOL)addLocationToNavigationItem:(web::NavigationItem*)item
-                       browserState:(ios::ChromeBrowserState*)browserState {
+                       browserState:(web::BrowserState*)browserState {
   // If this is incognito mode or is not an Omnibox query, then do nothing.
   //
   // Check the URL with URLIsQueryURL:transition: here and not
@@ -315,14 +323,22 @@
   return headerState == kHeaderStateSent;
 }
 
-- (void)finishPageLoadForTab:(Tab*)tab loadSuccess:(BOOL)loadSuccess {
-  if (!loadSuccess || !tab.browserState || tab.browserState->IsOffTheRecord()) {
+- (void)finishPageLoadForWebState:(web::WebState*)webState
+                      loadSuccess:(BOOL)loadSuccess {
+  if (!loadSuccess || !webState->GetBrowserState() ||
+      webState->GetBrowserState()->IsOffTheRecord()) {
     return;
   }
 
-  DCHECK(tab.webState->GetNavigationManager());
   web::NavigationItem* item =
-      tab.webState->GetNavigationManager()->GetVisibleItem();
+      webState->GetNavigationManager()->GetVisibleItem();
+
+  if (!item) {
+    // TODO(crbug.com/899827): remove this early return once committed
+    // navigation item always exists after WebStateObserver::PageLoaded.
+    return;
+  }
+
   if (![self URLIsAuthorizationPromptingURL:item->GetURL()] ||
       !self.locationManager.locationServicesEnabled) {
     return;
@@ -340,9 +356,9 @@
           geolocation::kAuthorizationStateNotDeterminedSystemPrompt;
       [self startUpdatingLocation];
 
-      // Save this tab in case we're able to transition to
+      // Save this webState in case we're able to transition to
       // kAuthorizationStateAuthorized.
-      weakTabToReload_ = tab;
+      self.webStateToReload = webState;
       break;
 
     case kCLAuthorizationStatusRestricted:
@@ -368,7 +384,7 @@
       if (self.localState.authorizationState ==
               geolocation::kAuthorizationStateNotDeterminedWaiting &&
           [self shouldShowAuthorizationAlert]) {
-        [self showAuthorizationAlertForTab:tab];
+        [self showAuthorizationAlertForWebState:webState];
       }
       break;
   }
@@ -398,6 +414,21 @@
   return locationManager_;
 }
 
+- (void)setWebStateToReload:(web::WebState*)webState {
+  if (webState == _webStateToReload)
+    return;
+
+  if (!webStateObserverBridge_) {
+    webStateObserverBridge_ =
+        std::make_unique<web::WebStateObserverBridge>(self);
+  }
+  if (_webStateToReload)
+    _webStateToReload->RemoveObserver(webStateObserverBridge_.get());
+  if (webState)
+    webState->AddObserver(webStateObserverBridge_.get());
+  _webStateToReload = webState;
+}
+
 - (BOOL)URLIsEligibleQueryURL:(const GURL&)url
                    transition:(ui::PageTransition)transition {
   return [self URLIsQueryURL:url transition:transition] &&
@@ -440,8 +471,8 @@
   [locationManager_ stopUpdatingLocation];
 }
 
-- (void)addLocationAndReloadTab:(Tab*)tab {
-  if (self.enabled && tab.webState) {
+- (void)addLocationAndReloadWebState:(web::WebState*)webState {
+  if (self.enabled && webState) {
     // Make sure that GeolocationUpdater is running the first time we request
     // the current location.
     //
@@ -453,10 +484,11 @@
     [self startUpdatingLocation];
 
     web::NavigationManager* navigationManager =
-        tab.webState->GetNavigationManager();
+        webState->GetNavigationManager();
     web::NavigationItem* item = navigationManager->GetVisibleItem();
     if (item &&
-        [self addLocationToNavigationItem:item browserState:tab.browserState]) {
+        [self addLocationToNavigationItem:item
+                             browserState:webState->GetBrowserState()]) {
       navigationManager->Reload(web::ReloadType::NORMAL,
                                 false /* check_for_repost */);
     }
@@ -473,10 +505,10 @@
   return currentVersion.components()[0] != previousVersion.components()[0];
 }
 
-- (void)showAuthorizationAlertForTab:(Tab*)tab {
-  // Save this tab in case we're able to transition to
+- (void)showAuthorizationAlertForWebState:(web::WebState*)webState {
+  // Save this webState in case we're able to transition to
   // kAuthorizationStateAuthorized.
-  weakTabToReload_ = tab;
+  self.webStateToReload = webState;
 
   authorizationAlert_ =
       [[OmniboxGeolocationAuthorizationAlert alloc] initWithDelegate:self];
@@ -531,9 +563,8 @@
             geolocation::kAuthorizationStateAuthorized;
         systemPrompt_ = NO;
 
-        Tab* tab = weakTabToReload_;
-        [self addLocationAndReloadTab:tab];
-        weakTabToReload_ = nil;
+        [self addLocationAndReloadWebState:self.webStateToReload];
+        self.webStateToReload = nullptr;
 
         [self recordAuthorizationAction:kAuthorizationActionAuthorized];
         break;
@@ -548,11 +579,10 @@
   self.localState.authorizationState =
       geolocation::kAuthorizationStateAuthorized;
 
-  Tab* tab = weakTabToReload_;
-  [self addLocationAndReloadTab:tab];
+  [self addLocationAndReloadWebState:self.webStateToReload];
 
   authorizationAlert_ = nil;
-  weakTabToReload_ = nil;
+  self.webStateToReload = nullptr;
 
   [self recordAuthorizationAction:kAuthorizationActionAuthorized];
 }
@@ -564,7 +594,7 @@
   // application update.
 
   authorizationAlert_ = nil;
-  weakTabToReload_ = nil;
+  self.webStateToReload = nullptr;
 
   [self recordAuthorizationAction:kAuthorizationActionDenied];
 }
@@ -579,4 +609,11 @@
   locationManager_ = locationManager;
 }
 
+#pragma mark - CRWWebStateObserver Methods
+
+- (void)webStateDestroyed:(web::WebState*)webState {
+  DCHECK_EQ(webState, _webStateToReload);
+  self.webStateToReload = nullptr;
+}
+
 @end
diff --git a/ios/chrome/browser/metrics/BUILD.gn b/ios/chrome/browser/metrics/BUILD.gn
index 36be492..d87b37a 100644
--- a/ios/chrome/browser/metrics/BUILD.gn
+++ b/ios/chrome/browser/metrics/BUILD.gn
@@ -79,6 +79,7 @@
     "//ios/chrome/browser/web_state_list",
     "//ios/chrome/common",
     "//ios/web",
+    "//url",
   ]
 }
 
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h
index 1530426..3d2b0d3 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.h
@@ -67,8 +67,8 @@
   std::string GetVersionString() override;
   void CollectFinalMetricsForLog(const base::Closure& done_callback) override;
   std::unique_ptr<metrics::MetricsLogUploader> CreateUploader(
-      base::StringPiece server_url,
-      base::StringPiece insecure_server_url,
+      const GURL& server_url,
+      const GURL& insecure_server_url,
       base::StringPiece mime_type,
       metrics::MetricsLogUploader::MetricServiceType service_type,
       const metrics::MetricsLogUploader::UploadCallback& on_upload_complete)
diff --git a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
index 4462781..db25c588 100644
--- a/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
+++ b/ios/chrome/browser/metrics/ios_chrome_metrics_service_client.mm
@@ -155,8 +155,8 @@
 
 std::unique_ptr<metrics::MetricsLogUploader>
 IOSChromeMetricsServiceClient::CreateUploader(
-    base::StringPiece server_url,
-    base::StringPiece insecure_server_url,
+    const GURL& server_url,
+    const GURL& insecure_server_url,
     base::StringPiece mime_type,
     metrics::MetricsLogUploader::MetricServiceType service_type,
     const metrics::MetricsLogUploader::UploadCallback& on_upload_complete) {
diff --git a/ios/chrome/browser/prerender/BUILD.gn b/ios/chrome/browser/prerender/BUILD.gn
index f0f1def1..1a3ceb92 100644
--- a/ios/chrome/browser/prerender/BUILD.gn
+++ b/ios/chrome/browser/prerender/BUILD.gn
@@ -30,7 +30,6 @@
     "//ios/chrome/browser/net",
     "//ios/chrome/browser/sessions:serialisation",
     "//ios/chrome/browser/signin",
-    "//ios/chrome/browser/snapshots",
     "//ios/chrome/browser/tabs",
     "//ios/chrome/browser/ui",
     "//ios/chrome/browser/ui/ntp:util",
diff --git a/ios/chrome/browser/prerender/preload_controller.mm b/ios/chrome/browser/prerender/preload_controller.mm
index 0b27cd0..62bbc295 100644
--- a/ios/chrome/browser/prerender/preload_controller.mm
+++ b/ios/chrome/browser/prerender/preload_controller.mm
@@ -283,8 +283,9 @@
   }
 
   if (!webState->IsLoading()) {
-    [[OmniboxGeolocationController sharedInstance] finishPageLoadForTab:tab
-                                                            loadSuccess:YES];
+    [[OmniboxGeolocationController sharedInstance]
+        finishPageLoadForWebState:webState.get()
+                      loadSuccess:YES];
   }
 
   return webState;
diff --git a/ios/chrome/browser/prerender/prerender_service.mm b/ios/chrome/browser/prerender/prerender_service.mm
index 17ba5ef..13bfcff 100644
--- a/ios/chrome/browser/prerender/prerender_service.mm
+++ b/ios/chrome/browser/prerender/prerender_service.mm
@@ -7,7 +7,6 @@
 #include "base/metrics/histogram_macros.h"
 #import "ios/chrome/browser/prerender/preload_controller.h"
 #import "ios/chrome/browser/sessions/session_window_restoring.h"
-#import "ios/chrome/browser/snapshots/snapshot_tab_helper.h"
 #import "ios/chrome/browser/ui/ntp/ntp_util.h"
 #import "ios/chrome/browser/web/load_timing_tab_helper.h"
 #import "ios/chrome/browser/web_state_list/web_state_list.h"
@@ -89,12 +88,6 @@
     // new_web_state is now null after the std::move, so grab a new pointer to
     // it for further updates.
     web::WebState* active_web_state = web_state_list->GetActiveWebState();
-    if (!active_web_state->IsLoading()) {
-      // If the page has finished loading, take a snapshot.  If the page is
-      // still loading, do nothing, as the tab helper will automatically take
-      // a snapshot once the load completes.
-      SnapshotTabHelper::FromWebState(active_web_state)->UpdateSnapshot();
-    }
 
     bool typed_or_generated_transition =
         PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) ||
diff --git a/ios/chrome/browser/signin/BUILD.gn b/ios/chrome/browser/signin/BUILD.gn
index 57b56346..6faeded7 100644
--- a/ios/chrome/browser/signin/BUILD.gn
+++ b/ios/chrome/browser/signin/BUILD.gn
@@ -57,7 +57,6 @@
     "signin_error_controller_factory.h",
     "signin_manager_factory.cc",
     "signin_manager_factory.h",
-    "signin_manager_factory_observer.h",
     "signin_util.h",
     "signin_util.mm",
   ]
@@ -117,8 +116,6 @@
     "authentication_service_delegate_fake.mm",
     "authentication_service_fake.h",
     "authentication_service_fake.mm",
-    "fake_gaia_cookie_manager_service_builder.cc",
-    "fake_gaia_cookie_manager_service_builder.h",
     "identity_test_environment_chrome_browser_state_adaptor.cc",
     "identity_test_environment_chrome_browser_state_adaptor.h",
   ]
diff --git a/ios/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc b/ios/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc
deleted file mode 100644
index bd5c4f6e2..0000000
--- a/ios/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.cc
+++ /dev/null
@@ -1,22 +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 "ios/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h"
-
-#include "components/keyed_service/core/keyed_service.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
-#include "components/signin/core/browser/profile_oauth2_token_service.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h"
-#include "ios/chrome/browser/signin/signin_client_factory.h"
-
-std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerService(
-    web::BrowserState* browser_state) {
-  ios::ChromeBrowserState* chrome_browser_state =
-      ios::ChromeBrowserState::FromBrowserState(browser_state);
-  return std::make_unique<FakeGaiaCookieManagerService>(
-      ProfileOAuth2TokenServiceFactory::GetForBrowserState(
-          chrome_browser_state),
-      SigninClientFactory::GetForBrowserState(chrome_browser_state));
-}
diff --git a/ios/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h b/ios/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h
deleted file mode 100644
index aff92eaf..0000000
--- a/ios/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_SIGNIN_FAKE_GAIA_COOKIE_MANAGER_SERVICE_BUILDER_H_
-#define IOS_CHROME_BROWSER_SIGNIN_FAKE_GAIA_COOKIE_MANAGER_SERVICE_BUILDER_H_
-
-#include <memory>
-
-class KeyedService;
-
-namespace web {
-class BrowserState;
-}
-
-// Helper functions to be used with KeyedService::SetTestingFactory().
-// This is a subset of the helpers available in //chrome. If more helpers are
-// needed, they can be copied from fake_gaia_cookie_manager_service_builder.h
-// under //chrome.
-std::unique_ptr<KeyedService> BuildFakeGaiaCookieManagerService(
-    web::BrowserState* browser_state);
-
-#endif  // IOS_CHROME_BROWSER_SIGNIN_FAKE_GAIA_COOKIE_MANAGER_SERVICE_BUILDER_H_
diff --git a/ios/chrome/browser/signin/identity_manager_factory.h b/ios/chrome/browser/signin/identity_manager_factory.h
index 87bb811..2fc912c 100644
--- a/ios/chrome/browser/signin/identity_manager_factory.h
+++ b/ios/chrome/browser/signin/identity_manager_factory.h
@@ -44,7 +44,9 @@
   ~IdentityManagerFactory() override;
 
   // List of observers. Checks that list is empty on destruction.
-  mutable base::ObserverList<IdentityManagerFactoryObserver, true>::Unchecked
+  base::ObserverList<IdentityManagerFactoryObserver,
+                     /*check_empty=*/true,
+                     /*allow_reentrancy=*/false>
       observer_list_;
 
   // BrowserStateKeyedServiceFactory:
diff --git a/ios/chrome/browser/signin/identity_manager_factory_observer.h b/ios/chrome/browser/signin/identity_manager_factory_observer.h
index aac5f08..1d5d7ec 100644
--- a/ios/chrome/browser/signin/identity_manager_factory_observer.h
+++ b/ios/chrome/browser/signin/identity_manager_factory_observer.h
@@ -6,16 +6,17 @@
 #define IOS_CHROME_BROWSER_SIGNIN_IDENTITY_MANAGER_FACTORY_OBSERVER_H_
 
 #include "base/macros.h"
+#include "base/observer_list_types.h"
 
 namespace identity {
 class IdentityManager;
 }
 
 // Observer for IdentityManagerFactory.
-class IdentityManagerFactoryObserver {
+class IdentityManagerFactoryObserver : public base::CheckedObserver {
  public:
   IdentityManagerFactoryObserver() {}
-  virtual ~IdentityManagerFactoryObserver() {}
+  ~IdentityManagerFactoryObserver() override {}
 
   // Called when an IdentityManager instance is created.
   virtual void IdentityManagerCreated(identity::IdentityManager* manager) {}
diff --git a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
index 589de167..56e53da0 100644
--- a/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
+++ b/ios/chrome/browser/signin/identity_test_environment_chrome_browser_state_adaptor.cc
@@ -13,7 +13,6 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/signin/account_fetcher_service_factory.h"
 #include "ios/chrome/browser/signin/account_tracker_service_factory.h"
-#include "ios/chrome/browser/signin/fake_gaia_cookie_manager_service_builder.h"
 #include "ios/chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "ios/chrome/browser/signin/identity_manager_factory.h"
 #include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h"
@@ -36,8 +35,6 @@
       ios::GaiaCookieManagerServiceFactory::GetForBrowserState(
           chrome_browser_state)));
   manager->Initialize(nullptr);
-  ios::SigninManagerFactory::GetInstance()
-      ->NotifyObserversOfSigninManagerCreationForTesting(manager.get());
   return manager;
 }
 
@@ -70,8 +67,6 @@
 TestChromeBrowserState::TestingFactories GetIdentityTestEnvironmentFactories() {
   return {{ios::AccountFetcherServiceFactory::GetInstance(),
            base::BindRepeating(&BuildFakeAccountFetcherService)},
-          {ios::GaiaCookieManagerServiceFactory::GetInstance(),
-           base::BindRepeating(&BuildFakeGaiaCookieManagerService)},
           {ProfileOAuth2TokenServiceFactory::GetInstance(),
            base::BindRepeating(&BuildFakeOAuth2TokenService)},
           {ios::SigninManagerFactory::GetInstance(),
diff --git a/ios/chrome/browser/signin/signin_manager_factory.cc b/ios/chrome/browser/signin/signin_manager_factory.cc
index 4ee029b..826e382 100644
--- a/ios/chrome/browser/signin/signin_manager_factory.cc
+++ b/ios/chrome/browser/signin/signin_manager_factory.cc
@@ -20,7 +20,6 @@
 #include "ios/chrome/browser/signin/gaia_cookie_manager_service_factory.h"
 #include "ios/chrome/browser/signin/profile_oauth2_token_service_factory.h"
 #include "ios/chrome/browser/signin/signin_client_factory.h"
-#include "ios/chrome/browser/signin/signin_manager_factory_observer.h"
 
 namespace ios {
 
@@ -66,21 +65,6 @@
   SigninManagerBase::RegisterPrefs(registry);
 }
 
-void SigninManagerFactory::AddObserver(SigninManagerFactoryObserver* observer) {
-  observer_list_.AddObserver(observer);
-}
-
-void SigninManagerFactory::RemoveObserver(
-    SigninManagerFactoryObserver* observer) {
-  observer_list_.RemoveObserver(observer);
-}
-
-void SigninManagerFactory::NotifyObserversOfSigninManagerCreationForTesting(
-    SigninManager* manager) {
-  for (auto& observer : observer_list_)
-    observer.SigninManagerCreated(manager);
-}
-
 std::unique_ptr<KeyedService> SigninManagerFactory::BuildServiceInstanceFor(
     web::BrowserState* context) const {
   ios::ChromeBrowserState* chrome_browser_state =
@@ -95,19 +79,7 @@
           chrome_browser_state),
       signin::AccountConsistencyMethod::kMirror));
   service->Initialize(GetApplicationContext()->GetLocalState());
-  for (auto& observer : observer_list_)
-    observer.SigninManagerCreated(service.get());
   return service;
 }
 
-void SigninManagerFactory::BrowserStateShutdown(web::BrowserState* context) {
-  SigninManager* manager =
-      static_cast<SigninManager*>(GetServiceForBrowserState(context, false));
-  if (manager) {
-    for (auto& observer : observer_list_)
-      observer.SigninManagerShutdown(manager);
-  }
-  BrowserStateKeyedServiceFactory::BrowserStateShutdown(context);
-}
-
 }  // namespace ios
diff --git a/ios/chrome/browser/signin/signin_manager_factory.h b/ios/chrome/browser/signin/signin_manager_factory.h
index 10a6d53..67115ae6 100644
--- a/ios/chrome/browser/signin/signin_manager_factory.h
+++ b/ios/chrome/browser/signin/signin_manager_factory.h
@@ -9,10 +9,8 @@
 
 #include "base/macros.h"
 #include "base/no_destructor.h"
-#include "base/observer_list.h"
 #include "components/keyed_service/ios/browser_state_keyed_service_factory.h"
 
-class SigninManagerFactoryObserver;
 class SigninManager;
 class PrefRegistrySimple;
 
@@ -40,29 +38,15 @@
   // Registers the browser-global prefs used by SigninManager.
   static void RegisterPrefs(PrefRegistrySimple* registry);
 
-  // Methods to register or remove observers of SigninManager creation/shutdown.
-  void AddObserver(SigninManagerFactoryObserver* observer);
-  void RemoveObserver(SigninManagerFactoryObserver* observer);
-
-  // Notifies observers of |manager|'s creation. Should be called only by test
-  // SigninManager subclasses whose construction does not occur in
-  // |BuildServiceInstanceFor()|.
-  void NotifyObserversOfSigninManagerCreationForTesting(SigninManager* manager);
-
  private:
   friend class base::NoDestructor<SigninManagerFactory>;
 
   SigninManagerFactory();
   ~SigninManagerFactory() override;
 
-  // List of observers. Checks that list is empty on destruction.
-  mutable base::ObserverList<SigninManagerFactoryObserver, true>::Unchecked
-      observer_list_;
-
   // BrowserStateKeyedServiceFactory:
   std::unique_ptr<KeyedService> BuildServiceInstanceFor(
       web::BrowserState* context) const override;
-  void BrowserStateShutdown(web::BrowserState* context) override;
 };
 }
 
diff --git a/ios/chrome/browser/signin/signin_manager_factory_observer.h b/ios/chrome/browser/signin/signin_manager_factory_observer.h
deleted file mode 100644
index 0948172..0000000
--- a/ios/chrome/browser/signin/signin_manager_factory_observer.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef IOS_CHROME_BROWSER_SIGNIN_SIGNIN_MANAGER_FACTORY_OBSERVER_H_
-#define IOS_CHROME_BROWSER_SIGNIN_SIGNIN_MANAGER_FACTORY_OBSERVER_H_
-
-#include "base/macros.h"
-
-class SigninManager;
-
-// Observer for SigninManagerFactory.
-class SigninManagerFactoryObserver {
- public:
-  SigninManagerFactoryObserver() {}
-  virtual ~SigninManagerFactoryObserver() {}
-
-  // Called when a SigninManager instance is created.
-  virtual void SigninManagerCreated(SigninManager* manager) {}
-
-  // Called when a SigninManager instance is being shut down. Observers
-  // of |manager| should remove themselves at this point.
-  virtual void SigninManagerShutdown(SigninManager* manager) {}
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(SigninManagerFactoryObserver);
-};
-
-#endif  // IOS_CHROME_BROWSER_SIGNIN_SIGNIN_MANAGER_FACTORY_OBSERVER_H_
diff --git a/ios/chrome/browser/snapshots/snapshot_cache.mm b/ios/chrome/browser/snapshots/snapshot_cache.mm
index dad100a..ebe2d03 100644
--- a/ios/chrome/browser/snapshots/snapshot_cache.mm
+++ b/ios/chrome/browser/snapshots/snapshot_cache.mm
@@ -3,17 +3,16 @@
 // found in the LICENSE file.
 
 #import "ios/chrome/browser/snapshots/snapshot_cache.h"
+#import "ios/chrome/browser/snapshots/snapshot_cache_internal.h"
 
 #import <UIKit/UIKit.h>
 
 #include "base/base_paths.h"
 #include "base/bind.h"
-#include "base/critical_closure.h"
 #include "base/files/file_enumerator.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
 #import "base/ios/crb_protocol_observers.h"
-#include "base/location.h"
 #include "base/logging.h"
 #include "base/mac/scoped_nsobject.h"
 #include "base/path_service.h"
@@ -24,9 +23,7 @@
 #include "base/task/post_task.h"
 #include "base/task_runner_util.h"
 #include "base/threading/scoped_blocking_call.h"
-#include "ios/chrome/browser/experimental_flags.h"
 #import "ios/chrome/browser/snapshots/lru_cache.h"
-#import "ios/chrome/browser/snapshots/snapshot_cache_internal.h"
 #import "ios/chrome/browser/snapshots/snapshot_cache_observer.h"
 #include "ios/chrome/browser/ui/util/ui_util.h"
 #import "ios/chrome/browser/ui/util/uikit_ui_util.h"
diff --git a/ios/chrome/browser/tabs/tab_model.mm b/ios/chrome/browser/tabs/tab_model.mm
index aa7b3e9..b4d40e92 100644
--- a/ios/chrome/browser/tabs/tab_model.mm
+++ b/ios/chrome/browser/tabs/tab_model.mm
@@ -847,8 +847,9 @@
   RecordInterfaceOrientationMetric();
   RecordMainFrameNavigationMetric(webState);
 
-  [[OmniboxGeolocationController sharedInstance] finishPageLoadForTab:tab
-                                                          loadSuccess:success];
+  [[OmniboxGeolocationController sharedInstance]
+      finishPageLoadForWebState:webState
+                    loadSuccess:success];
 }
 
 - (void)webState:(web::WebState*)webState
diff --git a/ios/chrome/browser/translate/translate_infobar_controller.mm b/ios/chrome/browser/translate/translate_infobar_controller.mm
index 2cfce6a..0c8ee14 100644
--- a/ios/chrome/browser/translate/translate_infobar_controller.mm
+++ b/ios/chrome/browser/translate/translate_infobar_controller.mm
@@ -164,7 +164,16 @@
 
   self.userAction |= UserActionTranslate;
 
-  self.infoBarDelegate->Translate();
+  if (self.infoBarDelegate->ShouldAutoAlwaysTranslate()) {
+    // Page will be translated once the snackbar finishes showing.
+    [self.translateNotificationHandler
+        showAlwaysTranslateLanguageNotificationWithDelegate:self
+                                             sourceLanguage:self.sourceLanguage
+                                             targetLanguage:
+                                                 self.targetLanguage];
+  } else {
+    self.infoBarDelegate->Translate();
+  }
 }
 
 - (void)translateInfobarViewDidTapOptions:(TranslateInfobarView*)sender {
@@ -181,7 +190,15 @@
     return;
 
   self.infoBarDelegate->InfoBarDismissed();
-  self.delegate->RemoveInfoBar();
+
+  if (self.infoBarDelegate->ShouldAutoNeverTranslate()) {
+    // Infobar will dismiss once the snackbar finishes showing.
+    [self.translateNotificationHandler
+        showNeverTranslateLanguageNotificationWithDelegate:self
+                                            sourceLanguage:self.sourceLanguage];
+  } else {
+    self.delegate->RemoveInfoBar();
+  }
 }
 
 #pragma mark - LanguageSelectionDelegate
@@ -234,6 +251,7 @@
   if (self.infoBarDelegate->ShouldAlwaysTranslate()) {
     self.infoBarDelegate->ToggleAlwaysTranslate();
   } else {
+    // Page will be translated once the snackbar finishes showing.
     [self.translateNotificationHandler
         showAlwaysTranslateLanguageNotificationWithDelegate:self
                                              sourceLanguage:self.sourceLanguage
@@ -252,6 +270,7 @@
   [_infobarView updateUIForPopUpMenuDisplayed:NO];
 
   if (self.infoBarDelegate->IsTranslatableLanguageByPrefs()) {
+    // Infobar will dismiss once the snackbar finishes showing.
     [self.translateNotificationHandler
         showNeverTranslateLanguageNotificationWithDelegate:self
                                             sourceLanguage:self.sourceLanguage];
@@ -268,6 +287,7 @@
   [_infobarView updateUIForPopUpMenuDisplayed:NO];
 
   if (!self.infoBarDelegate->IsSiteBlacklisted()) {
+    // Infobar will dismiss once the snackbar finishes showing.
     [self.translateNotificationHandler
         showNeverTranslateSiteNotificationWithDelegate:self];
   }
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index f61e21d0..100fed2 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -719,9 +719,6 @@
 // --------------------------
 // Whether the given tab's URL is an application specific URL.
 - (BOOL)isTabNativePage:(Tab*)tab;
-// Returns the view to use when animating a page in or out, positioning it to
-// fill the content area but not actually adding it to the view hierarchy.
-- (UIImageView*)pageOpenCloseAnimationView;
 // Add all delegates to the provided |tab|.
 - (void)installDelegatesForTab:(Tab*)tab;
 // Remove delegates from the provided |tab|.
@@ -2372,8 +2369,7 @@
       viewController.view.frame = [self ntpFrameForWebState:tab.webState];
       // TODO(crbug.com/873729): For a newly created WebState, the session will
       // not be restored until LoadIfNecessary call. Remove when fixed.
-      if (tab.webState->GetNavigationManager()->IsRestoreSessionInProgress())
-        tab.webState->GetNavigationManager()->LoadIfNecessary();
+      tab.webState->GetNavigationManager()->LoadIfNecessary();
 
       // Always show the webState view under the NTP, to work around
       // crbug.com/848789
@@ -2673,20 +2669,6 @@
   return web::GetWebClient()->IsAppSpecificURL(visibleItem->GetURL());
 }
 
-- (UIImageView*)pageOpenCloseAnimationView {
-  CGRect frame = self.contentArea.bounds;
-
-  frame.size.height = frame.size.height - self.headerHeight;
-  frame.origin.y = self.headerHeight;
-
-  UIImageView* pageView = [[UIImageView alloc] initWithFrame:frame];
-  CGPoint center = CGPointMake(CGRectGetMidX(frame), CGRectGetMidY(frame));
-  pageView.center = center;
-
-  pageView.backgroundColor = [UIColor whiteColor];
-  return pageView;
-}
-
 - (void)installDelegatesForTab:(Tab*)tab {
   // Unregistration happens when the Tab is removed from the TabModel.
   DCHECK_NE(tab.webState->GetDelegate(), _webStateDelegate.get());
@@ -2916,14 +2898,13 @@
     return UIEdgeInsetsZero;
   }
   if (isNTPActive || (outOfWeb && !usesContentInset)) {
-    return UIEdgeInsetsMake(self.headerHeight, 0.0, 0.0, 0.0);
+    return UIEdgeInsetsMake(self.headerHeight, 0.0, self.bottomToolbarHeight,
+                            0.0);
   }
 
   // For all other scenarios, the content area is inset from the snapshot base
   // view by the web view proxy's contentInset.
-  UIEdgeInsets insets = webState->GetWebViewProxy().contentInset;
-  insets.bottom = 0.0;
-  return insets;
+  return webState->GetWebViewProxy().contentInset;
 }
 
 - (NSArray<UIView*>*)snapshotGenerator:(SnapshotGenerator*)snapshotGenerator
@@ -4351,23 +4332,16 @@
   if (tabIndex == NSNotFound)
     return;
 
-  // TODO(crbug.com/688003): Evaluate if a screenshot of the tab is needed on
-  // iPad.
-  UIImageView* exitingPage = [self pageOpenCloseAnimationView];
-  // TODO(crbug.com/917929): Refactor to remove usage of UpdateSnapshot().
-  exitingPage.image =
-      SnapshotTabHelper::FromWebState(self.currentWebState)->UpdateSnapshot();
+  UIView* snapshotView = [self.contentArea snapshotViewAfterScreenUpdates:NO];
+  snapshotView.frame = self.contentArea.frame;
 
-  // Close the actual tab, and add its image as a subview.
   [self.tabModel closeTabAtIndex:tabIndex];
 
-  // Do not animate close in iPad.
   if (![self canShowTabStrip]) {
-    [self.contentArea addSubview:exitingPage];
-    page_animation_util::AnimateOutWithCompletion(
-        exitingPage, 0, YES, IsPortrait(), ^{
-          [exitingPage removeFromSuperview];
-        });
+    [self.contentArea addSubview:snapshotView];
+    page_animation_util::AnimateOutWithCompletion(snapshotView, ^{
+      [snapshotView removeFromSuperview];
+    });
   }
 }
 
diff --git a/ios/chrome/browser/ui/elements/top_aligned_image_view.h b/ios/chrome/browser/ui/elements/top_aligned_image_view.h
index ec088d0..d7c1abf 100644
--- a/ios/chrome/browser/ui/elements/top_aligned_image_view.h
+++ b/ios/chrome/browser/ui/elements/top_aligned_image_view.h
@@ -8,8 +8,10 @@
 #import <UIKit/UIKit.h>
 
 // The standard UIImageView zooms to the center of the image when it aspect
-// fills. TopAlignedImageView aligns to the top of the image instead of the
-// center.
+// fills. TopAlignedImageView aligns to the top of the image in all cases.
+// When the image is portrait, the image matches the width of the view in all
+// cases. When the image is landscape, the image matches the height of the view
+// in all cases.
 @interface TopAlignedImageView : UIView
 // The image displayed in the image view.
 @property(nonatomic) UIImage* image;
diff --git a/ios/chrome/browser/ui/elements/top_aligned_image_view.mm b/ios/chrome/browser/ui/elements/top_aligned_image_view.mm
index 1ccd833..1c01e79 100644
--- a/ios/chrome/browser/ui/elements/top_aligned_image_view.mm
+++ b/ios/chrome/browser/ui/elements/top_aligned_image_view.mm
@@ -37,12 +37,12 @@
       CGRectGetHeight(self.frame) / self.image.size.height;
   CGFloat imageViewWidth;
   CGFloat imageViewHeight;
-  if (widthScaleFactor > heightScaleFactor) {
-    imageViewWidth = self.image.size.width * widthScaleFactor;
-    imageViewHeight = self.image.size.height * widthScaleFactor;
-  } else {
+  if (self.image.size.width > self.image.size.height) {
     imageViewWidth = self.image.size.width * heightScaleFactor;
-    imageViewHeight = self.image.size.height * heightScaleFactor;
+    imageViewHeight = CGRectGetHeight(self.frame);
+  } else {
+    imageViewWidth = CGRectGetWidth(self.frame);
+    imageViewHeight = self.image.size.height * widthScaleFactor;
   }
   self.innerImageView.frame =
       CGRectMake((self.frame.size.width - imageViewWidth) / 2.0f, 0,
diff --git a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
index f69996c4..40c5a6d 100644
--- a/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
+++ b/ios/chrome/browser/ui/location_bar/location_bar_coordinator.mm
@@ -45,6 +45,7 @@
 #import "ios/web/public/navigation_manager.h"
 #import "ios/web/public/referrer.h"
 #import "ios/web/public/web_state/web_state.h"
+#include "services/network/public/cpp/resource_request.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -353,17 +354,17 @@
 
 // Returns a dictionary with variation headers for qualified URLs. Can be empty.
 - (NSDictionary*)variationHeadersForURL:(const GURL&)URL {
-  net::HttpRequestHeaders variation_headers;
-  variations::AppendVariationHeadersUnknownSignedIn(
+  network::ResourceRequest resource_request;
+  variations::AppendVariationsHeaderUnknownSignedIn(
       URL,
       self.browserState->IsOffTheRecord() ? variations::InIncognito::kYes
                                           : variations::InIncognito::kNo,
-      &variation_headers);
+      &resource_request);
   NSMutableDictionary* result = [NSMutableDictionary dictionary];
-  net::HttpRequestHeaders::Iterator header_iterator(variation_headers);
-  while (header_iterator.GetNext()) {
-    NSString* name = base::SysUTF8ToNSString(header_iterator.name());
-    NSString* value = base::SysUTF8ToNSString(header_iterator.value());
+  if (!resource_request.client_data_header.empty()) {
+    NSString* name = base::SysUTF8ToNSString("X-Client-Data");
+    NSString* value =
+        base::SysUTF8ToNSString(resource_request.client_data_header);
     result[name] = value;
   }
   return [result copy];
diff --git a/ios/chrome/browser/ui/side_swipe/OWNERS b/ios/chrome/browser/ui/side_swipe/OWNERS
index 867f7564..fef59f07 100644
--- a/ios/chrome/browser/ui/side_swipe/OWNERS
+++ b/ios/chrome/browser/ui/side_swipe/OWNERS
@@ -1,3 +1,4 @@
+edchin@chromium.org
 justincohen@chromium.org
 
 # TEAM: ios-directory-owners@chromium.org
diff --git a/ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.mm b/ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.mm
index a54d6a02..613a002 100644
--- a/ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.mm
+++ b/ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.mm
@@ -126,8 +126,7 @@
   self.languagesScrollView.translatesAutoresizingMaskIntoConstraints = NO;
   self.languagesScrollView.showsVerticalScrollIndicator = NO;
   self.languagesScrollView.showsHorizontalScrollIndicator = NO;
-  self.languagesScrollView.canCancelContentTouches = YES;
-  self.languagesScrollView.bounces = NO;
+  self.languagesScrollView.bounces = YES;
   self.languagesScrollView.delegate = self;
   [self addSubview:self.languagesScrollView];
 
diff --git a/ios/chrome/browser/ui/translate/translate_infobar_view.mm b/ios/chrome/browser/ui/translate/translate_infobar_view.mm
index 5e4f958..7b90b295 100644
--- a/ios/chrome/browser/ui/translate/translate_infobar_view.mm
+++ b/ios/chrome/browser/ui/translate/translate_infobar_view.mm
@@ -226,16 +226,18 @@
   self.toolbarConfiguration =
       [[ToolbarConfiguration alloc] initWithStyle:NORMAL];
 
-  self.optionsButton =
+  ToolbarButton* optionsButton =
       [self toolbarButtonWithImageNamed:@"translate_options"
                                  target:self
                                  action:@selector(showOptions)];
+  self.optionsButton = optionsButton;
   [contentView addSubview:self.optionsButton];
 
-  self.dismissButton =
+  ToolbarButton* dismissButton =
       [self toolbarButtonWithImageNamed:@"translate_dismiss"
                                  target:self
                                  action:@selector(dismiss)];
+  self.dismissButton = dismissButton;
   [contentView addSubview:self.dismissButton];
 
   ApplyVisualConstraintsWithMetrics(
diff --git a/ios/chrome/browser/ui/util/page_animation_util.h b/ios/chrome/browser/ui/util/page_animation_util.h
index 194b901f..682e5b4 100644
--- a/ios/chrome/browser/ui/util/page_animation_util.h
+++ b/ios/chrome/browser/ui/util/page_animation_util.h
@@ -10,13 +10,9 @@
 // Utility for handling the animation of a page closing.
 namespace page_animation_util {
 
-// Animates |view| to its final position following |delay| seconds, then calls
-// the given completion block when finished.
-void AnimateOutWithCompletion(UIView* view,
-                              NSTimeInterval delay,
-                              BOOL clockwise,
-                              BOOL isPortrait,
-                              void (^completion)(void));
+// Animates |view| to its final position, then calls the given completion block
+// when finished.
+void AnimateOutWithCompletion(UIView* view, void (^completion)(void));
 
 }  // namespace page_animation_util
 
diff --git a/ios/chrome/browser/ui/util/page_animation_util.mm b/ios/chrome/browser/ui/util/page_animation_util.mm
index a78a4b9..f6d8441 100644
--- a/ios/chrome/browser/ui/util/page_animation_util.mm
+++ b/ios/chrome/browser/ui/util/page_animation_util.mm
@@ -44,11 +44,7 @@
   layer.anchorPoint = newAnchor;
 }
 
-void AnimateOutWithCompletion(UIView* view,
-                              NSTimeInterval delay,
-                              BOOL clockwise,
-                              BOOL isPortrait,
-                              void (^completion)(void)) {
+void AnimateOutWithCompletion(UIView* view, void (^completion)(void)) {
   // The close animation spec calls for the anchor point to be the upper right.
   CGPoint newAnchorPoint = CGPointMake(kAnimateOutAnchorX, kAnimateOutAnchorY);
   CALayer* layer = [view layer];
diff --git a/ios/chrome/browser/web/page_placeholder_tab_helper.h b/ios/chrome/browser/web/page_placeholder_tab_helper.h
index 5449188..0bad8c0 100644
--- a/ios/chrome/browser/web/page_placeholder_tab_helper.h
+++ b/ios/chrome/browser/web/page_placeholder_tab_helper.h
@@ -43,6 +43,8 @@
   explicit PagePlaceholderTabHelper(web::WebState* web_state);
 
   // web::WebStateObserver overrides:
+  void WasShown(web::WebState* web_state) override;
+  void WasHidden(web::WebState* web_state) override;
   void DidStartNavigation(web::WebState* web_state,
                           web::NavigationContext* navigation_context) override;
   void PageLoaded(
diff --git a/ios/chrome/browser/web/page_placeholder_tab_helper.mm b/ios/chrome/browser/web/page_placeholder_tab_helper.mm
index 3dc02fea..164807a 100644
--- a/ios/chrome/browser/web/page_placeholder_tab_helper.mm
+++ b/ios/chrome/browser/web/page_placeholder_tab_helper.mm
@@ -42,6 +42,16 @@
   }
 }
 
+void PagePlaceholderTabHelper::WasShown(web::WebState* web_state) {
+  if (add_placeholder_for_next_navigation_) {
+    AddPlaceholder();
+  }
+}
+
+void PagePlaceholderTabHelper::WasHidden(web::WebState* web_state) {
+  RemovePlaceholder();
+}
+
 void PagePlaceholderTabHelper::DidStartNavigation(
     web::WebState* web_state,
     web::NavigationContext* navigation_context) {
@@ -84,18 +94,22 @@
   __weak UIImageView* weak_placeholder_view = placeholder_view_;
   __weak UIView* weak_web_state_view = web_state_->GetView();
   __weak id<CRWWebViewProxy> web_view_proxy = web_state_->GetWebViewProxy();
-  SnapshotTabHelper::FromWebState(web_state_)
-      ->RetrieveGreySnapshot(^(UIImage* snapshot) {
-        CGRect frame = weak_web_state_view.frame;
-        UIEdgeInsets inset = web_view_proxy.contentInset;
-        frame.origin.x += inset.left;
-        frame.origin.y += inset.top;
-        frame.size.width -= (inset.right + inset.left);
-        frame.size.height -= (inset.bottom + inset.top);
-        weak_placeholder_view.frame = frame;
-        weak_placeholder_view.image = snapshot;
-        [weak_web_state_view addSubview:weak_placeholder_view];
-      });
+
+  SnapshotTabHelper* snapshotTabHelper =
+      SnapshotTabHelper::FromWebState(web_state_);
+  if (snapshotTabHelper) {
+    snapshotTabHelper->RetrieveGreySnapshot(^(UIImage* snapshot) {
+      CGRect frame = weak_web_state_view.frame;
+      UIEdgeInsets inset = web_view_proxy.contentInset;
+      frame.origin.x += inset.left;
+      frame.origin.y += inset.top;
+      frame.size.width -= (inset.right + inset.left);
+      frame.size.height -= (inset.bottom + inset.top);
+      weak_placeholder_view.frame = frame;
+      weak_placeholder_view.image = snapshot;
+      [weak_web_state_view addSubview:weak_placeholder_view];
+    });
+  }
 
   // Remove placeholder if it takes too long to load the page.
   base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
@@ -119,5 +133,6 @@
       }
       completion:^(BOOL finished) {
         [weak_placeholder_view removeFromSuperview];
+        weak_placeholder_view.alpha = 1.0f;
       }];
 }
diff --git a/ios/chrome/browser/web/page_placeholder_tab_helper_unittest.mm b/ios/chrome/browser/web/page_placeholder_tab_helper_unittest.mm
index ddc1e4d4..f557468 100644
--- a/ios/chrome/browser/web/page_placeholder_tab_helper_unittest.mm
+++ b/ios/chrome/browser/web/page_placeholder_tab_helper_unittest.mm
@@ -53,6 +53,36 @@
   UIView* web_state_view_ = nil;
 };
 
+// Tests that placeholder is not shown after WasShown() if it was not requested.
+TEST_F(PagePlaceholderTabHelperTest, TabShownAndPlaceholderNotShown) {
+  ASSERT_FALSE(tab_helper()->displaying_placeholder());
+  ASSERT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
+  web_state_->WasShown();
+  EXPECT_FALSE(tab_helper()->displaying_placeholder());
+  EXPECT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
+}
+
+// Tests that placeholder is shown after WasShown() if it was requested.
+TEST_F(PagePlaceholderTabHelperTest, TabShownAndPlaceholderShown) {
+  ASSERT_FALSE(tab_helper()->displaying_placeholder());
+  ASSERT_FALSE(tab_helper()->will_add_placeholder_for_next_navigation());
+  tab_helper()->AddPlaceholderForNextNavigation();
+  ASSERT_FALSE(tab_helper()->displaying_placeholder());
+  EXPECT_TRUE(tab_helper()->will_add_placeholder_for_next_navigation());
+  web_state_->WasShown();
+  EXPECT_TRUE(tab_helper()->displaying_placeholder());
+  EXPECT_TRUE(tab_helper()->will_add_placeholder_for_next_navigation());
+}
+
+// Tests that placeholder is removed after WasHidden().
+TEST_F(PagePlaceholderTabHelperTest, TabHiddenAndPlaceholderRemoved) {
+  tab_helper()->AddPlaceholderForNextNavigation();
+  web_state_->WasShown();
+  ASSERT_TRUE(tab_helper()->displaying_placeholder());
+  web_state_->WasHidden();
+  EXPECT_FALSE(tab_helper()->displaying_placeholder());
+}
+
 // Tests that placeholder is not shown after DidStartNavigation if it was not
 // requested.
 TEST_F(PagePlaceholderTabHelperTest, NotShown) {
diff --git a/ios/web/public/global_state/ios_global_state.mm b/ios/web/public/global_state/ios_global_state.mm
index 16bed4f9..2228c72 100644
--- a/ios/web/public/global_state/ios_global_state.mm
+++ b/ios/web/public/global_state/ios_global_state.mm
@@ -23,19 +23,29 @@
 net::NetworkChangeNotifier* g_network_change_notifer = nullptr;
 
 base::TaskScheduler::InitParams GetDefaultTaskSchedulerInitParams() {
+  constexpr int kMinBackgroundThreads = 4;
+  constexpr int kMaxBackgroundThreads = 16;
+  constexpr double kCoreMultiplierBackgroundThreads = 0.2;
+  constexpr int kOffsetBackgroundThreads = 0;
+  constexpr int kReclaimTimeBackground = 30;
+
+  constexpr int kMinForegroundThreads = 6;
+  constexpr int kMaxForegroundThreads = 16;
+  constexpr double kCoreMultiplierForegroundThreads = 0.6;
+  constexpr int kOffsetForegroundThreads = 0;
+  constexpr int kReclaimTimeForeground = 30;
+
   return base::TaskScheduler::InitParams(
       base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(2, 8, 0.1, 0),
-          base::TimeDelta::FromSeconds(30)),
+          base::RecommendedMaxNumberOfThreadsInPool(
+              kMinBackgroundThreads, kMaxBackgroundThreads,
+              kCoreMultiplierBackgroundThreads, kOffsetBackgroundThreads),
+          base::TimeDelta::FromSeconds(kReclaimTimeBackground)),
       base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(2, 8, 0.1, 0),
-          base::TimeDelta::FromSeconds(30)),
-      base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.3, 0),
-          base::TimeDelta::FromSeconds(30)),
-      base::SchedulerWorkerPoolParams(
-          base::RecommendedMaxNumberOfThreadsInPool(3, 8, 0.3, 0),
-          base::TimeDelta::FromSeconds(60)));
+          base::RecommendedMaxNumberOfThreadsInPool(
+              kMinForegroundThreads, kMaxForegroundThreads,
+              kCoreMultiplierForegroundThreads, kOffsetForegroundThreads),
+          base::TimeDelta::FromSeconds(kReclaimTimeForeground)));
 }
 
 }  // namespace
diff --git a/ios/web/public/web_state/session_certificate_policy_cache.h b/ios/web/public/web_state/session_certificate_policy_cache.h
index 794ccd0..2fcabd0 100644
--- a/ios/web/public/web_state/session_certificate_policy_cache.h
+++ b/ios/web/public/web_state/session_certificate_policy_cache.h
@@ -8,10 +8,6 @@
 #include "base/memory/ref_counted.h"
 #include "net/cert/cert_status_flags.h"
 
-namespace net {
-class X509Certificate;
-}
-
 namespace web {
 
 class CertificatePolicyCache;
@@ -25,16 +21,6 @@
   SessionCertificatePolicyCache() {}
   virtual ~SessionCertificatePolicyCache() {}
 
-  // Stores certificate information that a user has indicated should be allowed
-  // for this session.
-  virtual void RegisterAllowedCertificate(
-      const scoped_refptr<net::X509Certificate> certificate,
-      const std::string& host,
-      net::CertStatus status) = 0;
-
-  // Removes all previously allowed certificates.
-  virtual void ClearAllowedCertificates() = 0;
-
   // Transfers the allowed certificate information from this session to |cache|.
   virtual void UpdateCertificatePolicyCache(
       const scoped_refptr<web::CertificatePolicyCache>& cache) const = 0;
diff --git a/ios/web/test/test_web_thread_bundle.cc b/ios/web/test/test_web_thread_bundle.cc
index 1e12d6b9..326ffd4 100644
--- a/ios/web/test/test_web_thread_bundle.cc
+++ b/ios/web/test/test_web_thread_bundle.cc
@@ -87,8 +87,7 @@
       std::make_unique<base::internal::TaskSchedulerImpl>(
           "ScopedTaskEnvironment"));
   base::TaskScheduler::GetInstance()->Start(
-      {worker_pool_params, worker_pool_params, worker_pool_params,
-       worker_pool_params});
+      {worker_pool_params, worker_pool_params});
 }
 
 }  // namespace web
diff --git a/ios/web/web_state/bad_ssl_response_inttest.mm b/ios/web/web_state/bad_ssl_response_inttest.mm
index 2b224ca..f461bce9 100644
--- a/ios/web/web_state/bad_ssl_response_inttest.mm
+++ b/ios/web/web_state/bad_ssl_response_inttest.mm
@@ -5,11 +5,13 @@
 #include "base/run_loop.h"
 #import "base/test/ios/wait_util.h"
 #include "base/test/scoped_feature_list.h"
+#include "ios/web/public/certificate_policy_cache.h"
 #include "ios/web/public/features.h"
 #import "ios/web/public/test/fakes/test_web_client.h"
 #import "ios/web/public/test/navigation_test_util.h"
 #import "ios/web/public/test/web_test_with_web_state.h"
 #import "ios/web/public/test/web_view_content_test_util.h"
+#include "ios/web/public/web_state/session_certificate_policy_cache.h"
 #include "net/cert/x509_certificate.h"
 #include "net/ssl/ssl_info.h"
 #include "net/test/embedded_test_server/default_handlers.h"
@@ -117,6 +119,42 @@
   // the load succeeds.
   ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "Echo"));
   EXPECT_EQ(url, web_state()->GetLastCommittedURL());
+
+  // Verify that |UpdateCertificatePolicyCache| correctly updates
+  // CertificatePolicyCache from another BrowserState.
+  TestBrowserState other_browser_state;
+  auto cache = BrowserState::GetCertificatePolicyCache(&other_browser_state);
+  scoped_refptr<net::X509Certificate> cert = https_server_.GetCertificate();
+  CertPolicy::Judgment default_judgement = cache->QueryPolicy(
+      cert.get(), url.host(), net::CERT_STATUS_AUTHORITY_INVALID);
+  ASSERT_EQ(CertPolicy::Judgment::UNKNOWN, default_judgement);
+  web_state()->GetSessionCertificatePolicyCache()->UpdateCertificatePolicyCache(
+      cache);
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForPageLoadTimeout, ^{
+    base::RunLoop().RunUntilIdle();
+    CertPolicy::Judgment policy = cache->QueryPolicy(
+        cert.get(), url.host(), net::CERT_STATUS_AUTHORITY_INVALID);
+    return CertPolicy::Judgment::ALLOWED == policy;
+  }));
+}
+
+// Tests navigation to a page with self signed SSL cert and allowing the load
+// via WebClient. Subsequent navigation should not call AllowCertificateError
+// but always allow the load.
+TEST_P(BadSslResponseTest, RememberCertDecision) {
+  // Allow the load via WebClient.
+  web_client()->SetAllowCertificateErrors(true);
+  GURL url(https_server_.GetURL("/echo"));
+  test::LoadUrl(web_state(), url);
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "Echo"));
+  EXPECT_EQ(url, web_state()->GetLastCommittedURL());
+
+  // Make sure that second loads succeeds without consulting WebClient.
+  web_client()->SetAllowCertificateErrors(false);
+  GURL url2(https_server_.GetURL("/echoall"));
+  test::LoadUrl(web_state(), url2);
+  ASSERT_TRUE(test::WaitForWebViewContainingText(web_state(), "GET /echoall"));
+  EXPECT_EQ(url2, web_state()->GetLastCommittedURL());
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/ios/web/web_state/session_certificate_policy_cache_impl.h b/ios/web/web_state/session_certificate_policy_cache_impl.h
index 86d1d11f..41ebfdab 100644
--- a/ios/web/web_state/session_certificate_policy_cache_impl.h
+++ b/ios/web/web_state/session_certificate_policy_cache_impl.h
@@ -9,6 +9,10 @@
 
 #include "ios/web/public/web_state/session_certificate_policy_cache.h"
 
+namespace net {
+class X509Certificate;
+}
+
 namespace web {
 
 // Concrete implementation of SessionCertificatePolicyCache.
@@ -18,13 +22,15 @@
   ~SessionCertificatePolicyCacheImpl() override;
 
   // SessionCertificatePolicyCache:
+  void UpdateCertificatePolicyCache(
+      const scoped_refptr<web::CertificatePolicyCache>& cache) const override;
+
+  // Stores certificate information that a user has indicated should be allowed
+  // for this session.
   void RegisterAllowedCertificate(
       const scoped_refptr<net::X509Certificate> certificate,
       const std::string& host,
-      net::CertStatus status) override;
-  void ClearAllowedCertificates() override;
-  void UpdateCertificatePolicyCache(
-      const scoped_refptr<web::CertificatePolicyCache>& cache) const override;
+      net::CertStatus status);
 
   // Allows for batch updating the allowed certificate storages.
   void SetAllowedCerts(NSSet* allowed_certs);
diff --git a/ios/web/web_state/session_certificate_policy_cache_impl.mm b/ios/web/web_state/session_certificate_policy_cache_impl.mm
index f115c4e..cd7241a 100644
--- a/ios/web/web_state/session_certificate_policy_cache_impl.mm
+++ b/ios/web/web_state/session_certificate_policy_cache_impl.mm
@@ -51,22 +51,6 @@
 
 SessionCertificatePolicyCacheImpl::~SessionCertificatePolicyCacheImpl() {}
 
-void SessionCertificatePolicyCacheImpl::RegisterAllowedCertificate(
-    const scoped_refptr<net::X509Certificate> certificate,
-    const std::string& host,
-    net::CertStatus status) {
-  DCHECK_CURRENTLY_ON(WebThread::UI);
-  [allowed_certs_ addObject:[[CRWSessionCertificateStorage alloc]
-                                initWithCertificate:certificate
-                                               host:host
-                                             status:status]];
-}
-
-void SessionCertificatePolicyCacheImpl::ClearAllowedCertificates() {
-  DCHECK_CURRENTLY_ON(WebThread::UI);
-  [allowed_certs_ removeAllObjects];
-}
-
 void SessionCertificatePolicyCacheImpl::UpdateCertificatePolicyCache(
     const scoped_refptr<web::CertificatePolicyCache>& cache) const {
   DCHECK_CURRENTLY_ON(WebThread::UI);
@@ -82,6 +66,17 @@
       }));
 }
 
+void SessionCertificatePolicyCacheImpl::RegisterAllowedCertificate(
+    const scoped_refptr<net::X509Certificate> certificate,
+    const std::string& host,
+    net::CertStatus status) {
+  DCHECK_CURRENTLY_ON(WebThread::UI);
+  [allowed_certs_ addObject:[[CRWSessionCertificateStorage alloc]
+                                initWithCertificate:certificate
+                                               host:host
+                                             status:status]];
+}
+
 void SessionCertificatePolicyCacheImpl::SetAllowedCerts(NSSet* allowed_certs) {
   allowed_certs_ = [allowed_certs mutableCopy];
 }
diff --git a/ios/web/web_state/session_certificate_policy_cache_impl_unittest.mm b/ios/web/web_state/session_certificate_policy_cache_impl_unittest.mm
index 93da74fd..d258830 100644
--- a/ios/web/web_state/session_certificate_policy_cache_impl_unittest.mm
+++ b/ios/web/web_state/session_certificate_policy_cache_impl_unittest.mm
@@ -81,15 +81,6 @@
   EXPECT_EQ(status_, cert_storage.status);
 }
 
-// Tests that ClearAllowedCerts() successfully removes all previously added
-// certs.
-TEST_F(SessionCertificatePolicyCacheImplTest, ClearAllowedCerts) {
-  // Clear the certs and verify that it has been removed.
-  EXPECT_EQ(1U, cache_.GetAllowedCerts().count);
-  cache_.ClearAllowedCertificates();
-  EXPECT_EQ(0U, cache_.GetAllowedCerts().count);
-}
-
 // Tests that UpdateCertificatePolicyCache() successfully transfers the allowed
 // certificate information to a CertificatePolicyCache.
 TEST_F(SessionCertificatePolicyCacheImplTest, UpdateCertificatePolicyCache) {
diff --git a/ios/web/web_state/ui/BUILD.gn b/ios/web/web_state/ui/BUILD.gn
index 1207c4b0..4113d6f 100644
--- a/ios/web/web_state/ui/BUILD.gn
+++ b/ios/web/web_state/ui/BUILD.gn
@@ -14,6 +14,7 @@
     "//base",
     "//ios/net",
     "//ios/web:core",
+    "//ios/web/find_in_page",
     "//ios/web/interstitials",
     "//ios/web/navigation",
     "//ios/web/navigation:core",
@@ -24,6 +25,7 @@
     "//ios/web/web_state:error_translation_util",
     "//ios/web/web_state:navigation_context",
     "//ios/web/web_state:page_viewport_state",
+    "//ios/web/web_state:session_certificate_policy_cache",
     "//ios/web/web_state:web_frame",
     "//ios/web/web_state:web_state_impl_header",
     "//ios/web/web_state:web_view_internal_creation_util",
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index a43c54c..222e975 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -40,6 +40,7 @@
 #include "base/values.h"
 #include "crypto/symmetric_key.h"
 #import "ios/net/http_response_headers_util.h"
+#import "ios/web/find_in_page/find_in_page_manager_impl.h"
 #include "ios/web/history_state_util.h"
 #import "ios/web/interstitials/web_interstitial_impl.h"
 #import "ios/web/navigation/crw_navigation_item_holder.h"
@@ -71,7 +72,6 @@
 #import "ios/web/public/web_state/js/crw_js_injection_manager.h"
 #import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
 #import "ios/web/public/web_state/page_display_state.h"
-#include "ios/web/public/web_state/session_certificate_policy_cache.h"
 #import "ios/web/public/web_state/ui/crw_content_view.h"
 #import "ios/web/public/web_state/ui/crw_context_menu_delegate.h"
 #import "ios/web/public/web_state/ui/crw_native_content.h"
@@ -90,6 +90,7 @@
 #import "ios/web/web_state/js/crw_js_window_id_manager.h"
 #import "ios/web/web_state/navigation_context_impl.h"
 #import "ios/web/web_state/page_viewport_state.h"
+#import "ios/web/web_state/session_certificate_policy_cache_impl.h"
 #import "ios/web/web_state/ui/crw_context_menu_controller.h"
 #import "ios/web/web_state/ui/crw_swipe_recognizer_provider.h"
 #import "ios/web/web_state/ui/crw_web_controller.h"
@@ -958,6 +959,7 @@
         std::make_unique<CertVerificationErrorsCacheType>(kMaxCertErrorsCount);
     _navigationStates = [[CRWWKNavigationStates alloc] init];
     web::WebFramesManagerImpl::CreateForWebState(_webStateImpl);
+    web::FindInPageManagerImpl::CreateForWebState(_webStateImpl);
     [[NSNotificationCenter defaultCenter]
         addObserver:self
            selector:@selector(orientationDidChange)
@@ -3929,8 +3931,8 @@
           [_certVerificationController allowCert:leafCert
                                          forHost:host
                                           status:info.cert_status];
-          _webStateImpl->GetSessionCertificatePolicyCache()
-              ->RegisterAllowedCertificate(
+          _webStateImpl->GetSessionCertificatePolicyCacheImpl()
+              .RegisterAllowedCertificate(
                   leafCert, base::SysNSStringToUTF8(host), info.cert_status);
           // New navigation is a different navigation from the original one.
           // The new navigation is always browser-initiated and happens when
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 3685b09..c1ff8c66 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
@@ -20,7 +20,6 @@
 #include "net/cert/ct_policy_enforcer.h"
 #include "net/cert/multi_log_ct_verifier.h"
 #include "net/dns/host_resolver.h"
-#include "net/extras/sqlite/sqlite_channel_id_store.h"
 #include "net/extras/sqlite/sqlite_persistent_cookie_store.h"
 #include "net/http/http_auth_handler_factory.h"
 #include "net/http/http_cache.h"
@@ -31,8 +30,6 @@
 #include "net/log/net_log.h"
 #include "net/proxy_resolution/proxy_config_service_ios.h"
 #include "net/proxy_resolution/proxy_resolution_service.h"
-#include "net/ssl/channel_id_service.h"
-#include "net/ssl/default_channel_id_store.h"
 #include "net/ssl/ssl_config_service_defaults.h"
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/data_protocol_handler.h"
@@ -115,18 +112,6 @@
             base::CreateSequencedTaskRunnerWithTraits(
                 {base::MayBlock(), base::TaskPriority::BEST_EFFORT}));
 
-    // Setup channel id store.
-    base::FilePath channel_id_path;
-    base::PathService::Get(base::DIR_APP_DATA, &channel_id_path);
-    channel_id_path =
-        channel_id_path.Append("ChromeWebView").Append("Channel ID");
-    scoped_refptr<net::SQLiteChannelIDStore> channel_id_db =
-        new net::SQLiteChannelIDStore(
-            channel_id_path,
-            base::CreateSequencedTaskRunnerWithTraits(
-                {base::MayBlock(), base::TaskPriority::BEST_EFFORT}));
-    storage_->set_channel_id_service(std::make_unique<net::ChannelIDService>(
-        new net::DefaultChannelIDStore(channel_id_db.get())));
     storage_->set_http_server_properties(
         std::unique_ptr<net::HttpServerProperties>(
             new net::HttpServerPropertiesImpl()));
@@ -145,8 +130,6 @@
         url_request_context_->transport_security_state();
     network_session_context.cert_transparency_verifier =
         url_request_context_->cert_transparency_verifier();
-    network_session_context.channel_id_service =
-        url_request_context_->channel_id_service();
     network_session_context.net_log = url_request_context_->net_log();
     network_session_context.proxy_resolution_service =
         url_request_context_->proxy_resolution_service();
diff --git a/jingle/glue/network_service_config_test_util.cc b/jingle/glue/network_service_config_test_util.cc
index f249e7d6..5abedba 100644
--- a/jingle/glue/network_service_config_test_util.cc
+++ b/jingle/glue/network_service_config_test_util.cc
@@ -6,8 +6,12 @@
 
 #include "jingle/glue/network_service_config_test_util.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/bind.h"
 #include "base/synchronization/waitable_event.h"
+#include "base/task/post_task.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "base/threading/thread_restrictions.h"
 
@@ -38,6 +42,13 @@
   }
 }
 
+NetworkServiceConfigTestUtil::NetworkServiceConfigTestUtil(
+    NetworkContextGetter network_context_getter)
+    : net_runner_(base::CreateSingleThreadTaskRunnerWithTraits({})),
+      mojo_runner_(base::SequencedTaskRunnerHandle::Get()),
+      network_context_getter_(network_context_getter),
+      weak_ptr_factory_(this) {}
+
 NetworkServiceConfigTestUtil::~NetworkServiceConfigTestUtil() {
   if (!net_runner_->BelongsToCurrentThread()) {
     base::ScopedAllowBaseSyncPrimitivesForTesting permission;
@@ -81,7 +92,12 @@
 void NetworkServiceConfigTestUtil::RequestSocketOnMojoRunner(
     base::WeakPtr<NetworkServiceConfigTestUtil> instance,
     network::mojom::ProxyResolvingSocketFactoryRequest request) {
-  if (instance) {
+  if (!instance)
+    return;
+  if (instance->network_context_getter_) {
+    instance->network_context_getter_.Run()->CreateProxyResolvingSocketFactory(
+        std::move(request));
+  } else {
     instance->network_context_ptr_->CreateProxyResolvingSocketFactory(
         std::move(request));
   }
diff --git a/jingle/glue/network_service_config_test_util.h b/jingle/glue/network_service_config_test_util.h
index 62dc1f07..57d79a8 100644
--- a/jingle/glue/network_service_config_test_util.h
+++ b/jingle/glue/network_service_config_test_util.h
@@ -18,10 +18,19 @@
 class WaitableEvent;
 }
 
+namespace network {
+namespace mojom {
+class NetworkContext;
+}
+}  // namespace network
+
 namespace jingle_glue {
 
 class NetworkServiceConfigTestUtil {
  public:
+  using NetworkContextGetter =
+      base::RepeatingCallback<network::mojom::NetworkContext*()>;
+
   // All public methods must be called on the thread this is created on,
   // but the callback returned by MakeSocketFactoryCallback() is expected to be
   // run on |url_request_context_getter->GetNetworkTaskRunner()|, which can be,
@@ -29,6 +38,8 @@
   // can block, but will not spin the event loop.
   explicit NetworkServiceConfigTestUtil(
       scoped_refptr<net::URLRequestContextGetter> url_request_context_getter);
+  explicit NetworkServiceConfigTestUtil(
+      NetworkContextGetter network_context_getter);
   ~NetworkServiceConfigTestUtil();
 
   // Configures |config| to run the result of MakeSocketFactoryCallback()
@@ -54,6 +65,7 @@
   scoped_refptr<base::SingleThreadTaskRunner> net_runner_;
   scoped_refptr<base::SequencedTaskRunner> mojo_runner_;
   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
+  NetworkContextGetter network_context_getter_;
   std::unique_ptr<network::NetworkContext>
       network_context_;  // lives on |net_runner_|
   network::mojom::NetworkContextPtr
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc
index fab6bfba..d1092da 100644
--- a/media/blink/webmediaplayer_impl.cc
+++ b/media/blink/webmediaplayer_impl.cc
@@ -1657,7 +1657,24 @@
   // URL, since MediaPlayer doesn't support data:// URLs, fail playback now.
   const bool found_hls = status == PipelineStatus::DEMUXER_ERROR_DETECTED_HLS;
   if (found_hls && mb_data_source_) {
-    demuxer_found_hls_ = found_hls;
+    UMA_HISTOGRAM_BOOLEAN("Media.WebMediaPlayerImpl.HLS.IsCorsCrossOrigin",
+                          mb_data_source_->IsCorsCrossOrigin());
+    // Note: Does not consider the full redirect chain. Redirecting through
+    // another origin will set WouldTaintOrigin() though, assuming that the
+    // crossorigin attribute is not set.
+    bool frame_url_is_cryptographic = url::Origin(frame_->GetSecurityOrigin())
+                                          .GetURL()
+                                          .SchemeIsCryptographic();
+    bool manifest_url_is_cryptographic =
+        loaded_url_.SchemeIsCryptographic() &&
+        mb_data_source_->GetUrlAfterRedirects().SchemeIsCryptographic();
+    UMA_HISTOGRAM_BOOLEAN(
+        "Media.WebMediaPlayerImpl.HLS.IsMixedContent",
+        frame_url_is_cryptographic && !manifest_url_is_cryptographic);
+    UMA_HISTOGRAM_BOOLEAN("Media.WebMediaPlayerImpl.HLS.WouldTaintOrigin",
+                          WouldTaintOrigin());
+    // Note: Affects WouldTaintOrigin().
+    demuxer_found_hls_ = true;
 
     renderer_factory_selector_->SetUseMediaPlayer(true);
 
diff --git a/media/cast/receiver/video_decoder.cc b/media/cast/receiver/video_decoder.cc
index ad6ea05..74d8d80 100644
--- a/media/cast/receiver/video_decoder.cc
+++ b/media/cast/receiver/video_decoder.cc
@@ -199,9 +199,8 @@
     // Make sure this is a JSON string.
     if (!len || data[0] != '{')
       return NULL;
-    base::JSONReader reader;
-    std::unique_ptr<base::Value> values(
-        reader.Read(base::StringPiece(reinterpret_cast<char*>(data), len)));
+    std::unique_ptr<base::Value> values(base::JSONReader::Read(
+        base::StringPiece(reinterpret_cast<char*>(data), len)));
     if (!values)
       return NULL;
     base::DictionaryValue* dict = NULL;
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 64c02a9..d4e784b 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -570,7 +570,8 @@
   const base::TimeDelta stream_timestamp =
       ConvertStreamTimestamp(stream_->time_base, packet->pts);
 
-  if (stream_timestamp == kNoTimestamp) {
+  if (stream_timestamp == kNoTimestamp ||
+      stream_timestamp == kInfiniteDuration) {
     MEDIA_LOG(ERROR, media_log_) << "FFmpegDemuxer: PTS is not defined";
     demuxer_->NotifyDemuxerError(DEMUXER_ERROR_COULD_NOT_PARSE);
     return;
diff --git a/media/filters/fuchsia/fuchsia_video_decoder.cc b/media/filters/fuchsia/fuchsia_video_decoder.cc
index 7a84dd6b..3cf45416 100644
--- a/media/filters/fuchsia/fuchsia_video_decoder.cc
+++ b/media/filters/fuchsia/fuchsia_video_decoder.cc
@@ -404,7 +404,7 @@
   auto codec_factory =
       base::fuchsia::ComponentContext::GetDefault()
           ->ConnectToService<fuchsia::mediacodec::CodecFactory>();
-  codec_factory->CreateDecoder2(std::move(codec_params), codec_.NewRequest());
+  codec_factory->CreateDecoder(std::move(codec_params), codec_.NewRequest());
 
   codec_.set_error_handler(
       [this](zx_status_t status) {
diff --git a/media/gpu/BUILD.gn b/media/gpu/BUILD.gn
index 0149d86..f79fed0f2 100644
--- a/media/gpu/BUILD.gn
+++ b/media/gpu/BUILD.gn
@@ -641,14 +641,11 @@
       "test:frame_file_writer",
       "test:frame_validator",
       "test:video_player",
-      "//base/test:test_support",
+      "test:video_player_test_environment",
       "//media:test_support",
       "//mojo/core/embedder",
       "//testing/gtest",
     ]
-    if (use_vaapi) {
-      deps += [ "//media/gpu/vaapi" ]
-    }
   }
 }
 
diff --git a/media/gpu/android/codec_image.cc b/media/gpu/android/codec_image.cc
index 05eaef9..5cf2987 100644
--- a/media/gpu/android/codec_image.cc
+++ b/media/gpu/android/codec_image.cc
@@ -202,8 +202,11 @@
   return true;
 }
 
-bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode) {
+bool CodecImage::RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode,
+                                                 bool bind_egl_image) {
   DCHECK(texture_owner_);
+  DCHECK(bind_egl_image);
+
   if (phase_ == Phase::kInFrontBuffer)
     return true;
   if (phase_ == Phase::kInvalidated)
@@ -221,14 +224,17 @@
   std::unique_ptr<ui::ScopedMakeCurrent> scoped_make_current =
       MakeCurrentIfNeeded(texture_owner_.get());
   // If we have to switch contexts, then we always want to restore the
-  // bindings.
+  // bindings. Also if bind_egl_image is set to false, we do no need to restore
+  // any bindings since UpdateTexImage will not bind any egl image to the
+  // texture target.
   bool should_restore_bindings =
-      bindings_mode == BindingsMode::kRestore || !!scoped_make_current;
+      (bindings_mode == BindingsMode::kRestore || !!scoped_make_current) &&
+      bind_egl_image;
 
   GLint bound_service_id = 0;
   if (should_restore_bindings)
     glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &bound_service_id);
-  texture_owner_->UpdateTexImage();
+  texture_owner_->UpdateTexImage(bind_egl_image);
   if (should_restore_bindings)
     glBindTexture(GL_TEXTURE_EXTERNAL_OES, bound_service_id);
   return true;
diff --git a/media/gpu/android/codec_image.h b/media/gpu/android/codec_image.h
index 8aaeaa6..f435e1b 100644
--- a/media/gpu/android/codec_image.h
+++ b/media/gpu/android/codec_image.h
@@ -109,11 +109,14 @@
   // frame available event before calling UpdateTexImage(). Passing
   // BindingsMode::kDontRestore skips the work of restoring the current texture
   // bindings if the texture owner's context is already current. Otherwise,
-  // this switches contexts and preserves the texture bindings.
-  // Returns true if the buffer is in the front buffer. Returns false if the
-  // buffer was invalidated.
+  // this switches contexts and preserves the texture bindings. Setting
+  // |bind_egl_image| = false skips doing the egl bindings to the texture target
+  // during texture update. |bind_egl_image| must always be true when using a
+  // SurfaceTexture backed CodecImage(TextureOwner). Returns true if the buffer
+  // is in the front buffer. Returns false if the buffer was invalidated.
   enum class BindingsMode { kRestore, kDontRestore };
-  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode);
+  bool RenderToTextureOwnerFrontBuffer(BindingsMode bindings_mode,
+                                       bool bind_egl_image = true);
 
   // Renders this image to the overlay. Returns true if the buffer is in the
   // overlay front buffer. Returns false if the buffer was invalidated.
diff --git a/media/gpu/android/codec_image_unittest.cc b/media/gpu/android/codec_image_unittest.cc
index 3271bfe..a0f9027 100644
--- a/media/gpu/android/codec_image_unittest.cc
+++ b/media/gpu/android/codec_image_unittest.cc
@@ -156,7 +156,7 @@
   InSequence s;
   EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
   EXPECT_CALL(*texture_owner_, WaitForFrameAvailable());
-  EXPECT_CALL(*texture_owner_, UpdateTexImage());
+  EXPECT_CALL(*texture_owner_, UpdateTexImage(true));
   i->CopyTexImage(GL_TEXTURE_EXTERNAL_OES);
   ASSERT_TRUE(i->was_rendered_to_front_buffer());
 }
@@ -166,7 +166,7 @@
   InSequence s;
   EXPECT_CALL(*codec_, ReleaseOutputBuffer(_, true));
   EXPECT_CALL(*texture_owner_, WaitForFrameAvailable());
-  EXPECT_CALL(*texture_owner_, UpdateTexImage());
+  EXPECT_CALL(*texture_owner_, UpdateTexImage(true));
   EXPECT_CALL(*texture_owner_, GetTransformMatrix(_));
   float matrix[16];
   i->GetTextureMatrix(matrix);
@@ -245,7 +245,7 @@
   glGenTextures(1, &pre_bound_texture);
   glBindTexture(GL_TEXTURE_EXTERNAL_OES, pre_bound_texture);
   auto i = NewImage(kTextureOwner);
-  EXPECT_CALL(*texture_owner_, UpdateTexImage());
+  EXPECT_CALL(*texture_owner_, UpdateTexImage(true));
   i->RenderToFrontBuffer();
   GLint post_bound_texture = 0;
   glGetIntegerv(GL_TEXTURE_BINDING_EXTERNAL_OES, &post_bound_texture);
@@ -264,7 +264,7 @@
 
   auto i = NewImage(kTextureOwner);
   // Our context should not be current when UpdateTexImage() is called.
-  EXPECT_CALL(*texture_owner_, UpdateTexImage()).WillOnce(Invoke([&]() {
+  EXPECT_CALL(*texture_owner_, UpdateTexImage(true)).WillOnce(Invoke([&](bool) {
     ASSERT_FALSE(context->IsCurrent(surface.get()));
   }));
   i->RenderToFrontBuffer();
@@ -299,7 +299,7 @@
   EXPECT_EQ(texture_owner_->get_a_hardware_buffer_count, 0);
   EXPECT_FALSE(i->was_rendered_to_front_buffer());
 
-  EXPECT_CALL(*texture_owner_, UpdateTexImage());
+  EXPECT_CALL(*texture_owner_, UpdateTexImage(true));
   i->GetAHardwareBuffer();
   EXPECT_EQ(texture_owner_->get_a_hardware_buffer_count, 1);
   EXPECT_TRUE(i->was_rendered_to_front_buffer());
diff --git a/media/gpu/android/image_reader_gl_owner.cc b/media/gpu/android/image_reader_gl_owner.cc
index 056864f..2971cd5eaf 100644
--- a/media/gpu/android/image_reader_gl_owner.cc
+++ b/media/gpu/android/image_reader_gl_owner.cc
@@ -185,7 +185,7 @@
   return gl::ScopedJavaSurface::AcquireExternalSurface(j_surface);
 }
 
-void ImageReaderGLOwner::UpdateTexImage() {
+void ImageReaderGLOwner::UpdateTexImage(bool bind_egl_image) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
 
   // If we've lost the texture, then do nothing.
@@ -251,9 +251,12 @@
   current_image_fence_ = std::move(scoped_acquire_fence_fd);
   current_image_bound_ = false;
 
-  // TODO(khushalsagar): This should be on the public API so that we only bind
-  // the texture if we were going to render it without an overlay.
-  EnsureTexImageBound();
+  // Skip generating and binding egl image if bind_egl_image is false.
+  if (bind_egl_image) {
+    // TODO(khushalsagar): This should be on the public API so that we only bind
+    // the texture if we were going to render it without an overlay.
+    EnsureTexImageBound();
+  }
 }
 
 void ImageReaderGLOwner::EnsureTexImageBound() {
diff --git a/media/gpu/android/image_reader_gl_owner.h b/media/gpu/android/image_reader_gl_owner.h
index f3d4857..48b4c978 100644
--- a/media/gpu/android/image_reader_gl_owner.h
+++ b/media/gpu/android/image_reader_gl_owner.h
@@ -34,7 +34,7 @@
   gl::GLContext* GetContext() const override;
   gl::GLSurface* GetSurface() const override;
   gl::ScopedJavaSurface CreateJavaSurface() const override;
-  void UpdateTexImage() override;
+  void UpdateTexImage(bool bind_egl_image) override;
   void GetTransformMatrix(float mtx[16]) override;
   void ReleaseBackBuffers() override;
   void SetReleaseTimeToNow() override;
diff --git a/media/gpu/android/mock_texture_owner.h b/media/gpu/android/mock_texture_owner.h
index 25815ab..adcff47 100644
--- a/media/gpu/android/mock_texture_owner.h
+++ b/media/gpu/android/mock_texture_owner.h
@@ -28,7 +28,7 @@
   MOCK_CONST_METHOD0(GetContext, gl::GLContext*());
   MOCK_CONST_METHOD0(GetSurface, gl::GLSurface*());
   MOCK_CONST_METHOD0(CreateJavaSurface, gl::ScopedJavaSurface());
-  MOCK_METHOD0(UpdateTexImage, void());
+  MOCK_METHOD1(UpdateTexImage, void(bool bind_egl_image));
   MOCK_METHOD1(GetTransformMatrix, void(float mtx[16]));
   MOCK_METHOD0(ReleaseBackBuffers, void());
   MOCK_METHOD0(SetReleaseTimeToNow, void());
diff --git a/media/gpu/android/surface_texture_gl_owner.cc b/media/gpu/android/surface_texture_gl_owner.cc
index a692b2b4..af0da9b 100644
--- a/media/gpu/android/surface_texture_gl_owner.cc
+++ b/media/gpu/android/surface_texture_gl_owner.cc
@@ -68,8 +68,11 @@
   return gl::ScopedJavaSurface(surface_texture_.get());
 }
 
-void SurfaceTextureGLOwner::UpdateTexImage() {
+// bind_egl_image is a no-op for surface texture since it always binds the egl
+// image under the hood.
+void SurfaceTextureGLOwner::UpdateTexImage(bool bind_egl_image) {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  DCHECK(bind_egl_image);
   if (surface_texture_)
     surface_texture_->UpdateTexImage();
 }
diff --git a/media/gpu/android/surface_texture_gl_owner.h b/media/gpu/android/surface_texture_gl_owner.h
index 91f0ad6f..a65c48e 100644
--- a/media/gpu/android/surface_texture_gl_owner.h
+++ b/media/gpu/android/surface_texture_gl_owner.h
@@ -33,7 +33,7 @@
   gl::GLContext* GetContext() const override;
   gl::GLSurface* GetSurface() const override;
   gl::ScopedJavaSurface CreateJavaSurface() const override;
-  void UpdateTexImage() override;
+  void UpdateTexImage(bool bind_egl_image) override;
   void GetTransformMatrix(float mtx[16]) override;
   void ReleaseBackBuffers() override;
   void SetReleaseTimeToNow() override;
diff --git a/media/gpu/android/texture_owner.h b/media/gpu/android/texture_owner.h
index a2004262..c2619cc 100644
--- a/media/gpu/android/texture_owner.h
+++ b/media/gpu/android/texture_owner.h
@@ -69,7 +69,9 @@
   virtual gl::ScopedJavaSurface CreateJavaSurface() const = 0;
 
   // Update the texture image using the latest available image data.
-  virtual void UpdateTexImage() = 0;
+  // |bind_egl_image| hints the underlying implementation whether an egl image
+  // should be bound to the texture target or not.
+  virtual void UpdateTexImage(bool bind_egl_image = true) = 0;
 
   // Transformation matrix if any associated with the texture image.
   virtual void GetTransformMatrix(float mtx[16]) = 0;
diff --git a/media/gpu/test/BUILD.gn b/media/gpu/test/BUILD.gn
index 33e26c7..058dda9 100644
--- a/media/gpu/test/BUILD.gn
+++ b/media/gpu/test/BUILD.gn
@@ -145,16 +145,33 @@
       "video_player/video_player.cc",
       "video_player/video_player.h",
     ]
-    data = [
-      "//media/test/data/",
-    ]
     deps = [
       ":decode_helpers",
       "//media/gpu",
     ]
+  }
+
+  static_library("video_player_test_environment") {
+    testonly = true
+    sources = [
+      "video_player/video_player_test_environment.h",
+    ]
+    data = [
+      "//media/test/data/",
+    ]
+    deps = [
+      ":video_player",
+      "//base/test:test_config",
+      "//base/test:test_support",
+      "//media/gpu",
+      "//testing/gtest:gtest",
+    ]
     if (use_ozone) {
       deps += [ "//ui/ozone" ]
     }
+    if (use_vaapi) {
+      deps += [ "//media/gpu/vaapi" ]
+    }
   }
 }
 
diff --git a/media/gpu/test/video_frame_file_writer.cc b/media/gpu/test/video_frame_file_writer.cc
index 6fa87a6..12c55118 100644
--- a/media/gpu/test/video_frame_file_writer.cc
+++ b/media/gpu/test/video_frame_file_writer.cc
@@ -14,15 +14,12 @@
 #include "media/gpu/test/video_frame_mapper_factory.h"
 #include "ui/gfx/codec/png_codec.h"
 
-// Default output folder used to store frames.
-constexpr const base::FilePath::CharType* kDefaultOutputFolder =
-    FILE_PATH_LITERAL("video_frames");
-
 namespace media {
 namespace test {
 
-VideoFrameFileWriter::VideoFrameFileWriter()
-    : num_frames_writing_(0),
+VideoFrameFileWriter::VideoFrameFileWriter(const base::FilePath& output_folder)
+    : output_folder_(output_folder),
+      num_frames_writing_(0),
       frame_writer_thread_("FrameWriterThread"),
       frame_writer_cv_(&frame_writer_lock_) {
   DETACH_FROM_SEQUENCE(writer_sequence_checker_);
@@ -30,12 +27,17 @@
 }
 
 VideoFrameFileWriter::~VideoFrameFileWriter() {
-  Destroy();
+  base::AutoLock auto_lock(frame_writer_lock_);
+  DCHECK_EQ(0u, num_frames_writing_);
+
+  frame_writer_thread_.Stop();
 }
 
 // static
-std::unique_ptr<VideoFrameFileWriter> VideoFrameFileWriter::Create() {
-  auto frame_file_writer = base::WrapUnique(new VideoFrameFileWriter());
+std::unique_ptr<VideoFrameFileWriter> VideoFrameFileWriter::Create(
+    const base::FilePath& output_folder) {
+  auto frame_file_writer =
+      base::WrapUnique(new VideoFrameFileWriter(output_folder));
   if (!frame_file_writer->Initialize()) {
     LOG(ERROR) << "Failed to initialize VideoFrameFileWriter";
     return nullptr;
@@ -56,24 +58,6 @@
     return false;
   }
 
-  SetOutputFolder(base::FilePath(kDefaultOutputFolder));
-  return true;
-}
-
-void VideoFrameFileWriter::Destroy() {
-  base::AutoLock auto_lock(frame_writer_lock_);
-  DCHECK_EQ(0u, num_frames_writing_);
-
-  frame_writer_thread_.Stop();
-}
-
-void VideoFrameFileWriter::SetOutputFolder(
-    const base::FilePath& output_folder) {
-  base::AutoLock auto_lock(frame_writer_lock_);
-  DCHECK_EQ(0u, num_frames_writing_);
-
-  output_folder_ = output_folder;
-
   // If the directory is not absolute, consider it relative to our working dir.
   if (!output_folder_.IsAbsolute()) {
     output_folder_ = base::MakeAbsoluteFilePath(
@@ -85,13 +69,8 @@
   if (!DirectoryExists(output_folder_)) {
     base::CreateDirectory(output_folder_);
   }
-}
 
-void VideoFrameFileWriter::WaitUntilDone() const {
-  base::AutoLock auto_lock(frame_writer_lock_);
-  while (num_frames_writing_ > 0) {
-    frame_writer_cv_.Wait();
-  }
+  return true;
 }
 
 void VideoFrameFileWriter::ProcessVideoFrame(
@@ -108,6 +87,14 @@
                      base::Unretained(this), video_frame, frame_index));
 }
 
+bool VideoFrameFileWriter::WaitUntilDone() {
+  base::AutoLock auto_lock(frame_writer_lock_);
+  while (num_frames_writing_ > 0) {
+    frame_writer_cv_.Wait();
+  }
+  return true;
+}
+
 void VideoFrameFileWriter::ProcessVideoFrameTask(
     scoped_refptr<const VideoFrame> video_frame,
     size_t frame_index) {
diff --git a/media/gpu/test/video_frame_file_writer.h b/media/gpu/test/video_frame_file_writer.h
index c0c9276..149bedbf 100644
--- a/media/gpu/test/video_frame_file_writer.h
+++ b/media/gpu/test/video_frame_file_writer.h
@@ -18,6 +18,10 @@
 namespace media {
 namespace test {
 
+// Default output folder used to store frames.
+constexpr const base::FilePath::CharType* kDefaultOutputFolder =
+    FILE_PATH_LITERAL("video_frames");
+
 class VideoFrameMapper;
 
 // The video frame file writer class implements functionality to write video
@@ -27,32 +31,28 @@
   ~VideoFrameFileWriter() override;
 
   // Create an instance of the video frame file writer.
-  static std::unique_ptr<VideoFrameFileWriter> Create();
-
-  // Set the folder the video frame files will be written to.
-  void SetOutputFolder(const base::FilePath& output_folder);
-
-  // Wait until all currently scheduled frame write operations are done.
-  void WaitUntilDone() const;
+  static std::unique_ptr<VideoFrameFileWriter> Create(
+      const base::FilePath& output_folder =
+          base::FilePath(kDefaultOutputFolder));
 
   // Interface VideoFrameProcessor
   void ProcessVideoFrame(scoped_refptr<const VideoFrame> video_frame,
                          size_t frame_index) override;
+  // Wait until all currently scheduled frame write operations are done.
+  bool WaitUntilDone() override;
 
  private:
-  VideoFrameFileWriter();
+  explicit VideoFrameFileWriter(const base::FilePath& output_folder);
 
   // Initialize the video frame file writer.
   bool Initialize();
-  // Destroy the video frame file writer.
-  void Destroy();
 
   // Writes the specified video frame to file on the |file_writer_thread_|.
   void ProcessVideoFrameTask(scoped_refptr<const VideoFrame> video_frame,
                              size_t frame_index);
 
   // Output folder the frames will be written to.
-  base::FilePath output_folder_ GUARDED_BY(frame_writer_lock_);
+  base::FilePath output_folder_;
 
   // The video frame mapper used to gain access to the raw video frame memory.
   std::unique_ptr<VideoFrameMapper> video_frame_mapper_;
diff --git a/media/gpu/test/video_frame_helpers.h b/media/gpu/test/video_frame_helpers.h
index 6958adc9..c834f0b 100644
--- a/media/gpu/test/video_frame_helpers.h
+++ b/media/gpu/test/video_frame_helpers.h
@@ -36,6 +36,10 @@
   // VideoFrameProcessor.
   virtual void ProcessVideoFrame(scoped_refptr<const VideoFrame> video_frame,
                                  size_t frame_index) = 0;
+
+  // Wait until all currently scheduled frames have been processed. Returns
+  // whether processing was successful.
+  virtual bool WaitUntilDone() = 0;
 };
 
 // Convert and copy the |src_frame| to the specified |dst_frame|. Supported
diff --git a/media/gpu/test/video_frame_validator.cc b/media/gpu/test/video_frame_validator.cc
index ad98531..6b5fa03 100644
--- a/media/gpu/test/video_frame_validator.cc
+++ b/media/gpu/test/video_frame_validator.cc
@@ -138,14 +138,6 @@
   return mismatched_frames_.size();
 }
 
-bool VideoFrameValidator::WaitUntilValidated() const {
-  base::AutoLock auto_lock(frame_validator_lock_);
-  while (num_frames_validating_ > 0) {
-    frame_validator_cv_.Wait();
-  }
-  return mismatched_frames_.size() == 0u;
-}
-
 void VideoFrameValidator::ProcessVideoFrame(
     scoped_refptr<const VideoFrame> video_frame,
     size_t frame_index) {
@@ -162,6 +154,19 @@
                      base::Unretained(this), video_frame, frame_index));
 }
 
+bool VideoFrameValidator::WaitUntilDone() {
+  base::AutoLock auto_lock(frame_validator_lock_);
+  while (num_frames_validating_ > 0) {
+    frame_validator_cv_.Wait();
+  }
+
+  if (mismatched_frames_.size() > 0u) {
+    LOG(ERROR) << mismatched_frames_.size() << " frames failed to validate.";
+    return false;
+  }
+  return true;
+}
+
 void VideoFrameValidator::ProcessVideoFrameTask(
     const scoped_refptr<const VideoFrame> video_frame,
     size_t frame_index) {
diff --git a/media/gpu/test/video_frame_validator.h b/media/gpu/test/video_frame_validator.h
index 3defb38..60813be 100644
--- a/media/gpu/test/video_frame_validator.h
+++ b/media/gpu/test/video_frame_validator.h
@@ -88,14 +88,13 @@
   // function is thread-safe.
   size_t GetMismatchedFramesCount() const;
 
-  // Wait until all currently scheduled frame validations are done. Returns true
-  // if no corrupt frames were found. This function might take a long time to
-  // complete, depending on the platform.
-  bool WaitUntilValidated() const;
-
   // Interface VideoFrameProcessor
   void ProcessVideoFrame(scoped_refptr<const VideoFrame> video_frame,
                          size_t frame_index) override;
+  // Wait until all currently scheduled frame validations are done. Returns true
+  // if no corrupt frames were found. This function might take a long time to
+  // complete, depending on the platform.
+  bool WaitUntilDone() override;
 
  private:
   VideoFrameValidator(uint32_t flags,
diff --git a/media/gpu/test/video_player/frame_renderer_dummy.cc b/media/gpu/test/video_player/frame_renderer_dummy.cc
index 020b747..3cb5b18 100644
--- a/media/gpu/test/video_player/frame_renderer_dummy.cc
+++ b/media/gpu/test/video_player/frame_renderer_dummy.cc
@@ -8,10 +8,6 @@
 #include <vector>
 
 #include "base/memory/ptr_util.h"
-#if defined(USE_OZONE)
-#include "ui/ozone/public/ozone_gpu_test_helper.h"
-#include "ui/ozone/public/ozone_platform.h"
-#endif
 
 #define VLOGF(level) VLOG(level) << __func__ << "(): "
 
@@ -22,9 +18,7 @@
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
-FrameRendererDummy::~FrameRendererDummy() {
-  Destroy();
-}
+FrameRendererDummy::~FrameRendererDummy() {}
 
 // static
 std::unique_ptr<FrameRendererDummy> FrameRendererDummy::Create() {
@@ -36,37 +30,9 @@
 }
 
 bool FrameRendererDummy::Initialize() {
-#if defined(USE_OZONE)
-  // Initialize Ozone. This is necessary even though we are not doing any actual
-  // rendering. If not initialized a crash will occur when assigning picture
-  // buffers, even when passing 0 as texture ID.
-  // TODO(@dstaessens):
-  // * Get rid of the Ozone dependency, as it forces us to call 'stop ui' when
-  //   running tests.
-  LOG(INFO) << "Initializing Ozone Platform...\n"
-               "If this hangs indefinitely please call 'stop ui' first!";
-  ui::OzonePlatform::InitParams params = {.single_process = false};
-  ui::OzonePlatform::InitializeForUI(params);
-  ui::OzonePlatform::InitializeForGPU(params);
-  ui::OzonePlatform::GetInstance()->AfterSandboxEntry();
-
-  // Initialize the Ozone GPU helper. If this is not done an error will occur:
-  // "Check failed: drm. No devices available for buffer allocation."
-  // Note: If a task environment is not set up initialization will hang
-  // indefinitely here.
-  gpu_helper_.reset(new ui::OzoneGpuTestHelper());
-  gpu_helper_->Initialize(base::ThreadTaskRunnerHandle::Get());
-#endif
-
   return true;
 }
 
-void FrameRendererDummy::Destroy() {
-#if defined(USE_OZONE)
-  gpu_helper_.reset();
-#endif
-}
-
 void FrameRendererDummy::AcquireGLContext() {
   // As no actual rendering is done we don't have a GLContext to acquire.
 }
diff --git a/media/gpu/test/video_player/frame_renderer_dummy.h b/media/gpu/test/video_player/frame_renderer_dummy.h
index 6ba496c..ce14e5b 100644
--- a/media/gpu/test/video_player/frame_renderer_dummy.h
+++ b/media/gpu/test/video_player/frame_renderer_dummy.h
@@ -11,14 +11,6 @@
 #include "base/sequence_checker.h"
 #include "media/gpu/test/video_player/frame_renderer.h"
 
-#ifdef USE_OZONE
-namespace ui {
-
-class OzoneGpuTestHelper;
-
-}  // namespace ui
-#endif
-
 namespace media {
 namespace test {
 
@@ -43,12 +35,6 @@
 
   // Initialize the frame renderer, performs all rendering-related setup.
   bool Initialize();
-  // Destroy the frame renderer.
-  void Destroy();
-
-#ifdef USE_OZONE
-  std::unique_ptr<ui::OzoneGpuTestHelper> gpu_helper_;
-#endif
 
   SEQUENCE_CHECKER(sequence_checker_);
   DISALLOW_COPY_AND_ASSIGN(FrameRendererDummy);
diff --git a/media/gpu/test/video_player/video_decoder_client.cc b/media/gpu/test/video_player/video_decoder_client.cc
index 97822bed..69fe89b2 100644
--- a/media/gpu/test/video_player/video_decoder_client.cc
+++ b/media/gpu/test/video_player/video_decoder_client.cc
@@ -25,12 +25,12 @@
 
 VideoDecoderClient::VideoDecoderClient(
     const VideoPlayer::EventCallback& event_cb,
-    FrameRenderer* renderer,
-    const std::vector<VideoFrameProcessor*>& frame_processors,
+    std::unique_ptr<FrameRenderer> renderer,
+    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
     const VideoDecoderClientConfig& config)
     : event_cb_(event_cb),
-      frame_renderer_(renderer),
-      frame_processors_(frame_processors),
+      frame_renderer_(std::move(renderer)),
+      frame_processors_(std::move(frame_processors)),
       decoder_client_thread_("VDAClientDecoderThread"),
       decoder_client_state_(VideoDecoderClientState::kUninitialized),
       decoder_client_config_(config),
@@ -41,17 +41,24 @@
 
 VideoDecoderClient::~VideoDecoderClient() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(video_player_sequence_checker_);
-  Destroy();
+
+  DestroyDecoder();
+  decoder_client_thread_.Stop();
+
+  // Wait until the frame processors are done, before destroying them. As the
+  // decoder has been destroyed no new frames will be sent to the processors.
+  WaitForFrameProcessors();
 }
 
 // static
 std::unique_ptr<VideoDecoderClient> VideoDecoderClient::Create(
     const VideoPlayer::EventCallback& event_cb,
-    FrameRenderer* frame_renderer,
-    const std::vector<VideoFrameProcessor*>& frame_processors,
+    std::unique_ptr<FrameRenderer> frame_renderer,
+    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
     const VideoDecoderClientConfig& config) {
-  auto decoder_client = base::WrapUnique(new VideoDecoderClient(
-      event_cb, frame_renderer, frame_processors, config));
+  auto decoder_client = base::WrapUnique(
+      new VideoDecoderClient(event_cb, std::move(frame_renderer),
+                             std::move(frame_processors), config));
   if (!decoder_client->Initialize()) {
     return nullptr;
   }
@@ -78,13 +85,6 @@
   return true;
 }
 
-void VideoDecoderClient::Destroy() {
-  DCHECK_CALLED_ON_VALID_SEQUENCE(video_player_sequence_checker_);
-
-  DestroyDecoder();
-  decoder_client_thread_.Stop();
-}
-
 void VideoDecoderClient::CreateDecoder(
     const VideoDecodeAccelerator::Config& config,
     const std::vector<uint8_t>& stream) {
@@ -107,6 +107,13 @@
   done.Wait();
 }
 
+bool VideoDecoderClient::WaitForFrameProcessors() {
+  bool success = true;
+  for (auto& frame_processor : frame_processors_)
+    success &= frame_processor->WaitUntilDone();
+  return success;
+}
+
 void VideoDecoderClient::Play() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(video_player_sequence_checker_);
 
@@ -191,7 +198,7 @@
 
   frame_renderer_->RenderFrame(wrapped_video_frame);
 
-  for (VideoFrameProcessor* frame_processor : frame_processors_)
+  for (auto& frame_processor : frame_processors_)
     frame_processor->ProcessVideoFrame(wrapped_video_frame,
                                        current_frame_index_);
   current_frame_index_++;
@@ -266,7 +273,7 @@
   if (hasGLContext) {
     decoder_factory_ = GpuVideoDecodeAcceleratorFactory::Create(
         base::BindRepeating(&FrameRenderer::GetGLContext,
-                            base::Unretained(frame_renderer_)),
+                            base::Unretained(frame_renderer_.get())),
         base::BindRepeating([]() { return true; }),
         base::BindRepeating([](uint32_t, uint32_t,
                                const scoped_refptr<gl::GLImage>&,
diff --git a/media/gpu/test/video_player/video_decoder_client.h b/media/gpu/test/video_player/video_decoder_client.h
index 552e2b9..b4e432a 100644
--- a/media/gpu/test/video_player/video_decoder_client.h
+++ b/media/gpu/test/video_player/video_decoder_client.h
@@ -55,8 +55,8 @@
   // thread-safe.
   static std::unique_ptr<VideoDecoderClient> Create(
       const VideoPlayer::EventCallback& event_cb,
-      FrameRenderer* frame_renderer,
-      const std::vector<VideoFrameProcessor*>& frame_processors,
+      std::unique_ptr<FrameRenderer> frame_renderer,
+      std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
       const VideoDecoderClientConfig& config);
 
   // Create a decoder with specified |config| and video |stream|. The video
@@ -67,6 +67,10 @@
   // Destroy the currently active decoder.
   void DestroyDecoder();
 
+  // Wait until all frame processors have finished processing. Returns whether
+  // processing was successful.
+  bool WaitForFrameProcessors();
+
   // Start decoding the video stream, decoder should be idle when this function
   // is called. This function is non-blocking, for each frame decoded a
   // 'kFrameDecoded' event will be thrown.
@@ -87,13 +91,13 @@
     kResetting,
   };
 
-  VideoDecoderClient(const VideoPlayer::EventCallback& event_cb,
-                     FrameRenderer* renderer,
-                     const std::vector<VideoFrameProcessor*>& frame_processors,
-                     const VideoDecoderClientConfig& config);
+  VideoDecoderClient(
+      const VideoPlayer::EventCallback& event_cb,
+      std::unique_ptr<FrameRenderer> renderer,
+      std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
+      const VideoDecoderClientConfig& config);
 
   bool Initialize();
-  void Destroy();
 
   // VideoDecodeAccelerator::Client implementation
   void ProvidePictureBuffers(uint32_t requested_num_of_buffers,
@@ -135,8 +139,8 @@
   int32_t GetNextPictureBufferId();
 
   VideoPlayer::EventCallback event_cb_;
-  FrameRenderer* const frame_renderer_;
-  std::vector<VideoFrameProcessor*> const frame_processors_;
+  std::unique_ptr<FrameRenderer> const frame_renderer_;
+  std::vector<std::unique_ptr<VideoFrameProcessor>> const frame_processors_;
 
   std::unique_ptr<GpuVideoDecodeAcceleratorFactory> decoder_factory_;
   std::unique_ptr<VideoDecodeAccelerator> decoder_;
diff --git a/media/gpu/test/video_player/video_player.cc b/media/gpu/test/video_player/video_player.cc
index 79dd642..2fb37a3 100644
--- a/media/gpu/test/video_player/video_player.cc
+++ b/media/gpu/test/video_player/video_player.cc
@@ -31,18 +31,19 @@
 // static
 std::unique_ptr<VideoPlayer> VideoPlayer::Create(
     const Video* video,
-    FrameRenderer* frame_renderer,
-    const std::vector<VideoFrameProcessor*>& frame_processors,
+    std::unique_ptr<FrameRenderer> frame_renderer,
+    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
     const VideoDecoderClientConfig& config) {
   auto video_player = base::WrapUnique(new VideoPlayer());
-  video_player->Initialize(video, frame_renderer, frame_processors, config);
+  video_player->Initialize(video, std::move(frame_renderer),
+                           std::move(frame_processors), config);
   return video_player;
 }
 
 void VideoPlayer::Initialize(
     const Video* video,
-    FrameRenderer* frame_renderer,
-    const std::vector<VideoFrameProcessor*>& frame_processors,
+    std::unique_ptr<FrameRenderer> frame_renderer,
+    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
     const VideoDecoderClientConfig& config) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK_EQ(video_player_state_, VideoPlayerState::kUninitialized);
@@ -52,8 +53,8 @@
   EventCallback event_cb =
       base::BindRepeating(&VideoPlayer::NotifyEvent, base::Unretained(this));
 
-  decoder_client_ = VideoDecoderClient::Create(event_cb, frame_renderer,
-                                               frame_processors, config);
+  decoder_client_ = VideoDecoderClient::Create(
+      event_cb, std::move(frame_renderer), std::move(frame_processors), config);
   CHECK(decoder_client_) << "Failed to create decoder client";
 
   // Create a decoder for the specified video. We'll always use import mode as
@@ -180,6 +181,10 @@
   return video_player_event_counts_[static_cast<size_t>(event)];
 }
 
+bool VideoPlayer::WaitForFrameProcessors() {
+  return !decoder_client_ || decoder_client_->WaitForFrameProcessors();
+}
+
 size_t VideoPlayer::GetFlushDoneCount() const {
   return GetEventCount(VideoPlayerEvent::kFlushDone);
 }
diff --git a/media/gpu/test/video_player/video_player.h b/media/gpu/test/video_player/video_player.h
index 2a233d3..bdfeb22 100644
--- a/media/gpu/test/video_player/video_player.h
+++ b/media/gpu/test/video_player/video_player.h
@@ -15,6 +15,8 @@
 #include "base/synchronization/condition_variable.h"
 #include "base/synchronization/lock.h"
 #include "base/thread_annotations.h"
+#include "media/gpu/test/video_frame_helpers.h"
+#include "media/gpu/test/video_player/frame_renderer.h"
 
 namespace media {
 namespace test {
@@ -23,7 +25,6 @@
 class Video;
 class VideoDecoderClient;
 struct VideoDecoderClientConfig;
-class VideoFrameProcessor;
 
 // Default timeout used when waiting for events.
 constexpr base::TimeDelta kDefaultTimeout = base::TimeDelta::FromSeconds(30);
@@ -59,10 +60,14 @@
   // guarantee they outlive the video player.
   static std::unique_ptr<VideoPlayer> Create(
       const Video* video,
-      FrameRenderer* frame_renderer,
-      const std::vector<VideoFrameProcessor*>& frame_processors,
+      std::unique_ptr<FrameRenderer> frame_renderer,
+      std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
       const VideoDecoderClientConfig& config);
 
+  // Wait until all frame processors have finished processing. Returns whether
+  // processing was successful.
+  bool WaitForFrameProcessors();
+
   // Play the video asynchronously.
   void Play();
   // Play the video asynchronously. Automatically pause decoding when the
@@ -106,10 +111,11 @@
  private:
   VideoPlayer();
 
-  void Initialize(const Video* video,
-                  FrameRenderer* frame_renderer,
-                  const std::vector<VideoFrameProcessor*>& frame_processors,
-                  const VideoDecoderClientConfig& config);
+  void Initialize(
+      const Video* video,
+      std::unique_ptr<FrameRenderer> frame_renderer,
+      std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors,
+      const VideoDecoderClientConfig& config);
   void Destroy();
 
   // Notify the video player an event has occurred (e.g. frame decoded). Returns
diff --git a/media/gpu/test/video_player/video_player_test_environment.h b/media/gpu/test/video_player/video_player_test_environment.h
new file mode 100644
index 0000000..3d36e3ea
--- /dev/null
+++ b/media/gpu/test/video_player/video_player_test_environment.h
@@ -0,0 +1,91 @@
+// 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 MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_PLAYER_TEST_ENVIRONMENT_H_
+#define MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_PLAYER_TEST_ENVIRONMENT_H_
+
+#include <memory>
+#include "base/at_exit.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_timeouts.h"
+#include "media/gpu/buildflags.h"
+#include "media/gpu/test/video_player/video.h"
+#if BUILDFLAG(USE_VAAPI)
+#include "media/gpu/vaapi/vaapi_wrapper.h"
+#endif
+#include "testing/gtest/include/gtest/gtest.h"
+#if defined(USE_OZONE)
+#include "ui/ozone/public/ozone_gpu_test_helper.h"
+#include "ui/ozone/public/ozone_platform.h"
+#endif
+
+namespace media {
+namespace test {
+
+// Test environment for video decode tests. Performs setup and teardown once for
+// the entire test run.
+class VideoPlayerTestEnvironment : public ::testing::Environment {
+ public:
+  explicit VideoPlayerTestEnvironment(const Video* video) : video_(video) {}
+
+  // Set up the video decode test environment, only called once.
+  void SetUp() override;
+  // Tear down the video decode test environment, only called once.
+  void TearDown() override;
+
+  std::unique_ptr<base::test::ScopedTaskEnvironment> task_environment_;
+  const Video* video_ = nullptr;
+  bool enable_validator_ = true;
+  bool output_frames_ = false;
+
+ private:
+  // An exit manager is required to run callbacks on shutdown.
+  base::AtExitManager at_exit_manager;
+
+#if defined(USE_OZONE)
+  std::unique_ptr<ui::OzoneGpuTestHelper> gpu_helper_;
+#endif
+};
+
+void VideoPlayerTestEnvironment::SetUp() {
+  // Setting up a task environment will create a task runner for the current
+  // thread and allow posting tasks to other threads. This is required for the
+  // test video player to function correctly.
+  TestTimeouts::Initialize();
+  task_environment_ = std::make_unique<base::test::ScopedTaskEnvironment>(
+      base::test::ScopedTaskEnvironment::MainThreadType::UI);
+
+  // Perform all static initialization that is required when running video
+  // decoders in a test environment.
+#if BUILDFLAG(USE_VAAPI)
+  media::VaapiWrapper::PreSandboxInitialization();
+#endif
+
+#if defined(USE_OZONE)
+  // Initialize Ozone. This is necessary to gain access to the GPU for hardware
+  // video decode acceleration.
+  LOG(WARNING) << "Initializing Ozone Platform...\n"
+                  "If this hangs indefinitely please call 'stop ui' first!";
+  ui::OzonePlatform::InitParams params = {.single_process = false};
+  ui::OzonePlatform::InitializeForUI(params);
+  ui::OzonePlatform::InitializeForGPU(params);
+  ui::OzonePlatform::GetInstance()->AfterSandboxEntry();
+
+  // Initialize the Ozone GPU helper. If this is not done an error will occur:
+  // "Check failed: drm. No devices available for buffer allocation."
+  // Note: If a task environment is not set up initialization will hang
+  // indefinitely here.
+  gpu_helper_.reset(new ui::OzoneGpuTestHelper());
+  gpu_helper_->Initialize(base::ThreadTaskRunnerHandle::Get());
+#endif
+}
+
+void VideoPlayerTestEnvironment::TearDown() {
+  task_environment_.reset();
+}
+
+}  // namespace test
+}  // namespace media
+
+#endif  // MEDIA_GPU_TEST_VIDEO_PLAYER_VIDEO_PLAYER_TEST_ENVIRONMENT_H_
diff --git a/media/gpu/v4l2/v4l2_image_processor.cc b/media/gpu/v4l2/v4l2_image_processor.cc
index 0b408d9..76b576f 100644
--- a/media/gpu/v4l2/v4l2_image_processor.cc
+++ b/media/gpu/v4l2/v4l2_image_processor.cc
@@ -99,9 +99,6 @@
 
   DCHECK(!device_thread_.IsRunning());
   DCHECK(!device_poll_thread_.IsRunning());
-
-  DestroyInputBuffers();
-  DestroyOutputBuffers();
 }
 
 void V4L2ImageProcessor::NotifyError() {
@@ -296,14 +293,22 @@
     return false;
   }
 
-  if (!CreateInputBuffers() || !CreateOutputBuffers())
-    return false;
-
   if (!device_thread_.Start()) {
     VLOGF(1) << "Initialize(): device thread failed to start";
     return false;
   }
 
+  // Call to AllocateBuffers must be asynchronous.
+  base::WaitableEvent done;
+  bool result;
+  device_thread_.task_runner()->PostTask(
+      FROM_HERE, base::BindOnce(&V4L2ImageProcessor::AllocateBuffersTask,
+                                base::Unretained(this), &result, &done));
+  done.Wait();
+  if (!result) {
+    return false;
+  }
+
   // StartDevicePoll will NotifyError on failure.
   device_thread_.task_runner()->PostTask(
       FROM_HERE,
@@ -465,6 +470,9 @@
     device_thread_.task_runner()->PostTask(
         FROM_HERE, base::BindOnce(&V4L2ImageProcessor::StopDevicePoll,
                                   base::Unretained(this)));
+    device_thread_.task_runner()->PostTask(
+        FROM_HERE, base::Bind(&V4L2ImageProcessor::DestroyBuffersTask,
+                              base::Unretained(this)));
     // Wait for tasks to finish/early-exit.
     device_thread_.Stop();
   } else {
@@ -475,8 +483,7 @@
 
 bool V4L2ImageProcessor::CreateInputBuffers() {
   VLOGF(2);
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
-
+  DCHECK(device_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!input_streamon_);
 
   struct v4l2_control control;
@@ -543,7 +550,7 @@
 
 bool V4L2ImageProcessor::CreateOutputBuffers() {
   VLOGF(2);
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK(device_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!output_streamon_);
 
   struct v4l2_rect visible_rect;
@@ -600,7 +607,7 @@
 
 void V4L2ImageProcessor::DestroyInputBuffers() {
   VLOGF(2);
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK(device_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!input_streamon_);
 
   struct v4l2_requestbuffers reqbufs;
@@ -616,7 +623,7 @@
 
 void V4L2ImageProcessor::DestroyOutputBuffers() {
   VLOGF(2);
-  DCHECK_CALLED_ON_VALID_THREAD(client_thread_checker_);
+  DCHECK(device_thread_.task_runner()->BelongsToCurrentThread());
   DCHECK(!output_streamon_);
 
   output_buffer_map_.clear();
@@ -896,6 +903,23 @@
   return true;
 }
 
+void V4L2ImageProcessor::AllocateBuffersTask(bool* result,
+                                             base::WaitableEvent* done) {
+  VLOGF(2);
+  DCHECK(device_thread_.task_runner()->BelongsToCurrentThread());
+
+  *result = CreateInputBuffers() && CreateOutputBuffers();
+  done->Signal();
+}
+
+void V4L2ImageProcessor::DestroyBuffersTask() {
+  VLOGF(2);
+  DCHECK(device_thread_.task_runner()->BelongsToCurrentThread());
+
+  DestroyInputBuffers();
+  DestroyOutputBuffers();
+}
+
 void V4L2ImageProcessor::StartDevicePoll() {
   DVLOGF(3) << "starting device poll";
   DCHECK(device_thread_.task_runner()->BelongsToCurrentThread());
diff --git a/media/gpu/v4l2/v4l2_image_processor.h b/media/gpu/v4l2/v4l2_image_processor.h
index 8d9936b..78e180f1c 100644
--- a/media/gpu/v4l2/v4l2_image_processor.h
+++ b/media/gpu/v4l2/v4l2_image_processor.h
@@ -149,6 +149,10 @@
   void ProcessTask(std::unique_ptr<JobRecord> job_record);
   void ServiceDeviceTask();
 
+  // Allocate/Destroy the input/output V4L2 buffers.
+  void AllocateBuffersTask(bool* result, base::WaitableEvent* done);
+  void DestroyBuffersTask();
+
   // Attempt to start/stop device_poll_thread_.
   void StartDevicePoll();
   void StopDevicePoll();
diff --git a/media/gpu/vaapi/vaapi_wrapper.cc b/media/gpu/vaapi/vaapi_wrapper.cc
index 28194690..67d24a1 100644
--- a/media/gpu/vaapi/vaapi_wrapper.cc
+++ b/media/gpu/vaapi/vaapi_wrapper.cc
@@ -1016,19 +1016,20 @@
   for (size_t i = 0; i < num_planes; ++i) {
     va_attrib_extbuf.pitches[i] = pixmap->GetDmaBufPitch(i);
     va_attrib_extbuf.offsets[i] = pixmap->GetDmaBufOffset(i);
-    int dmabuf_fd = pixmap->GetDmaBufFd(i);
-    if (dmabuf_fd < 0) {
-      LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap";
-      return nullptr;
-    }
-    fds[i] = base::checked_cast<uintptr_t>(dmabuf_fd);
     DVLOG(4) << "plane " << i << ": pitch: " << va_attrib_extbuf.pitches[i]
              << " offset: " << va_attrib_extbuf.offsets[i];
   }
-
   va_attrib_extbuf.num_planes = num_planes;
-  va_attrib_extbuf.buffers = fds.data();
-  va_attrib_extbuf.num_buffers = fds.size();
+
+  if (pixmap->GetDmaBufFd(0) < 0) {
+    LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap";
+    return nullptr;
+  }
+  // We only have to pass the first file descriptor to a driver. A VA-API driver
+  // shall create a VASurface from the single fd correctly.
+  uintptr_t fd = base::checked_cast<uintptr_t>(pixmap->GetDmaBufFd(0));
+  va_attrib_extbuf.buffers = &fd;
+  va_attrib_extbuf.num_buffers = 1u;
 
   va_attrib_extbuf.flags = 0;
   va_attrib_extbuf.private_data = NULL;
diff --git a/media/gpu/video_decode_accelerator_tests.cc b/media/gpu/video_decode_accelerator_tests.cc
index 7f06e12..65032498 100644
--- a/media/gpu/video_decode_accelerator_tests.cc
+++ b/media/gpu/video_decode_accelerator_tests.cc
@@ -2,12 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/at_exit.h"
 #include "base/command_line.h"
-#include "base/test/scoped_task_environment.h"
-#include "base/test/test_timeouts.h"
 #include "media/base/test_data_util.h"
-#include "media/gpu/buildflags.h"
 #include "media/gpu/test/video_frame_file_writer.h"
 #include "media/gpu/test/video_frame_validator.h"
 #include "media/gpu/test/video_player/frame_renderer_dummy.h"
@@ -15,68 +11,30 @@
 #include "media/gpu/test/video_player/video_collection.h"
 #include "media/gpu/test/video_player/video_decoder_client.h"
 #include "media/gpu/test/video_player/video_player.h"
+#include "media/gpu/test/video_player/video_player_test_environment.h"
 #include "mojo/core/embedder/embedder.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-#if BUILDFLAG(USE_VAAPI)
-#include "media/gpu/vaapi/vaapi_wrapper.h"
-#endif
-
 namespace media {
 namespace test {
 
 namespace {
-// Test environment for video decode tests. Performs setup and teardown once for
-// the entire test run.
-class VideoDecoderTestEnvironment : public ::testing::Environment {
- public:
-  explicit VideoDecoderTestEnvironment(const Video* video) : video_(video) {}
-  virtual ~VideoDecoderTestEnvironment() {}
+// Video decoder tests usage message.
+constexpr const char* usage_msg =
+    "usage: video_decode_accelerator_tests [--help] [--disable_validator]\n"
+    "                                      [--output_frames] [<video>]\n";
+// Video decoder tests help message.
+constexpr const char* help_msg =
+    "Run the video decode accelerator tests on the specified video. If no\n"
+    "video is specified the default \"test-25fps.h264\" video will be used.\n"
+    "\nThe following arguments are supported:\n"
+    "  --disable_validator  disable frame validation, useful on old\n"
+    "                       platforms that don't support import mode.\n"
+    "  --output_frames      write all decoded video frames to the\n"
+    "                       \"video_frames\" folder.\n"
+    "  --help               display this help and exit.\n";
 
-  // Set up the video decode test environment, only called once.
-  void SetUp() override;
-  // Tear down the video decode test environment, only called once.
-  void TearDown() override;
-
-  std::unique_ptr<base::test::ScopedTaskEnvironment> task_environment_;
-  std::unique_ptr<FrameRendererDummy> dummy_frame_renderer_;
-  std::unique_ptr<VideoFrameFileWriter> frame_file_writer_;
-  const Video* const video_;
-
-  // An exit manager is required to run callbacks on shutdown.
-  base::AtExitManager at_exit_manager;
-};
-
-void VideoDecoderTestEnvironment::SetUp() {
-  // Setting up a task environment will create a task runner for the current
-  // thread and allow posting tasks to other threads. This is required for the
-  // test video player to function correctly.
-  TestTimeouts::Initialize();
-  task_environment_ = std::make_unique<base::test::ScopedTaskEnvironment>(
-      base::test::ScopedTaskEnvironment::MainThreadType::UI);
-
-  // Set the default test data path.
-  media::test::Video::SetTestDataPath(media::GetTestDataPath());
-
-  // Perform all static initialization that is required when running video
-  // decoders in a test environment.
-#if BUILDFLAG(USE_VAAPI)
-  media::VaapiWrapper::PreSandboxInitialization();
-#endif
-
-  dummy_frame_renderer_ = FrameRendererDummy::Create();
-  ASSERT_NE(dummy_frame_renderer_, nullptr);
-
-  frame_file_writer_ = media::test::VideoFrameFileWriter::Create();
-}
-
-void VideoDecoderTestEnvironment::TearDown() {
-  frame_file_writer_.reset();
-  dummy_frame_renderer_.reset();
-  task_environment_.reset();
-}
-
-media::test::VideoDecoderTestEnvironment* g_env;
+media::test::VideoPlayerTestEnvironment* g_env;
 
 // Video decode test class. Performs setup and teardown for each single test.
 class VideoDecoderTest : public ::testing::Test {
@@ -84,32 +42,28 @@
   std::unique_ptr<VideoPlayer> CreateVideoPlayer(
       const Video* video,
       const VideoDecoderClientConfig& config = VideoDecoderClientConfig()) {
-    frame_validators_.push_back(
-        media::test::VideoFrameValidator::Create(video->FrameChecksums()));
-    return VideoPlayer::Create(
-        video, g_env->dummy_frame_renderer_.get(),
-        {frame_validators_.back().get(), g_env->frame_file_writer_.get()},
-        config);
+    LOG_ASSERT(video);
+    std::vector<std::unique_ptr<VideoFrameProcessor>> frame_processors;
+
+    // Validate decoded video frames.
+    if (g_env->output_frames_) {
+      frame_processors.push_back(
+          media::test::VideoFrameValidator::Create(video->FrameChecksums()));
+    }
+
+    // Write decoded video frames to the 'video_frames/<test_name/>' folder.
+    if (g_env->enable_validator_) {
+      const ::testing::TestInfo* const test_info =
+          ::testing::UnitTest::GetInstance()->current_test_info();
+      base::FilePath output_folder =
+          base::FilePath("video_frames")
+              .Append(base::FilePath(test_info->name()));
+      frame_processors.push_back(VideoFrameFileWriter::Create(output_folder));
+    }
+
+    return VideoPlayer::Create(video, FrameRendererDummy::Create(),
+                               std::move(frame_processors), config);
   }
-
-  void SetUp() override {
-    const ::testing::TestInfo* const test_info =
-        ::testing::UnitTest::GetInstance()->current_test_info();
-    // Change the video frame output folder to 'video_frames/test_name/'.
-    base::FilePath output_folder =
-        base::FilePath("video_frames")
-            .Append(base::FilePath(test_info->name()));
-    g_env->frame_file_writer_->SetOutputFolder(output_folder);
-  }
-
-  void TearDown() override { g_env->frame_file_writer_->WaitUntilDone(); }
-
-  const VideoFrameValidator* GetFrameValidator(size_t index = 0) {
-    return frame_validators_[index].get();
-  }
-
- protected:
-  std::vector<std::unique_ptr<VideoFrameValidator>> frame_validators_;
 };
 
 }  // namespace
@@ -124,7 +78,7 @@
 
   EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
   EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
-  EXPECT_TRUE(GetFrameValidator()->WaitUntilValidated());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
 // Flush the decoder immediately after initialization.
@@ -138,7 +92,7 @@
 
   EXPECT_EQ(tvp->GetFlushDoneCount(), 2u);
   EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
-  EXPECT_TRUE(GetFrameValidator()->WaitUntilValidated());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
 // Flush the decoder immediately after doing a mid-stream reset, without waiting
@@ -160,7 +114,7 @@
   EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
   EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
   EXPECT_LE(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
-  EXPECT_TRUE(GetFrameValidator()->WaitUntilValidated());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
 // Reset the decoder immediately after initialization.
@@ -175,7 +129,7 @@
   EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
   EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
   EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
-  EXPECT_TRUE(GetFrameValidator()->WaitUntilValidated());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
 // Reset the decoder when the middle of the stream is reached.
@@ -194,7 +148,7 @@
   EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
   EXPECT_EQ(tvp->GetFrameDecodedCount(),
             numFramesDecoded + g_env->video_->NumFrames());
-  EXPECT_TRUE(GetFrameValidator()->WaitUntilValidated());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
 // Reset the decoder when the end of the stream is reached.
@@ -212,7 +166,7 @@
   EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
   EXPECT_EQ(tvp->GetFlushDoneCount(), 2u);
   EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames() * 2);
-  EXPECT_TRUE(GetFrameValidator()->WaitUntilValidated());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
 // Reset the decoder immediately when the end-of-stream flush starts, without
@@ -234,7 +188,7 @@
   EXPECT_LE(tvp->GetFlushDoneCount(), 1u);
   EXPECT_EQ(tvp->GetResetDoneCount(), 1u);
   EXPECT_LE(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
-  EXPECT_TRUE(GetFrameValidator()->WaitUntilValidated());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
 // Reset the decoder immediately when encountering the first config info in a
@@ -260,7 +214,7 @@
   EXPECT_EQ(tvp->GetFrameDecodedCount(),
             numFramesDecoded + g_env->video_->NumFrames());
   EXPECT_GE(tvp->GetEventCount(VideoPlayerEvent::kConfigInfo), 1u);
-  EXPECT_EQ(0u, GetFrameValidator()->GetMismatchedFramesCount());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
 // Play video from start to end. Multiple buffer decodes will be queued in the
@@ -275,7 +229,7 @@
 
   EXPECT_EQ(tvp->GetFlushDoneCount(), 1u);
   EXPECT_EQ(tvp->GetFrameDecodedCount(), g_env->video_->NumFrames());
-  EXPECT_TRUE(GetFrameValidator()->WaitUntilValidated());
+  EXPECT_TRUE(tvp->WaitForFrameProcessors());
 }
 
 // Play multiple videos simultaneously from start to finish.
@@ -295,7 +249,7 @@
     EXPECT_TRUE(tvps[i]->WaitForFlushDone());
     EXPECT_EQ(tvps[i]->GetFlushDoneCount(), 1u);
     EXPECT_EQ(tvps[i]->GetFrameDecodedCount(), g_env->video_->NumFrames());
-    EXPECT_TRUE(GetFrameValidator()->WaitUntilValidated());
+    EXPECT_TRUE(tvps[i]->WaitForFrameProcessors());
   }
 }
 
@@ -303,8 +257,18 @@
 }  // namespace media
 
 int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
   base::CommandLine::Init(argc, argv);
+  const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
+
+  // Print the help message if requested. This needs to be done before
+  // initializing gtest, to overwrite the default gtest help message.
+  LOG_ASSERT(cmd_line);
+  if (cmd_line->HasSwitch("help")) {
+    std::cout << media::test::usage_msg << media::test::help_msg;
+    return 0;
+  }
+
+  testing::InitGoogleTest(&argc, argv);
 
   // Using shared memory requires mojo to be initialized (crbug.com/849207).
   mojo::core::Init();
@@ -314,12 +278,44 @@
   settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
   LOG_ASSERT(logging::InitLogging(settings));
 
-  // Set up our test environment
-  const media::test::Video* video =
-      &media::test::kDefaultTestVideoCollection[0];
-  media::test::g_env = static_cast<media::test::VideoDecoderTestEnvironment*>(
+  // Set the default test data path.
+  media::test::Video::SetTestDataPath(media::GetTestDataPath());
+
+  // Check if a video was specified on the command line.
+  std::unique_ptr<media::test::Video> video;
+  base::CommandLine::StringVector args = cmd_line->GetArgs();
+  if (args.size() >= 1) {
+    video = std::make_unique<media::test::Video>(base::FilePath(args[0]));
+    if (!video->Load()) {
+      LOG(ERROR) << "Failed to load " << args[0];
+      return 0;
+    }
+  }
+
+  // Set up our test environment.
+  media::test::g_env = static_cast<media::test::VideoPlayerTestEnvironment*>(
       testing::AddGlobalTestEnvironment(
-          new media::test::VideoDecoderTestEnvironment(video)));
+          new media::test::VideoPlayerTestEnvironment(
+              video ? video.get()
+                    : &media::test::kDefaultTestVideoCollection[0])));
+
+  // Parse command line arguments.
+  base::CommandLine::SwitchMap switches = cmd_line->GetSwitches();
+  for (base::CommandLine::SwitchMap::const_iterator it = switches.begin();
+       it != switches.end(); ++it) {
+    if (it->first == "disable_validator") {
+      media::test::g_env->enable_validator_ = false;
+    } else if (it->first == "output_frames") {
+      media::test::g_env->output_frames_ = true;
+    } else if (it->first.find("gtest_") == 0 || it->first == "v" ||
+               it->first == "vmodule") {
+      // Ignore
+    } else {
+      std::cout << "unknown option: --" << it->first << "\n"
+                << media::test::usage_msg;
+      return 0;
+    }
+  }
 
   return RUN_ALL_TESTS();
 }
diff --git a/media/gpu/video_decode_accelerator_unittest.cc b/media/gpu/video_decode_accelerator_unittest.cc
index e7dc801..0702f890 100644
--- a/media/gpu/video_decode_accelerator_unittest.cc
+++ b/media/gpu/video_decode_accelerator_unittest.cc
@@ -607,7 +607,7 @@
     ASSERT_NE(video_frame.get(), nullptr);
     video_frame_validator_->ProcessVideoFrame(std::move(video_frame),
                                               frame_index_);
-    video_frame_validator_->WaitUntilValidated();
+    video_frame_validator_->WaitUntilDone();
     frame_index_++;
   }
   rendering_helper_->ConsumeVideoFrame(config_.window_id,
diff --git a/media/midi/java/src/org/chromium/midi/MidiInputPortAndroid.java b/media/midi/java/src/org/chromium/midi/MidiInputPortAndroid.java
index 2ca80b63..f866c51e 100644
--- a/media/midi/java/src/org/chromium/midi/MidiInputPortAndroid.java
+++ b/media/midi/java/src/org/chromium/midi/MidiInputPortAndroid.java
@@ -69,7 +69,12 @@
         mPort.connect(new MidiReceiver() {
             @Override
             public void onSend(byte[] bs, int offset, int count, long timestamp) {
-                nativeOnData(mNativeReceiverPointer, bs, offset, count, timestamp);
+                synchronized (MidiInputPortAndroid.this) {
+                    if (mPort == null) {
+                        return;
+                    }
+                    nativeOnData(mNativeReceiverPointer, bs, offset, count, timestamp);
+                }
             }
         });
         return true;
@@ -79,7 +84,7 @@
      * Closes the port.
      */
     @CalledByNative
-    void close() {
+    synchronized void close() {
         if (mPort == null) {
             return;
         }
diff --git a/media/midi/java/src/org/chromium/midi/MidiManagerAndroid.java b/media/midi/java/src/org/chromium/midi/MidiManagerAndroid.java
index bd073e0..e0a4cbbd 100644
--- a/media/midi/java/src/org/chromium/midi/MidiManagerAndroid.java
+++ b/media/midi/java/src/org/chromium/midi/MidiManagerAndroid.java
@@ -55,6 +55,14 @@
     private final long mNativeManagerPointer;
 
     /**
+     * True is this object is stopped.
+     * This is needed because MidiManagerAndroid functions are called from the IO thread but
+     * callbacks are called on the UI thread (because the IO thread doesn't have a Looper). We need
+     * to protect each native function call with a synchronized block that also checks this flag.
+     */
+    private boolean mStopped;
+
+    /**
      * Checks if Android MIDI is supported on the device.
      */
     @CalledByNative
@@ -94,7 +102,12 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    nativeOnInitializationFailed(mNativeManagerPointer);
+                    synchronized (MidiManagerAndroid.this) {
+                        if (mStopped) {
+                            return;
+                        }
+                        nativeOnInitializationFailed(mNativeManagerPointer);
+                    }
                 }
             });
             return;
@@ -119,15 +132,28 @@
         mHandler.post(new Runnable() {
             @Override
             public void run() {
-                if (mPendingDevices.isEmpty() && !mIsInitialized) {
-                    nativeOnInitialized(
-                            mNativeManagerPointer, mDevices.toArray(new MidiDeviceAndroid[0]));
-                    mIsInitialized = true;
+                synchronized (MidiManagerAndroid.this) {
+                    if (mStopped) {
+                        return;
+                    }
+                    if (mPendingDevices.isEmpty() && !mIsInitialized) {
+                        nativeOnInitialized(
+                                mNativeManagerPointer, mDevices.toArray(new MidiDeviceAndroid[0]));
+                        mIsInitialized = true;
+                    }
                 }
             }
         });
     }
 
+    /**
+     * Marks this object as stopped.
+     */
+    @CalledByNative
+    synchronized void stop() {
+        mStopped = true;
+    }
+
     private void openDevice(final MidiDeviceInfo info) {
         mManager.openDevice(info, new MidiManager.OnDeviceOpenedListener() {
             @Override
@@ -152,7 +178,10 @@
      * Called when a midi device is detached.
      * @param info the detached device information.
      */
-    private void onDeviceRemoved(MidiDeviceInfo info) {
+    private synchronized void onDeviceRemoved(MidiDeviceInfo info) {
+        if (mStopped) {
+            return;
+        }
         for (MidiDeviceAndroid device : mDevices) {
             if (device.isOpen() && device.getInfo().getId() == info.getId()) {
                 device.close();
@@ -161,7 +190,10 @@
         }
     }
 
-    private void onDeviceOpened(MidiDevice device, MidiDeviceInfo info) {
+    private synchronized void onDeviceOpened(MidiDevice device, MidiDeviceInfo info) {
+        if (mStopped) {
+            return;
+        }
         mPendingDevices.remove(info);
         if (device != null) {
             MidiDeviceAndroid xdevice = new MidiDeviceAndroid(device);
diff --git a/media/midi/midi_manager_android.cc b/media/midi/midi_manager_android.cc
index af0931b..a7ddeca 100644
--- a/media/midi/midi_manager_android.cc
+++ b/media/midi/midi_manager_android.cc
@@ -51,6 +51,8 @@
     : MidiManager(service) {}
 
 MidiManagerAndroid::~MidiManagerAndroid() {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  Java_MidiManagerAndroid_stop(env, raw_manager_);
   bool result = service()->task_service()->UnbindInstance();
   CHECK(result);
 }
diff --git a/mojo/public/cpp/test_support/test_utils.h b/mojo/public/cpp/test_support/test_utils.h
index 54651c5d..790df11 100644
--- a/mojo/public/cpp/test_support/test_utils.h
+++ b/mojo/public/cpp/test_support/test_utils.h
@@ -64,6 +64,9 @@
   // Waits for the bad message and returns the error string.
   std::string WaitForBadMessage();
 
+  // Returns true iff a bad message was already received.
+  bool got_bad_message() const { return got_bad_message_; }
+
  private:
   void OnReportBadMessage(const std::string& message);
 
diff --git a/mojo/public/tools/bindings/BUILD.gn b/mojo/public/tools/bindings/BUILD.gn
index 357f083..f59f1104 100644
--- a/mojo/public/tools/bindings/BUILD.gn
+++ b/mojo/public/tools/bindings/BUILD.gn
@@ -17,6 +17,8 @@
     "$mojom_generator_root/generators/cpp_templates/interface_request_validator_declaration.tmpl",
     "$mojom_generator_root/generators/cpp_templates/interface_response_validator_declaration.tmpl",
     "$mojom_generator_root/generators/cpp_templates/interface_stub_declaration.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/module-forward.h.tmpl",
+    "$mojom_generator_root/generators/cpp_templates/module-import-headers.h.tmpl",
     "$mojom_generator_root/generators/cpp_templates/module-params-data.h.tmpl",
     "$mojom_generator_root/generators/cpp_templates/module-shared-internal.h.tmpl",
     "$mojom_generator_root/generators/cpp_templates/module-shared-message-ids.h.tmpl",
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
index f783cab..b7683be 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/enum_macros.tmpl
@@ -2,8 +2,14 @@
   Macro for enum definition, and the declaration of associated functions.
 ---#}
 
+{%- macro enum_forward(enum) %}
+{%-   set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %}
+enum class {{enum_name}} : int32_t;
+{%- endmacro %}
+
 {%- macro enum_decl(enum, export_attribute) %}
 {%-   set enum_name = enum|get_name_for_kind(flatten_nested_kind=True) %}
+
 enum class {{enum_name}} : int32_t {
 {%-   for field in enum.fields %}
 {%-     if field.value %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
new file mode 100644
index 0000000..c5b8eec3
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-forward.h.tmpl
@@ -0,0 +1,160 @@
+// 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.
+
+{%- if variant -%}
+{%-   set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%-   set variant_path = module.path -%}
+{%- endif -%}
+
+{%- set header_guard = "%s_FORWARD_H_"|format(
+        variant_path|upper|replace("/","_")|replace(".","_")|
+            replace("-", "_")) %}
+
+{%- macro namespace_begin() %}
+{%-   for namespace in namespaces_as_array %}
+namespace {{namespace}} {
+{%-   endfor %}
+{%-   if variant %}
+namespace {{variant}} {
+{%-   endif %}
+{%- endmacro %}
+
+{%- macro namespace_end() %}
+{%-   if variant %}
+}  // namespace {{variant}}
+{%-   endif %}
+{%-   for namespace in namespaces_as_array|reverse %}
+}  // namespace {{namespace}}
+{%-   endfor %}
+{%- endmacro %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+#include "mojo/public/cpp/bindings/struct_ptr.h"
+
+{% if not disallow_interfaces -%}
+#include "mojo/public/cpp/bindings/associated_interface_ptr.h"
+#include "mojo/public/cpp/bindings/associated_interface_ptr_info.h"
+#include "mojo/public/cpp/bindings/associated_interface_request.h"
+#include "mojo/public/cpp/bindings/interface_ptr.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/lib/control_message_handler.h"
+#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h"
+#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
+{%-   if for_blink %}
+#include "third_party/blink/renderer/platform/mojo/revocable_interface_ptr.h"
+{%-   endif %}
+{%- endif %}
+
+{% if not disallow_native_types %}
+#include "mojo/public/cpp/bindings/lib/native_enum_serialization.h"
+#include "mojo/public/cpp/bindings/lib/native_struct_serialization.h"
+{%- endif %}
+
+{%- if export_header %}
+#include "{{export_header}}"
+{%- endif %}
+
+{{namespace_begin()}}
+
+{#- These are non-variant header only. #}
+{%- if not variant %}
+
+{#--- Struct Forward Declarations -#}
+{%- for struct in structs %}
+{%-   if struct|is_native_only_kind %}
+using {{struct.name}}DataView = mojo::native::NativeStructDataView;
+{%-   else %}
+class {{struct.name}}DataView;
+{%-   endif %}
+{%  endfor %}
+
+{#--- Union Forward Declarations -#}
+{%- for union in unions %}
+class {{union.name}}DataView;
+{%- endfor %}
+
+{#--- Enums #}
+{%- from "enum_macros.tmpl" import enum_forward%}
+{%- for enum in all_enums %}
+{%-   if enum|is_native_only_kind %}
+using {{enum|get_name_for_kind(flatten_nested_kind=True)}} = mojo::NativeEnum;
+{%-   else %}
+{{enum_forward(enum)}}
+{%-   endif %}
+{%- endfor %}
+
+{%- endif %}
+
+{#--- Enums #}
+{%- if variant %}
+{%-   for enum in enums %}
+using {{enum.name}} = {{enum.name}};  // Alias for definition in the parent namespace.
+{%-   endfor %}
+{%- endif %}
+
+{#--- Constants #}
+{%- for constant in module.constants %}
+{{constant|format_constant_declaration}};
+{%- endfor %}
+
+{#--- Struct Forward Declarations -#}
+{%  for struct in structs %}
+{%-   if struct|is_native_only_kind %}
+using {{struct.name}} = mojo::native::NativeStruct;
+using {{struct.name}}Ptr = mojo::native::NativeStructPtr;
+{%-   else %}
+class {{struct.name}};
+{%-     if struct|should_inline %}
+using {{struct.name}}Ptr = mojo::InlinedStructPtr<{{struct.name}}>;
+{%-     else %}
+using {{struct.name}}Ptr = mojo::StructPtr<{{struct.name}}>;
+{%-     endif %}
+{%-   endif %}
+{%  endfor %}
+
+{#--- Union Forward Declarations -#}
+{%  for union in unions %}
+class {{union.name}};
+{%    if union|should_inline_union %}
+typedef mojo::InlinedStructPtr<{{union.name}}> {{union.name}}Ptr;
+{%    else %}
+typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr;
+{%    endif %}
+{%- endfor %}
+
+
+{#--- Interface Forward Declarations -#}
+{%  for interface in interfaces %}
+class {{interface.name}};
+using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>;
+{%-   if for_blink %}
+using Revocable{{interface.name}}Ptr = ::blink::RevocableInterfacePtr<{{interface.name}}>;
+{%-   endif %}
+using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>;
+using ThreadSafe{{interface.name}}Ptr =
+    mojo::ThreadSafeInterfacePtr<{{interface.name}}>;
+using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>;
+using {{interface.name}}AssociatedPtr =
+    mojo::AssociatedInterfacePtr<{{interface.name}}>;
+using ThreadSafe{{interface.name}}AssociatedPtr =
+    mojo::ThreadSafeAssociatedInterfacePtr<{{interface.name}}>;
+using {{interface.name}}AssociatedPtrInfo =
+    mojo::AssociatedInterfacePtrInfo<{{interface.name}}>;
+using {{interface.name}}AssociatedRequest =
+    mojo::AssociatedInterfaceRequest<{{interface.name}}>;
+{%  endfor %}
+
+
+{{namespace_end()}}
+
+{#- TODO(tikuta): Use forward declaration of native enum/struct here. #}
+
+{%- for header in extra_public_headers %}
+#include "{{header}}"
+{%- endfor %}
+
+#endif  // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-import-headers.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-import-headers.h.tmpl
new file mode 100644
index 0000000..928ad1a
--- /dev/null
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-import-headers.h.tmpl
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+{%- if variant -%}
+{%-   set variant_path = "%s-%s"|format(module.path, variant) -%}
+{%- else -%}
+{%-   set variant_path = module.path -%}
+{%- endif -%}
+
+{%- set header_guard = "%s_IMPORT_HEADERS_H_"|format(
+        variant_path|upper|replace("/","_")|replace(".","_")|
+            replace("-", "_")) %}
+
+#ifndef {{header_guard}}
+#define {{header_guard}}
+
+{%- for import in imports %}
+{%-   if variant %}
+#include "{{"%s-%s.h"|format(import.path, variant)}}"
+#include "{{"%s-%s-import-headers.h"|format(import.path, variant)}}"
+{%-   else %}
+#include "{{import.path}}.h"
+#include "{{import.path}}-import-headers.h"
+{%-   endif %}
+{%- endfor %}
+
+#endif  // {{header_guard}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module-test-utils.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module-test-utils.cc.tmpl
index a7ea114a..93e620f 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module-test-utils.cc.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module-test-utils.cc.tmpl
@@ -52,6 +52,14 @@
 #include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
 {%- endif %}
 
+{%- for import in imports %}
+{%-   if variant %}
+#include "{{"%s-%s.h"|format(import.path, variant)}}"
+{%-   else %}
+#include "{{import.path}}.h"
+{%-   endif %}
+{%- endfor %}
+
 {# This is include guard for jumbo build. #}
 #ifndef {{header_guard_for_jumbo}}
 #define {{header_guard_for_jumbo}}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
index 31a6c339..f6f1388 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.cc.tmpl
@@ -41,6 +41,8 @@
 #include "{{module.path}}-params-data.h"
 #include "{{module.path}}-shared-message-ids.h"
 
+#include "{{variant_path}}-import-headers.h"
+
 {%- if for_blink %}
 #include "mojo/public/cpp/bindings/lib/wtf_serialization.h"
 {%- endif %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
index 1537b1c..b4f2a45 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl
@@ -54,13 +54,35 @@
 #include "mojo/public/cpp/bindings/struct_traits.h"
 #include "mojo/public/cpp/bindings/union_traits.h"
 #include "{{module.path}}-shared.h"
+#include "{{variant_path}}-forward.h"
+
+{#- Imported struct and union types must be fully defined if used in this
+    mojom. #}
+{%- set import_definition_header = namespace(need=false) %}
+{%- for struct in structs %}
+{%-   for field in struct.fields %}
+{%-     if field.kind|is_object_kind or field.kind|is_union_kind %}
+{%-       set import_definition_header.need = true %}
+{%-     endif %}
+{%-   endfor %}
+{%- endfor %}
+
 {%- for import in imports %}
-{%-   if variant %}
+{%-   if import_definition_header.need %}
+{%-     if variant %}
 #include "{{"%s-%s.h"|format(import.path, variant)}}"
-{%-   else %}
+{%-     else %}
 #include "{{import.path}}.h"
+{%-     endif %}
+{%-   else %}
+{%-     if variant %}
+#include "{{"%s-%s-forward.h"|format(import.path, variant)}}"
+{%-     else %}
+#include "{{import.path}}-forward.h"
+{%-     endif %}
 {%-   endif %}
 {%- endfor %}
+
 {%- if not for_blink %}
 #include <string>
 #include <vector>
@@ -113,64 +135,6 @@
 
 {{namespace_begin()}}
 
-{#--- Enums #}
-{%- if variant %}
-{%-   for enum in enums %}
-using {{enum.name}} = {{enum.name}};  // Alias for definition in the parent namespace.
-{%-   endfor %}
-{%- endif %}
-
-{#--- Constants #}
-{%- for constant in module.constants %}
-{{constant|format_constant_declaration}};
-{%- endfor %}
-
-{#--- Interface Forward Declarations -#}
-{%  for interface in interfaces %}
-class {{interface.name}};
-using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>;
-{%-   if for_blink %}
-using Revocable{{interface.name}}Ptr = ::blink::RevocableInterfacePtr<{{interface.name}}>;
-{%-   endif %}
-using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>;
-using ThreadSafe{{interface.name}}Ptr =
-    mojo::ThreadSafeInterfacePtr<{{interface.name}}>;
-using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>;
-using {{interface.name}}AssociatedPtr =
-    mojo::AssociatedInterfacePtr<{{interface.name}}>;
-using ThreadSafe{{interface.name}}AssociatedPtr =
-    mojo::ThreadSafeAssociatedInterfacePtr<{{interface.name}}>;
-using {{interface.name}}AssociatedPtrInfo =
-    mojo::AssociatedInterfacePtrInfo<{{interface.name}}>;
-using {{interface.name}}AssociatedRequest =
-    mojo::AssociatedInterfaceRequest<{{interface.name}}>;
-{%  endfor %}
-
-{#--- Struct Forward Declarations -#}
-{%  for struct in structs %}
-{%-   if struct|is_native_only_kind %}
-using {{struct.name}} = mojo::native::NativeStruct;
-using {{struct.name}}Ptr = mojo::native::NativeStructPtr;
-{%-   else %}
-class {{struct.name}};
-{%-     if struct|should_inline %}
-using {{struct.name}}Ptr = mojo::InlinedStructPtr<{{struct.name}}>;
-{%-     else %}
-using {{struct.name}}Ptr = mojo::StructPtr<{{struct.name}}>;
-{%-     endif %}
-{%-   endif %}
-{%  endfor %}
-
-{#--- Union Forward Declarations -#}
-{%  for union in unions %}
-class {{union.name}};
-{%    if union|should_inline_union %}
-typedef mojo::InlinedStructPtr<{{union.name}}> {{union.name}}Ptr;
-{%    else %}
-typedef mojo::StructPtr<{{union.name}}> {{union.name}}Ptr;
-{%    endif %}
-{%- endfor %}
-
 {#--- Interfaces -#}
 {%  for interface in interfaces %}
 {%    include "interface_declaration.tmpl" %}
diff --git a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
index d825b489..ec33438 100644
--- a/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
+++ b/mojo/public/tools/bindings/generators/cpp_templates/wrapper_union_class_template_definition.tmpl
@@ -14,7 +14,7 @@
 {%-   endif %}
       break;
 {%- endfor %}
-  };
+  }
   return rv;
 }
 
diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
index 443f5e2..0d6dc76 100644
--- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
+++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py
@@ -376,10 +376,18 @@
   def _GenerateModuleHeader(self):
     return self._GetJinjaExports()
 
+  @UseJinja("module-forward.h.tmpl")
+  def _GenerateModuleForwardHeader(self):
+    return self._GetJinjaExports()
+
   @UseJinja("module.cc.tmpl")
   def _GenerateModuleSource(self):
     return self._GetJinjaExports()
 
+  @UseJinja("module-import-headers.h.tmpl")
+  def _GenerateModuleImportHeadersHeader(self):
+    return self._GetJinjaExports()
+
   @UseJinja("module-shared.h.tmpl")
   def _GenerateModuleSharedHeader(self):
     return self._GetJinjaExports()
@@ -428,8 +436,12 @@
       suffix = "-%s" % self.variant if self.variant else ""
       self.Write(self._GenerateModuleHeader(),
                  "%s%s.h" % (self.module.path, suffix))
+      self.Write(self._GenerateModuleForwardHeader(),
+                 "%s%s-forward.h" % (self.module.path, suffix))
       self.Write(self._GenerateModuleSource(),
                  "%s%s.cc" % (self.module.path, suffix))
+      self.Write(self._GenerateModuleImportHeadersHeader(),
+                 "%s%s-import-headers.h" % (self.module.path, suffix))
       self.Write(self._GenerateModuleTestUtilsHeader(),
                  "%s%s-test-utils.h" % (self.module.path, suffix))
       self.Write(self._GenerateModuleTestUtilsSource(),
diff --git a/mojo/public/tools/bindings/mojom.gni b/mojo/public/tools/bindings/mojom.gni
index bbb8c6b..726cd66 100644
--- a/mojo/public/tools/bindings/mojom.gni
+++ b/mojo/public/tools/bindings/mojom.gni
@@ -754,10 +754,12 @@
         variant_dash_suffix = "-${variant}"
       }
       generator_cpp_outputs += [
-        "{{source_gen_dir}}/{{source_file_part}}${variant_dash_suffix}.cc",
-        "{{source_gen_dir}}/{{source_file_part}}${variant_dash_suffix}.h",
+        "{{source_gen_dir}}/{{source_file_part}}${variant_dash_suffix}-forward.h",
+        "{{source_gen_dir}}/{{source_file_part}}${variant_dash_suffix}-import-headers.h",
         "{{source_gen_dir}}/{{source_file_part}}${variant_dash_suffix}-test-utils.cc",
         "{{source_gen_dir}}/{{source_file_part}}${variant_dash_suffix}-test-utils.h",
+        "{{source_gen_dir}}/{{source_file_part}}${variant_dash_suffix}.cc",
+        "{{source_gen_dir}}/{{source_file_part}}${variant_dash_suffix}.h",
       ]
       enabled_sources = []
       if (defined(bindings_configuration.blacklist)) {
@@ -830,10 +832,12 @@
         foreach(source, invoker.sources) {
           filelist += [ rebase_path("$source", root_build_dir) ]
           outputs += [
-            "$target_gen_dir/${source}${variant_dash_suffix}.cc",
-            "$target_gen_dir/${source}${variant_dash_suffix}.h",
+            "$target_gen_dir/${source}${variant_dash_suffix}-forward.h",
+            "$target_gen_dir/${source}${variant_dash_suffix}-import-headers.h",
             "$target_gen_dir/${source}${variant_dash_suffix}-test-utils.cc",
             "$target_gen_dir/${source}${variant_dash_suffix}-test-utils.h",
+            "$target_gen_dir/${source}${variant_dash_suffix}.cc",
+            "$target_gen_dir/${source}${variant_dash_suffix}.h",
           ]
         }
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index aca9ff9f..672f603 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -72,6 +72,7 @@
     "ENABLE_WEBSOCKETS=$enable_websockets",
     "INCLUDE_TRANSPORT_SECURITY_STATE_PRELOAD_LIST=$include_transport_security_state_preload_list",
     "USE_KERBEROS=$use_kerberos",
+    "TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED=$trial_comparison_cert_verifier_supported",
   ]
 }
 
@@ -1232,7 +1233,6 @@
       "third_party/quic/core/crypto/crypto_utils.h",
       "third_party/quic/core/crypto/curve25519_key_exchange.cc",
       "third_party/quic/core/crypto/curve25519_key_exchange.h",
-      "third_party/quic/core/crypto/ephemeral_key_source.h",
       "third_party/quic/core/crypto/key_exchange.h",
       "third_party/quic/core/crypto/null_decrypter.cc",
       "third_party/quic/core/crypto/null_decrypter.h",
@@ -1488,7 +1488,6 @@
       "third_party/quic/platform/api/quic_file_utils.h",
       "third_party/quic/platform/api/quic_flag_utils.h",
       "third_party/quic/platform/api/quic_flags.h",
-      "third_party/quic/platform/api/quic_goog_cc_sender.h",
       "third_party/quic/platform/api/quic_hostname_utils.cc",
       "third_party/quic/platform/api/quic_hostname_utils.h",
       "third_party/quic/platform/api/quic_interval.h",
@@ -1533,7 +1532,6 @@
       "third_party/quic/platform/impl/quic_flag_utils_impl.h",
       "third_party/quic/platform/impl/quic_flags_impl.cc",
       "third_party/quic/platform/impl/quic_flags_impl.h",
-      "third_party/quic/platform/impl/quic_goog_cc_sender_impl.h",
       "third_party/quic/platform/impl/quic_hostname_utils_impl.cc",
       "third_party/quic/platform/impl/quic_hostname_utils_impl.h",
       "third_party/quic/platform/impl/quic_interval_impl.h",
@@ -1541,7 +1539,6 @@
       "third_party/quic/platform/impl/quic_ip_address_impl.cc",
       "third_party/quic/platform/impl/quic_ip_address_impl.h",
       "third_party/quic/platform/impl/quic_logging_impl.h",
-      "third_party/quic/platform/impl/quic_lru_cache_impl.h",
       "third_party/quic/platform/impl/quic_map_util_impl.h",
       "third_party/quic/platform/impl/quic_mem_slice_impl.cc",
       "third_party/quic/platform/impl/quic_mem_slice_impl.h",
@@ -2021,6 +2018,13 @@
         "cert/test_root_certs_fuchsia.cc",
       ]
     }
+
+    if (trial_comparison_cert_verifier_supported) {
+      sources += [
+        "cert/trial_comparison_cert_verifier.cc",
+        "cert/trial_comparison_cert_verifier.h",
+      ]
+    }
   }
 
   if (enable_unix_sockets) {
@@ -2511,10 +2515,12 @@
     "data/ssl/certificates/googlenew.chain.pem",
     "data/ssl/certificates/intermediate_ca_cert.pem",
     "data/ssl/certificates/invalid_key_usage_cert.der",
+    "data/ssl/certificates/key_usage_p256.key",
     "data/ssl/certificates/key_usage_p256_both.pem",
     "data/ssl/certificates/key_usage_p256_digitalsignature.pem",
     "data/ssl/certificates/key_usage_p256_keyagreement.pem",
     "data/ssl/certificates/key_usage_p256_no_extension.pem",
+    "data/ssl/certificates/key_usage_rsa.key",
     "data/ssl/certificates/key_usage_rsa_both.pem",
     "data/ssl/certificates/key_usage_rsa_digitalsignature.pem",
     "data/ssl/certificates/key_usage_rsa_keyencipherment.pem",
@@ -2522,6 +2528,7 @@
     "data/ssl/certificates/large_key.pem",
     "data/ssl/certificates/localhost_cert.pem",
     "data/ssl/certificates/login.trustwave.com.pem",
+    "data/ssl/certificates/may_2018.pem",
     "data/ssl/certificates/mit.davidben.der",
     "data/ssl/certificates/multi-root-A-by-B.pem",
     "data/ssl/certificates/multi-root-B-by-C.pem",
@@ -3747,6 +3754,7 @@
     "data/parse_certificate_unittest/v1_explicit_version.pem",
     "data/parse_certificate_unittest/v3_certificate_template.pk8",
     "data/test.html",
+    "data/trial_comparison_cert_verifier_unittest/target-multiple-policies/chain.pem",
     "data/url_request_unittest/308-without-location-header",
     "data/url_request_unittest/308-without-location-header.mock-http-headers",
     "data/url_request_unittest/BullRunSpeech.txt",
@@ -5763,6 +5771,10 @@
     ]
   }
 
+  if (trial_comparison_cert_verifier_supported) {
+    sources += [ "cert/trial_comparison_cert_verifier_unittest.cc" ]
+  }
+
   # Include transport_security_state_generator tests.
   if (host_toolchain == current_toolchain) {
     deps += [
diff --git a/net/cert/nss_cert_database.cc b/net/cert/nss_cert_database.cc
index db1d843..d2b5a268 100644
--- a/net/cert/nss_cert_database.cc
+++ b/net/cert/nss_cert_database.cc
@@ -110,6 +110,15 @@
 crypto::ScopedPK11Slot NSSCertDatabase::GetSystemSlot() const {
   return crypto::ScopedPK11Slot();
 }
+
+bool NSSCertDatabase::IsCertificateOnSystemSlot(CERTCertificate* cert) const {
+  crypto::ScopedPK11Slot system_slot = GetSystemSlot();
+  if (!system_slot)
+    return false;
+
+  return PK11_FindCertInSlot(system_slot.get(), cert, nullptr) !=
+         CK_INVALID_HANDLE;
+}
 #endif
 
 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
diff --git a/net/cert/nss_cert_database.h b/net/cert/nss_cert_database.h
index 653bd413..cee2f01 100644
--- a/net/cert/nss_cert_database.h
+++ b/net/cert/nss_cert_database.h
@@ -131,6 +131,10 @@
   // before SetSystemSlot is called and get a NULL result.
   // See https://crbug.com/399554 .
   virtual crypto::ScopedPK11Slot GetSystemSlot() const;
+
+  // Check whether the certificate is stored on the system slot (i.e. is a
+  // device certificate).
+  bool IsCertificateOnSystemSlot(CERTCertificate* cert) const;
 #endif
 
   // Get the default slot for public key data.
diff --git a/net/cert/trial_comparison_cert_verifier.cc b/net/cert/trial_comparison_cert_verifier.cc
new file mode 100644
index 0000000..eb82799d
--- /dev/null
+++ b/net/cert/trial_comparison_cert_verifier.cc
@@ -0,0 +1,518 @@
+// 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 "net/cert/trial_comparison_cert_verifier.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/metrics/histogram_macros.h"
+#include "base/task/post_task.h"
+#include "base/values.h"
+#include "build/build_config.h"
+#include "crypto/sha2.h"
+#include "net/base/net_errors.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/ev_root_ca_metadata.h"
+#include "net/cert/internal/cert_errors.h"
+#include "net/cert/internal/parsed_certificate.h"
+#include "net/cert/multi_threaded_cert_verifier.h"
+#include "net/cert/x509_util.h"
+#include "net/log/net_log.h"
+#include "net/log/net_log_event_type.h"
+#include "net/log/net_log_source_type.h"
+#include "net/log/net_log_with_source.h"
+
+namespace net {
+
+namespace {
+
+std::unique_ptr<base::Value> TrialVerificationJobResultCallback(
+    bool trial_success,
+    NetLogCaptureMode capture_mode) {
+  std::unique_ptr<base::DictionaryValue> results(new base::DictionaryValue());
+  results->SetKey("trial_success", base::Value(trial_success));
+  return std::move(results);
+}
+
+bool CertVerifyResultEqual(const CertVerifyResult& a,
+                           const CertVerifyResult& b) {
+  return std::tie(a.cert_status, a.is_issued_by_known_root) ==
+             std::tie(b.cert_status, b.is_issued_by_known_root) &&
+         (!!a.verified_cert == !!b.verified_cert) &&
+         (!a.verified_cert ||
+          a.verified_cert->EqualsIncludingChain(b.verified_cert.get()));
+}
+
+scoped_refptr<ParsedCertificate> ParsedCertificateFromBuffer(
+    CRYPTO_BUFFER* cert_handle,
+    CertErrors* errors) {
+  return ParsedCertificate::Create(bssl::UpRef(cert_handle),
+                                   x509_util::DefaultParseCertificateOptions(),
+                                   errors);
+}
+
+ParsedCertificateList ParsedCertificateListFromX509Certificate(
+    const X509Certificate* cert) {
+  CertErrors parsing_errors;
+
+  ParsedCertificateList certs;
+  scoped_refptr<ParsedCertificate> target =
+      ParsedCertificateFromBuffer(cert->cert_buffer(), &parsing_errors);
+  if (!target)
+    return {};
+  certs.push_back(target);
+
+  for (const auto& buf : cert->intermediate_buffers()) {
+    scoped_refptr<ParsedCertificate> intermediate =
+        ParsedCertificateFromBuffer(buf.get(), &parsing_errors);
+    if (!intermediate)
+      return {};
+    certs.push_back(intermediate);
+  }
+
+  return certs;
+}
+
+// Tests whether cert has multiple EV policies, and at least one matches the
+// root. This is not a complete test of EV, but just enough to give a possible
+// explanation as to why the platform verifier did not validate as EV while
+// builtin did. (Since only the builtin verifier correctly handles multiple
+// candidate EV policies.)
+bool CertHasMultipleEVPoliciesAndOneMatchesRoot(const X509Certificate* cert) {
+  if (cert->intermediate_buffers().empty())
+    return false;
+
+  ParsedCertificateList certs = ParsedCertificateListFromX509Certificate(cert);
+  if (certs.empty())
+    return false;
+
+  ParsedCertificate* leaf = certs.front().get();
+  ParsedCertificate* root = certs.back().get();
+
+  if (!leaf->has_policy_oids())
+    return false;
+
+  const EVRootCAMetadata* ev_metadata = EVRootCAMetadata::GetInstance();
+  std::set<der::Input> candidate_oids;
+  for (const der::Input& oid : leaf->policy_oids()) {
+    if (ev_metadata->IsEVPolicyOIDGivenBytes(oid))
+      candidate_oids.insert(oid);
+  }
+
+  if (candidate_oids.size() <= 1)
+    return false;
+
+  SHA256HashValue root_fingerprint;
+  crypto::SHA256HashString(root->der_cert().AsStringPiece(),
+                           root_fingerprint.data,
+                           sizeof(root_fingerprint.data));
+
+  for (const der::Input& oid : candidate_oids) {
+    if (ev_metadata->HasEVPolicyOIDGivenBytes(root_fingerprint, oid))
+      return true;
+  }
+
+  return false;
+}
+
+}  // namespace
+
+class TrialComparisonCertVerifier::TrialVerificationJob {
+ public:
+  TrialVerificationJob(const CertVerifier::Config& config,
+                       const CertVerifier::RequestParams& params,
+                       const NetLogWithSource& source_net_log,
+                       TrialComparisonCertVerifier* cert_verifier,
+                       int primary_error,
+                       const CertVerifyResult& primary_result)
+      : config_(config),
+        config_changed_(false),
+        params_(params),
+        net_log_(
+            NetLogWithSource::Make(source_net_log.net_log(),
+                                   NetLogSourceType::TRIAL_CERT_VERIFIER_JOB)),
+        cert_verifier_(cert_verifier),
+        primary_error_(primary_error),
+        primary_result_(primary_result) {
+    net_log_.BeginEvent(NetLogEventType::TRIAL_CERT_VERIFIER_JOB);
+    source_net_log.AddEvent(
+        NetLogEventType::TRIAL_CERT_VERIFIER_JOB_COMPARISON_STARTED,
+        net_log_.source().ToEventParametersCallback());
+  }
+
+  ~TrialVerificationJob() {
+    if (cert_verifier_) {
+      net_log_.AddEvent(NetLogEventType::CANCELLED);
+      net_log_.EndEvent(NetLogEventType::TRIAL_CERT_VERIFIER_JOB);
+    }
+  }
+
+  void Start() {
+    // Unretained is safe because trial_request_ will cancel the callback on
+    // destruction.
+    int rv = cert_verifier_->trial_verifier()->Verify(
+        params_, &trial_result_,
+        base::BindOnce(&TrialVerificationJob::OnJobCompleted,
+                       base::Unretained(this)),
+        &trial_request_, net_log_);
+    if (rv != ERR_IO_PENDING)
+      OnJobCompleted(rv);
+  }
+
+  void OnConfigChanged() { config_changed_ = true; }
+
+  void Finish(bool is_success, TrialComparisonResult result_code) {
+    TrialComparisonCertVerifier* cert_verifier = cert_verifier_;
+    cert_verifier_ = nullptr;
+
+    UMA_HISTOGRAM_ENUMERATION("Net.CertVerifier_TrialComparisonResult",
+                              result_code);
+
+    net_log_.EndEvent(
+        NetLogEventType::TRIAL_CERT_VERIFIER_JOB,
+        base::BindRepeating(&TrialVerificationJobResultCallback, is_success));
+
+    if (!is_success) {
+      cert_verifier->report_callback_.Run(
+          params_.hostname(), params_.certificate(),
+          config_.enable_rev_checking,
+          config_.require_rev_checking_local_anchors,
+          config_.enable_sha1_local_anchors,
+          config_.disable_symantec_enforcement, primary_result_, trial_result_);
+    }
+
+    // |this| is deleted after RemoveJob returns.
+    cert_verifier->RemoveJob(this);
+  }
+
+  void FinishSuccess(TrialComparisonResult result_code) {
+    Finish(true /* is_success */, result_code);
+  }
+
+  void FinishWithError() {
+    DCHECK(trial_error_ != primary_error_ ||
+           !CertVerifyResultEqual(trial_result_, primary_result_));
+
+    TrialComparisonResult result_code = kInvalid;
+
+    if (primary_error_ == OK && trial_error_ == OK) {
+      result_code = kBothValidDifferentDetails;
+    } else if (primary_error_ == OK) {
+      result_code = kPrimaryValidSecondaryError;
+    } else if (trial_error_ == OK) {
+      result_code = kPrimaryErrorSecondaryValid;
+    } else {
+      result_code = kBothErrorDifferentDetails;
+    }
+    Finish(false /* is_success */, result_code);
+  }
+
+  void OnJobCompleted(int trial_result_error) {
+    DCHECK(primary_result_.verified_cert);
+    DCHECK(trial_result_.verified_cert);
+
+    trial_error_ = trial_result_error;
+
+    bool errors_equal = trial_result_error == primary_error_;
+    bool details_equal = CertVerifyResultEqual(trial_result_, primary_result_);
+    bool trial_success = errors_equal && details_equal;
+
+    if (trial_success) {
+      FinishSuccess(kEqual);
+      return;
+    }
+
+#if defined(OS_MACOSX)
+    if (primary_error_ == ERR_CERT_REVOKED && !config_.enable_rev_checking &&
+        !(primary_result_.cert_status & CERT_STATUS_REV_CHECKING_ENABLED) &&
+        !(trial_result_.cert_status &
+          (CERT_STATUS_REVOKED | CERT_STATUS_REV_CHECKING_ENABLED))) {
+      if (config_changed_) {
+        FinishSuccess(kIgnoredConfigurationChanged);
+        return;
+      }
+      // CertVerifyProcMac does some revocation checking even if we didn't want
+      // it. Try verifying with the trial verifier with revocation checking
+      // enabled, see if it then returns REVOKED.
+
+      int rv = cert_verifier_->revocation_trial_verifier()->Verify(
+          params_, &reverification_result_,
+          base::BindOnce(
+              &TrialVerificationJob::OnMacRevcheckingReverificationJobCompleted,
+              base::Unretained(this)),
+          &reverification_request_, net_log_);
+      if (rv != ERR_IO_PENDING)
+        OnMacRevcheckingReverificationJobCompleted(rv);
+      return;
+    }
+#endif
+
+    const bool chains_equal =
+        primary_result_.verified_cert->EqualsIncludingChain(
+            trial_result_.verified_cert.get());
+
+    if (!chains_equal && (trial_error_ == OK || primary_error_ != OK)) {
+      if (config_changed_) {
+        FinishSuccess(kIgnoredConfigurationChanged);
+        return;
+      }
+      // Chains were different, reverify the trial_result_.verified_cert chain
+      // using the platform verifier and compare results again.
+      RequestParams reverification_params(trial_result_.verified_cert,
+                                          params_.hostname(), params_.flags(),
+                                          params_.ocsp_response());
+
+      int rv = cert_verifier_->primary_reverifier()->Verify(
+          reverification_params, &reverification_result_,
+          base::BindOnce(&TrialVerificationJob::
+                             OnPrimaryReverifiyWithSecondaryChainCompleted,
+                         base::Unretained(this)),
+          &reverification_request_, net_log_);
+      if (rv != ERR_IO_PENDING)
+        OnPrimaryReverifiyWithSecondaryChainCompleted(rv);
+      return;
+    }
+
+    TrialComparisonResult ignorable_difference =
+        IsSynchronouslyIgnorableDifference(primary_error_, primary_result_,
+                                           trial_error_, trial_result_);
+    if (ignorable_difference != kInvalid) {
+      FinishSuccess(ignorable_difference);
+      return;
+    }
+
+    FinishWithError();
+  }
+
+  // Check if the differences between the primary and trial verifiers can be
+  // ignored. This only handles differences that can be checked synchronously.
+  // If the difference is ignorable, returns the relevant TrialComparisonResult,
+  // otherwise returns kInvalid.
+  static TrialComparisonResult IsSynchronouslyIgnorableDifference(
+      int primary_error,
+      const CertVerifyResult& primary_result,
+      int trial_error,
+      const CertVerifyResult& trial_result) {
+    DCHECK(primary_result.verified_cert);
+    DCHECK(trial_result.verified_cert);
+
+    if (primary_error == OK &&
+        primary_result.verified_cert->intermediate_buffers().empty()) {
+      // Platform may support trusting a leaf certificate directly. Builtin
+      // verifier does not. See https://crbug.com/814994.
+      return kIgnoredLocallyTrustedLeaf;
+    }
+
+    const bool chains_equal =
+        primary_result.verified_cert->EqualsIncludingChain(
+            trial_result.verified_cert.get());
+
+    if (chains_equal && (trial_result.cert_status & CERT_STATUS_IS_EV) &&
+        !(primary_result.cert_status & CERT_STATUS_IS_EV) &&
+        (primary_error == trial_error)) {
+      // The platform CertVerifyProc impls only check a single potential EV
+      // policy from the leaf.  If the leaf had multiple policies, builtin
+      // verifier may verify it as EV when the platform verifier did not.
+      if (CertHasMultipleEVPoliciesAndOneMatchesRoot(
+              trial_result.verified_cert.get())) {
+        return kIgnoredMultipleEVPoliciesAndOneMatchesRoot;
+      }
+    }
+    return kInvalid;
+  }
+
+#if defined(OS_MACOSX)
+  void OnMacRevcheckingReverificationJobCompleted(int reverification_error) {
+    if (reverification_error == ERR_CERT_REVOKED) {
+      FinishSuccess(kIgnoredMacUndesiredRevocationChecking);
+      return;
+    }
+    FinishWithError();
+  }
+#endif
+
+  void OnPrimaryReverifiyWithSecondaryChainCompleted(int reverification_error) {
+    if (reverification_error == trial_error_ &&
+        CertVerifyResultEqual(reverification_result_, trial_result_)) {
+      // The new result matches the builtin verifier, so this was just
+      // a difference in the platform's path-building ability.
+      // Ignore the difference.
+      FinishSuccess(kIgnoredDifferentPathReVerifiesEquivalent);
+      return;
+    }
+
+    if (IsSynchronouslyIgnorableDifference(reverification_error,
+                                           reverification_result_, trial_error_,
+                                           trial_result_) != kInvalid) {
+      // The new result matches if ignoring differences. Still use the
+      // |kIgnoredDifferentPathReVerifiesEquivalent| code rather than the
+      // result of IsSynchronouslyIgnorableDifference, since it's the higher
+      // level description of what the difference is in this case.
+      FinishSuccess(kIgnoredDifferentPathReVerifiesEquivalent);
+      return;
+    }
+
+    FinishWithError();
+  }
+
+ private:
+  const CertVerifier::Config config_;
+  bool config_changed_;
+  const CertVerifier::RequestParams params_;
+  const NetLogWithSource net_log_;
+  TrialComparisonCertVerifier* cert_verifier_;  // Non-owned.
+
+  // Results from the trial verification.
+  int trial_error_;
+  CertVerifyResult trial_result_;
+  std::unique_ptr<CertVerifier::Request> trial_request_;
+
+  // Saved results of the primary verification.
+  int primary_error_;
+  const CertVerifyResult primary_result_;
+
+  // Results from re-verification attempt.
+  CertVerifyResult reverification_result_;
+  std::unique_ptr<CertVerifier::Request> reverification_request_;
+
+  DISALLOW_COPY_AND_ASSIGN(TrialVerificationJob);
+};
+
+TrialComparisonCertVerifier::TrialComparisonCertVerifier(
+    bool initial_allowed,
+    scoped_refptr<CertVerifyProc> primary_verify_proc,
+    scoped_refptr<CertVerifyProc> trial_verify_proc,
+    ReportCallback report_callback)
+    : allowed_(initial_allowed),
+      report_callback_(report_callback),
+      primary_verifier_(
+          MultiThreadedCertVerifier::CreateForDualVerificationTrial(
+              primary_verify_proc,
+              // Unretained is safe since the callback won't be called after
+              // |primary_verifier_| is destroyed.
+              base::BindRepeating(
+                  &TrialComparisonCertVerifier::OnPrimaryVerifierComplete,
+                  base::Unretained(this)),
+              true /* should_record_histograms */)),
+      primary_reverifier_(
+          std::make_unique<MultiThreadedCertVerifier>(primary_verify_proc)),
+      trial_verifier_(MultiThreadedCertVerifier::CreateForDualVerificationTrial(
+          trial_verify_proc,
+          // Unretained is safe since the callback won't be called after
+          // |trial_verifier_| is destroyed.
+          base::BindRepeating(
+              &TrialComparisonCertVerifier::OnTrialVerifierComplete,
+              base::Unretained(this)),
+          false /* should_record_histograms */)),
+      revocation_trial_verifier_(
+          MultiThreadedCertVerifier::CreateForDualVerificationTrial(
+              trial_verify_proc,
+              // Unretained is safe since the callback won't be called after
+              // |trial_verifier_| is destroyed.
+              base::BindRepeating(
+                  &TrialComparisonCertVerifier::OnTrialVerifierComplete,
+                  base::Unretained(this)),
+              false /* should_record_histograms */)) {
+  CertVerifier::Config config;
+  config.enable_rev_checking = true;
+  revocation_trial_verifier_->SetConfig(config);
+}
+
+TrialComparisonCertVerifier::~TrialComparisonCertVerifier() = default;
+
+int TrialComparisonCertVerifier::Verify(const RequestParams& params,
+                                        CertVerifyResult* verify_result,
+                                        CompletionOnceCallback callback,
+                                        std::unique_ptr<Request>* out_req,
+                                        const NetLogWithSource& net_log) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  return primary_verifier_->Verify(params, verify_result, std::move(callback),
+                                   out_req, net_log);
+}
+
+void TrialComparisonCertVerifier::SetConfig(const Config& config) {
+  config_ = config;
+
+  primary_verifier_->SetConfig(config);
+  primary_reverifier_->SetConfig(config);
+  trial_verifier_->SetConfig(config);
+
+  // Always enable revocation checking for the revocation trial verifier.
+  CertVerifier::Config config_with_revocation = config;
+  config_with_revocation.enable_rev_checking = true;
+  revocation_trial_verifier_->SetConfig(config_with_revocation);
+
+  // Notify all in-process jobs that the underlying configuration has changed.
+  for (auto& job : jobs_) {
+    job->OnConfigChanged();
+  }
+}
+
+void TrialComparisonCertVerifier::OnPrimaryVerifierComplete(
+    const RequestParams& params,
+    const NetLogWithSource& net_log,
+    int primary_error,
+    const CertVerifyResult& primary_result,
+    base::TimeDelta primary_latency,
+    bool is_first_job) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  if (!trial_allowed())
+    return;
+
+  // Only record the TrialPrimary histograms for the same set of requests
+  // that TrialSecondary histograms will be recorded for, in order to get a
+  // direct comparison.
+  UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency_TrialPrimary",
+                             primary_latency,
+                             base::TimeDelta::FromMilliseconds(1),
+                             base::TimeDelta::FromMinutes(10), 100);
+  if (is_first_job) {
+    UMA_HISTOGRAM_CUSTOM_TIMES(
+        "Net.CertVerifier_First_Job_Latency_TrialPrimary", primary_latency,
+        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
+        100);
+  }
+
+  std::unique_ptr<TrialVerificationJob> job =
+      std::make_unique<TrialVerificationJob>(config_, params, net_log, this,
+                                             primary_error, primary_result);
+  TrialVerificationJob* job_ptr = job.get();
+  jobs_.insert(std::move(job));
+  job_ptr->Start();
+}
+
+void TrialComparisonCertVerifier::OnTrialVerifierComplete(
+    const RequestParams& params,
+    const NetLogWithSource& net_log,
+    int trial_error,
+    const CertVerifyResult& trial_result,
+    base::TimeDelta latency,
+    bool is_first_job) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency_TrialSecondary",
+                             latency, base::TimeDelta::FromMilliseconds(1),
+                             base::TimeDelta::FromMinutes(10), 100);
+  if (is_first_job) {
+    UMA_HISTOGRAM_CUSTOM_TIMES(
+        "Net.CertVerifier_First_Job_Latency_TrialSecondary", latency,
+        base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(10),
+        100);
+  }
+}
+
+void TrialComparisonCertVerifier::RemoveJob(TrialVerificationJob* job_ptr) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+  auto it = jobs_.find(job_ptr);
+  DCHECK(it != jobs_.end());
+  jobs_.erase(it);
+}
+
+}  // namespace net
diff --git a/net/cert/trial_comparison_cert_verifier.h b/net/cert/trial_comparison_cert_verifier.h
new file mode 100644
index 0000000..cb04bcd5
--- /dev/null
+++ b/net/cert/trial_comparison_cert_verifier.h
@@ -0,0 +1,124 @@
+// 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 NET_CERT_TRIAL_COMPARISON_CERT_VERIFIER_H_
+#define NET_CERT_TRIAL_COMPARISON_CERT_VERIFIER_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include "base/containers/unique_ptr_adapters.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+#include "net/base/net_export.h"
+#include "net/cert/cert_verifier.h"
+
+namespace net {
+class CertVerifyProc;
+
+class NET_EXPORT TrialComparisonCertVerifier : public CertVerifier {
+ public:
+  // These values are persisted to logs. Entries should not be renumbered and
+  // numeric values should never be reused.
+  enum TrialComparisonResult {
+    kInvalid = 0,
+    kEqual = 1,
+    kPrimaryValidSecondaryError = 2,
+    kPrimaryErrorSecondaryValid = 3,
+    kBothValidDifferentDetails = 4,
+    kBothErrorDifferentDetails = 5,
+    kIgnoredMacUndesiredRevocationChecking = 6,
+    kIgnoredMultipleEVPoliciesAndOneMatchesRoot = 7,
+    kIgnoredDifferentPathReVerifiesEquivalent = 8,
+    kIgnoredLocallyTrustedLeaf = 9,
+    kIgnoredConfigurationChanged = 10,
+    kMaxValue = kIgnoredConfigurationChanged
+  };
+
+  using ReportCallback = base::RepeatingCallback<void(
+      const std::string& hostname,
+      const scoped_refptr<X509Certificate>& unverified_cert,
+      bool enable_rev_checking,
+      bool require_rev_checking_local_anchors,
+      bool enable_sha1_local_anchors,
+      bool disable_symantec_enforcement,
+      const net::CertVerifyResult& primary_result,
+      const net::CertVerifyResult& trial_result)>;
+
+  TrialComparisonCertVerifier(bool initial_allowed,
+                              scoped_refptr<CertVerifyProc> primary_verify_proc,
+                              scoped_refptr<CertVerifyProc> trial_verify_proc,
+                              ReportCallback report_callback);
+
+  ~TrialComparisonCertVerifier() override;
+
+  void set_trial_allowed(bool allowed) { allowed_ = allowed; }
+  bool trial_allowed() const { return allowed_; }
+
+  // CertVerifier implementation
+  int Verify(const RequestParams& params,
+             CertVerifyResult* verify_result,
+             CompletionOnceCallback callback,
+             std::unique_ptr<Request>* out_req,
+             const NetLogWithSource& net_log) override;
+  void SetConfig(const Config& config) override;
+
+  // Returns a CertVerifier using the primary CertVerifyProc, which will not
+  // cause OnPrimaryVerifierComplete to be called. This can be used to
+  // attempt to re-verify a cert with different chain or flags without
+  // messing up the stats or potentially causing an infinite loop.
+  CertVerifier* primary_reverifier() const { return primary_reverifier_.get(); }
+  CertVerifier* trial_verifier() const { return trial_verifier_.get(); }
+  CertVerifier* revocation_trial_verifier() const {
+    return revocation_trial_verifier_.get();
+  }
+
+ private:
+  class TrialVerificationJob;
+
+  void OnPrimaryVerifierComplete(const RequestParams& params,
+                                 const NetLogWithSource& net_log,
+                                 int primary_error,
+                                 const CertVerifyResult& primary_result,
+                                 base::TimeDelta primary_latency,
+                                 bool is_first_job);
+  void OnTrialVerifierComplete(const RequestParams& params,
+                               const NetLogWithSource& net_log,
+                               int trial_error,
+                               const CertVerifyResult& trial_result,
+                               base::TimeDelta latency,
+                               bool is_first_job);
+
+  void RemoveJob(TrialVerificationJob* job_ptr);
+
+  // Whether the trial is allowed.
+  bool allowed_;
+  // Callback that reports are sent to.
+  ReportCallback report_callback_;
+
+  CertVerifier::Config config_;
+
+  std::unique_ptr<CertVerifier> primary_verifier_;
+  std::unique_ptr<CertVerifier> primary_reverifier_;
+  std::unique_ptr<CertVerifier> trial_verifier_;
+  // Similar to |trial_verifier_|, except configured to always check
+  // revocation information.
+  std::unique_ptr<CertVerifier> revocation_trial_verifier_;
+
+  std::set<std::unique_ptr<TrialVerificationJob>, base::UniquePtrComparator>
+      jobs_;
+
+  THREAD_CHECKER(thread_checker_);
+
+  DISALLOW_COPY_AND_ASSIGN(TrialComparisonCertVerifier);
+};
+
+}  // namespace net
+
+#endif  // NET_CERT_TRIAL_COMPARISON_CERT_VERIFIER_H_
diff --git a/net/cert/trial_comparison_cert_verifier_unittest.cc b/net/cert/trial_comparison_cert_verifier_unittest.cc
new file mode 100644
index 0000000..894b53e
--- /dev/null
+++ b/net/cert/trial_comparison_cert_verifier_unittest.cc
@@ -0,0 +1,1701 @@
+// 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 "net/cert/trial_comparison_cert_verifier.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/threading/sequenced_task_runner_handle.h"
+#include "build/build_config.h"
+#include "crypto/sha2.h"
+#include "net/base/test_completion_callback.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/cert_verify_result.h"
+#include "net/cert/ev_root_ca_metadata.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+#include "net/log/net_log_with_source.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 "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using net::test::IsError;
+using net::test::IsOk;
+using testing::_;
+using testing::Return;
+using testing::SetArgPointee;
+
+namespace net {
+
+namespace {
+
+MATCHER_P(CertChainMatches, expected_cert, "") {
+  CertificateList actual_certs =
+      X509Certificate::CreateCertificateListFromBytes(
+          arg.data(), arg.size(), X509Certificate::FORMAT_PEM_CERT_SEQUENCE);
+  if (actual_certs.empty()) {
+    *result_listener << "failed to parse arg";
+    return false;
+  }
+  std::vector<std::string> actual_der_certs;
+  for (const auto& cert : actual_certs) {
+    actual_der_certs.emplace_back(
+        x509_util::CryptoBufferAsStringPiece(cert->cert_buffer()));
+  }
+
+  std::vector<std::string> expected_der_certs;
+  expected_der_certs.emplace_back(
+      x509_util::CryptoBufferAsStringPiece(expected_cert->cert_buffer()));
+  for (const auto& buffer : expected_cert->intermediate_buffers()) {
+    expected_der_certs.emplace_back(
+        x509_util::CryptoBufferAsStringPiece(buffer.get()));
+  }
+
+  return actual_der_certs == expected_der_certs;
+}
+
+// Like TestClosure, but handles multiple closure.Run()/WaitForResult()
+// calls correctly regardless of ordering.
+class RepeatedTestClosure {
+ public:
+  RepeatedTestClosure()
+      : closure_(base::BindRepeating(&RepeatedTestClosure::DidSetResult,
+                                     base::Unretained(this))) {}
+
+  const base::RepeatingClosure& closure() const { return closure_; }
+
+  void WaitForResult() {
+    DCHECK(!run_loop_);
+    if (!have_result_) {
+      run_loop_.reset(new base::RunLoop());
+      run_loop_->Run();
+      run_loop_.reset();
+      DCHECK(have_result_);
+    }
+    have_result_--;  // Auto-reset for next callback.
+  }
+
+ private:
+  void DidSetResult() {
+    have_result_++;
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+  // RunLoop.  Only non-NULL during the call to WaitForResult, so the class is
+  // reusable.
+  std::unique_ptr<base::RunLoop> run_loop_;
+
+  unsigned int have_result_ = 0;
+
+  base::RepeatingClosure closure_;
+};
+
+// Fake CertVerifyProc that sets the CertVerifyResult to a given value for
+// all certificates that are Verify()'d
+class FakeCertVerifyProc : public CertVerifyProc {
+ public:
+  FakeCertVerifyProc(const int result_error, const CertVerifyResult& result)
+      : result_error_(result_error),
+        result_(result),
+        main_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
+
+  void WaitForVerifyCall() { verify_called_.WaitForResult(); }
+
+  // CertVerifyProc implementation:
+  bool SupportsAdditionalTrustAnchors() const override { return false; }
+
+ protected:
+  ~FakeCertVerifyProc() override = default;
+
+ private:
+  int VerifyInternal(X509Certificate* cert,
+                     const std::string& hostname,
+                     const std::string& ocsp_response,
+                     int flags,
+                     CRLSet* crl_set,
+                     const CertificateList& additional_trust_anchors,
+                     CertVerifyResult* verify_result) override;
+
+  const int result_error_;
+  const CertVerifyResult result_;
+  RepeatedTestClosure verify_called_;
+  scoped_refptr<base::TaskRunner> main_task_runner_;
+
+  DISALLOW_COPY_AND_ASSIGN(FakeCertVerifyProc);
+};
+
+int FakeCertVerifyProc::VerifyInternal(
+    X509Certificate* cert,
+    const std::string& hostname,
+    const std::string& ocsp_response,
+    int flags,
+    CRLSet* crl_set,
+    const CertificateList& additional_trust_anchors,
+    CertVerifyResult* verify_result) {
+  *verify_result = result_;
+  main_task_runner_->PostTask(FROM_HERE, verify_called_.closure());
+  return result_error_;
+}
+
+// Fake CertVerifyProc that causes a failure if it is called.
+class NotCalledCertVerifyProc : public CertVerifyProc {
+ public:
+  NotCalledCertVerifyProc() = default;
+
+  // CertVerifyProc implementation:
+  bool SupportsAdditionalTrustAnchors() const override { return false; }
+
+ protected:
+  ~NotCalledCertVerifyProc() override = default;
+
+ private:
+  int VerifyInternal(X509Certificate* cert,
+                     const std::string& hostname,
+                     const std::string& ocsp_response,
+                     int flags,
+                     CRLSet* crl_set,
+                     const CertificateList& additional_trust_anchors,
+                     CertVerifyResult* verify_result) override;
+
+  DISALLOW_COPY_AND_ASSIGN(NotCalledCertVerifyProc);
+};
+
+int NotCalledCertVerifyProc::VerifyInternal(
+    X509Certificate* cert,
+    const std::string& hostname,
+    const std::string& ocsp_response,
+    int flags,
+    CRLSet* crl_set,
+    const CertificateList& additional_trust_anchors,
+    CertVerifyResult* verify_result) {
+  ADD_FAILURE() << "NotCalledCertVerifyProc was called!";
+  return ERR_UNEXPECTED;
+}
+
+void NotCalledCallback(int error) {
+  ADD_FAILURE() << "NotCalledCallback was called with error code " << error;
+}
+
+class MockCertVerifyProc : public CertVerifyProc {
+ public:
+  MockCertVerifyProc() = default;
+  // CertVerifyProc implementation:
+  bool SupportsAdditionalTrustAnchors() const override { return false; }
+  MOCK_METHOD7(VerifyInternal,
+               int(X509Certificate* cert,
+                   const std::string& hostname,
+                   const std::string& ocsp_response,
+                   int flags,
+                   CRLSet* crl_set,
+                   const CertificateList& additional_trust_anchors,
+                   CertVerifyResult* verify_result));
+
+ protected:
+  ~MockCertVerifyProc() override = default;
+
+  DISALLOW_COPY_AND_ASSIGN(MockCertVerifyProc);
+};
+
+struct TrialReportInfo {
+  TrialReportInfo(const std::string& hostname,
+                  const scoped_refptr<X509Certificate>& unverified_cert,
+                  bool enable_rev_checking,
+                  bool require_rev_checking_local_anchors,
+                  bool enable_sha1_local_anchors,
+                  bool disable_symantec_enforcement,
+                  const CertVerifyResult& primary_result,
+                  const CertVerifyResult& trial_result)
+      : hostname(hostname),
+        unverified_cert(unverified_cert),
+        enable_rev_checking(enable_rev_checking),
+        require_rev_checking_local_anchors(require_rev_checking_local_anchors),
+        enable_sha1_local_anchors(enable_sha1_local_anchors),
+        disable_symantec_enforcement(disable_symantec_enforcement),
+        primary_result(primary_result),
+        trial_result(trial_result) {}
+
+  std::string hostname;
+  scoped_refptr<X509Certificate> unverified_cert;
+  bool enable_rev_checking;
+  bool require_rev_checking_local_anchors;
+  bool enable_sha1_local_anchors;
+  bool disable_symantec_enforcement;
+  CertVerifyResult primary_result;
+  CertVerifyResult trial_result;
+};
+
+void RecordTrialReport(std::vector<TrialReportInfo>* reports,
+                       const std::string& hostname,
+                       const scoped_refptr<X509Certificate>& unverified_cert,
+                       bool enable_rev_checking,
+                       bool require_rev_checking_local_anchors,
+                       bool enable_sha1_local_anchors,
+                       bool disable_symantec_enforcement,
+                       const CertVerifyResult& primary_result,
+                       const CertVerifyResult& trial_result) {
+  TrialReportInfo report(
+      hostname, unverified_cert, enable_rev_checking,
+      require_rev_checking_local_anchors, enable_sha1_local_anchors,
+      disable_symantec_enforcement, primary_result, trial_result);
+  reports->push_back(report);
+}
+
+}  // namespace
+
+class TrialComparisonCertVerifierTest : public TestWithScopedTaskEnvironment {
+  void SetUp() override {
+    cert_chain_1_ = CreateCertificateChainFromFile(
+        GetTestCertsDirectory(), "multi-root-chain1.pem",
+        X509Certificate::FORMAT_AUTO);
+    ASSERT_TRUE(cert_chain_1_);
+    leaf_cert_1_ = X509Certificate::CreateFromBuffer(
+        bssl::UpRef(cert_chain_1_->cert_buffer()), {});
+    ASSERT_TRUE(leaf_cert_1_);
+    cert_chain_2_ = CreateCertificateChainFromFile(
+        GetTestCertsDirectory(), "multi-root-chain2.pem",
+        X509Certificate::FORMAT_AUTO);
+    ASSERT_TRUE(cert_chain_2_);
+  }
+
+ protected:
+  scoped_refptr<X509Certificate> cert_chain_1_;
+  scoped_refptr<X509Certificate> cert_chain_2_;
+  scoped_refptr<X509Certificate> leaf_cert_1_;
+  base::HistogramTester histograms_;
+};
+
+TEST_F(TrialComparisonCertVerifierTest, InitiallyDisallowed) {
+  CertVerifyResult dummy_result;
+  dummy_result.verified_cert = cert_chain_1_;
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      false /* initial_allowed */,
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, dummy_result),
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      base::BindRepeating(&RecordTrialReport, &reports));
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  RunUntilIdle();
+
+  // Expect no report.
+  EXPECT_TRUE(reports.empty());
+
+  // Primary verifier should have ran, trial verifier should not have.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               0);
+  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, InitiallyDisallowedThenAllowed) {
+  // Certificate that has multiple subjectAltName entries. This allows easily
+  // confirming which verification attempt the report was generated for without
+  // having to mock different CertVerifyProc results for each.
+  base::FilePath certs_dir =
+      GetTestNetDataDirectory()
+          .AppendASCII("verify_certificate_chain_unittest")
+          .AppendASCII("many-names");
+  scoped_refptr<X509Certificate> cert_chain = CreateCertificateChainFromFile(
+      certs_dir, "ok-all-types.pem", X509Certificate::FORMAT_AUTO);
+  ASSERT_TRUE(cert_chain);
+  ASSERT_EQ(2U, cert_chain->intermediate_buffers().size());
+
+  scoped_refptr<X509Certificate> leaf = X509Certificate::CreateFromBuffer(
+      bssl::UpRef(cert_chain->cert_buffer()), {});
+  ASSERT_TRUE(leaf);
+
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  // Trial verifier returns an error status.
+  CertVerifyResult secondary_result;
+  secondary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  secondary_result.verified_cert = cert_chain;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      false /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf, "t0.test", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  // Enable the trial and do another verification.
+  verifier.set_trial_allowed(true);
+  CertVerifier::RequestParams params2(leaf, "t1.test", 0 /* flags */,
+                                      std::string() /* ocsp_response */);
+  CertVerifyResult result2;
+  TestCompletionCallback callback2;
+  std::unique_ptr<CertVerifier::Request> request2;
+  error = verifier.Verify(params2, &result2, callback2.callback(), &request2,
+                          NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request2);
+
+  error = callback2.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Primary verifier should have run twice, trial verifier should run once.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
+
+  // Expect a report from the second verification.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+  EXPECT_EQ("t1.test", report.hostname);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, InitiallyAllowedThenDisallowed) {
+  // Certificate that has multiple subjectAltName entries. This allows easily
+  // confirming which verification attempt the report was generated for without
+  // having to mock different CertVerifyProc results for each.
+  base::FilePath certs_dir =
+      GetTestNetDataDirectory()
+          .AppendASCII("verify_certificate_chain_unittest")
+          .AppendASCII("many-names");
+  scoped_refptr<X509Certificate> cert_chain = CreateCertificateChainFromFile(
+      certs_dir, "ok-all-types.pem", X509Certificate::FORMAT_AUTO);
+  ASSERT_TRUE(cert_chain);
+  ASSERT_EQ(2U, cert_chain->intermediate_buffers().size());
+
+  scoped_refptr<X509Certificate> leaf = X509Certificate::CreateFromBuffer(
+      bssl::UpRef(cert_chain->cert_buffer()), {});
+  ASSERT_TRUE(leaf);
+
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  // Trial verifier returns an error status.
+  CertVerifyResult secondary_result;
+  secondary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  secondary_result.verified_cert = cert_chain;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf, "t0.test", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  // Disable the trial and do another verification.
+  verifier.set_trial_allowed(false);
+  CertVerifier::RequestParams params2(leaf, "t1.test", 0 /* flags */,
+                                      std::string() /* ocsp_response */);
+  CertVerifyResult result2;
+  TestCompletionCallback callback2;
+  std::unique_ptr<CertVerifier::Request> request2;
+  error = verifier.Verify(params2, &result2, callback2.callback(), &request2,
+                          NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request2);
+
+  error = callback2.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Primary verifier should have run twice, trial verifier should run once.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
+
+  // Expect a report from the first verification.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+  EXPECT_EQ("t0.test", report.hostname);
+}
+
+TEST_F(TrialComparisonCertVerifierTest,
+       ConfigChangedDuringPrimaryVerification) {
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1,
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  // Change the verifier config before the primary verification finishes.
+  CertVerifier::Config config;
+  config.enable_sha1_local_anchors = true;
+  verifier.SetConfig(config);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  RunUntilIdle();
+
+  // Since the config changed, trial verifier should not run.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               0);
+  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
+
+  // Expect no report.
+  EXPECT_TRUE(reports.empty());
+}
+
+TEST_F(TrialComparisonCertVerifierTest, ConfigChangedDuringTrialVerification) {
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  // Trial verifier returns an error status.
+  CertVerifyResult secondary_result;
+  secondary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  secondary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  // Change the verifier config during the trial verification.
+  CertVerifier::Config config;
+  config.enable_sha1_local_anchors = true;
+  verifier.SetConfig(config);
+
+  RunUntilIdle();
+
+  // Since the config was the same when both primary and trial verification
+  // started, the result should still be reported.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  // CertVerifier_Job_Latency_TrialSecondary is not recorded due to
+  // MultiThreadedCertVerifier's config_id_ check before calling the
+  // verify_complete_callback_.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               0);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+
+  EXPECT_EQ(0U, report.primary_result.cert_status);
+  EXPECT_EQ(CERT_STATUS_DATE_INVALID, report.trial_result.cert_status);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, SameResult) {
+  CertVerifyResult dummy_result;
+  dummy_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, dummy_result);
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, dummy_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect no report.
+  EXPECT_TRUE(reports.empty());
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample("Net.CertVerifier_TrialComparisonResult",
+                                 TrialComparisonCertVerifier::kEqual, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierErrorSecondaryOk) {
+  // Primary verifier returns an error status.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  primary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               primary_result);
+
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+
+  EXPECT_EQ(CERT_STATUS_DATE_INVALID, report.primary_result.cert_status);
+  EXPECT_EQ(0U, report.trial_result.cert_status);
+
+  EXPECT_TRUE(report.primary_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.trial_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.unverified_cert->EqualsIncludingChain(leaf_cert_1_.get()));
+
+  EXPECT_FALSE(report.enable_rev_checking);
+  EXPECT_FALSE(report.require_rev_checking_local_anchors);
+  EXPECT_FALSE(report.enable_sha1_local_anchors);
+  EXPECT_FALSE(report.disable_symantec_enforcement);
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, PrimaryVerifierOkSecondaryError) {
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  // Trial verifier returns an error status.
+  CertVerifyResult secondary_result;
+  secondary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  secondary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+
+  EXPECT_EQ(0U, report.primary_result.cert_status);
+  EXPECT_EQ(CERT_STATUS_DATE_INVALID, report.trial_result.cert_status);
+
+  EXPECT_TRUE(report.primary_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.trial_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.unverified_cert->EqualsIncludingChain(leaf_cert_1_.get()));
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kPrimaryValidSecondaryError, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, BothVerifiersDifferentErrors) {
+  // Primary verifier returns an error status.
+  CertVerifyResult primary_result;
+  primary_result.cert_status = CERT_STATUS_VALIDITY_TOO_LONG;
+  primary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_VALIDITY_TOO_LONG,
+                                               primary_result);
+
+  // Trial verifier returns a different error status.
+  CertVerifyResult secondary_result;
+  secondary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  secondary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(ERR_CERT_VALIDITY_TOO_LONG));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+
+  EXPECT_EQ(CERT_STATUS_VALIDITY_TOO_LONG, report.primary_result.cert_status);
+  EXPECT_EQ(CERT_STATUS_DATE_INVALID, report.trial_result.cert_status);
+
+  EXPECT_TRUE(report.primary_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.trial_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.unverified_cert->EqualsIncludingChain(leaf_cert_1_.get()));
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kBothErrorDifferentDetails, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest,
+       BothVerifiersOkDifferentVerifiedChains) {
+  // Primary verifier returns chain1 regardless of arguments.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  // Trial verifier returns a different verified cert chain.
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain_2_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+
+  EXPECT_EQ(0U, report.primary_result.cert_status);
+  EXPECT_EQ(0U, report.trial_result.cert_status);
+
+  EXPECT_TRUE(report.primary_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.trial_result.verified_cert->EqualsIncludingChain(
+      cert_chain_2_.get()));
+  EXPECT_TRUE(report.unverified_cert->EqualsIncludingChain(leaf_cert_1_.get()));
+
+  // Main CertVerifier_Job_Latency should have 2 counts since the
+  // primary_reverifier was used.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
+  // CertVerifier_Job_Latency_TrialPrimary only has 1 count since
+  // primary_reverifier doesn't use the same CertVerifier.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest,
+       BothVerifiersOkDifferentVerifiedChainsEqualAfterReverification) {
+  CertVerifyResult chain1_result;
+  chain1_result.verified_cert = cert_chain_1_;
+  CertVerifyResult chain2_result;
+  chain2_result.verified_cert = cert_chain_2_;
+
+  scoped_refptr<MockCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<MockCertVerifyProc>();
+  // Primary verifier returns ok status and chain1 if verifying the leaf alone.
+  EXPECT_CALL(*verify_proc1,
+              VerifyInternal(leaf_cert_1_.get(), _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(chain1_result), Return(OK)));
+  // Primary verifier returns ok status and chain2 if verifying chain2.
+  EXPECT_CALL(*verify_proc1,
+              VerifyInternal(cert_chain_2_.get(), _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(chain2_result), Return(OK)));
+
+  // Trial verifier returns ok status and chain2.
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, chain2_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect no report.
+  EXPECT_TRUE(reports.empty());
+
+  // Main CertVerifier_Job_Latency should have 2 counts since the
+  // primary_reverifier was used.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
+  // CertVerifier_Job_Latency_TrialPrimary only has 1 count since
+  // primary_reverifier doesn't use the same CertVerifier.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kIgnoredDifferentPathReVerifiesEquivalent,
+      1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest,
+       DifferentVerifiedChainsIgnorableDifferenceAfterReverification) {
+  base::FilePath certs_dir =
+      GetTestNetDataDirectory()
+          .AppendASCII("trial_comparison_cert_verifier_unittest")
+          .AppendASCII("target-multiple-policies");
+  scoped_refptr<X509Certificate> cert_chain = CreateCertificateChainFromFile(
+      certs_dir, "chain.pem", X509Certificate::FORMAT_AUTO);
+  ASSERT_TRUE(cert_chain);
+  ASSERT_EQ(2U, cert_chain->intermediate_buffers().size());
+
+  scoped_refptr<X509Certificate> leaf = X509Certificate::CreateFromBuffer(
+      bssl::UpRef(cert_chain->cert_buffer()), {});
+  ASSERT_TRUE(leaf);
+
+  // Chain with the same leaf and different root. This is not a valid chain, but
+  // doesn't matter for the unittest since this uses mock CertVerifyProcs.
+  std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
+  intermediates.push_back(
+      bssl::UpRef(cert_chain_1_->intermediate_buffers().back().get()));
+  scoped_refptr<X509Certificate> different_chain =
+      X509Certificate::CreateFromBuffer(bssl::UpRef(cert_chain->cert_buffer()),
+                                        std::move(intermediates));
+  ASSERT_TRUE(different_chain);
+
+  CertVerifyResult different_chain_result;
+  different_chain_result.verified_cert = different_chain;
+
+  CertVerifyResult nonev_chain_result;
+  nonev_chain_result.verified_cert = cert_chain;
+
+  CertVerifyResult ev_chain_result;
+  ev_chain_result.verified_cert = cert_chain;
+  ev_chain_result.cert_status =
+      CERT_STATUS_IS_EV | CERT_STATUS_REV_CHECKING_ENABLED;
+
+  SHA256HashValue root_fingerprint;
+  crypto::SHA256HashString(x509_util::CryptoBufferAsStringPiece(
+                               cert_chain->intermediate_buffers().back().get()),
+                           root_fingerprint.data,
+                           sizeof(root_fingerprint.data));
+  // Both policies in the target are EV policies, but only 1.2.6.7 is valid for
+  // the root in cert_chain.
+  ScopedTestEVPolicy scoped_ev_policy_1(EVRootCAMetadata::GetInstance(),
+                                        root_fingerprint, "1.2.6.7");
+  ScopedTestEVPolicy scoped_ev_policy_2(EVRootCAMetadata::GetInstance(),
+                                        SHA256HashValue(), "1.2.3.4");
+
+  scoped_refptr<MockCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<MockCertVerifyProc>();
+  // Primary verifier returns ok status and different_chain if verifying leaf
+  // alone.
+  EXPECT_CALL(*verify_proc1, VerifyInternal(leaf.get(), _, _, _, _, _, _))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<6>(different_chain_result), Return(OK)));
+  // Primary verifier returns ok status and nonev_chain_result if verifying
+  // cert_chain.
+  EXPECT_CALL(*verify_proc1, VerifyInternal(cert_chain.get(), _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(nonev_chain_result), Return(OK)));
+
+  // Trial verifier returns ok status and ev_chain_result.
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, ev_chain_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf, "test.example", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect no report.
+  EXPECT_TRUE(reports.empty());
+
+  // Main CertVerifier_Job_Latency should have 2 counts since the
+  // primary_reverifier was used.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 2);
+  // CertVerifier_Job_Latency_TrialPrimary only has 1 count since
+  // primary_reverifier doesn't use the same CertVerifier.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kIgnoredDifferentPathReVerifiesEquivalent,
+      1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, BothVerifiersOkDifferentCertStatus) {
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  primary_result.cert_status =
+      CERT_STATUS_IS_EV | CERT_STATUS_REV_CHECKING_ENABLED;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain_1_;
+  secondary_result.cert_status = CERT_STATUS_CT_COMPLIANCE_FAILED;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::Config config;
+  config.enable_rev_checking = true;
+  config.enable_sha1_local_anchors = true;
+  verifier.SetConfig(config);
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+
+  EXPECT_EQ(CERT_STATUS_IS_EV | CERT_STATUS_REV_CHECKING_ENABLED,
+            report.primary_result.cert_status);
+  EXPECT_EQ(CERT_STATUS_CT_COMPLIANCE_FAILED, report.trial_result.cert_status);
+
+  EXPECT_TRUE(report.enable_rev_checking);
+  EXPECT_FALSE(report.require_rev_checking_local_anchors);
+  EXPECT_TRUE(report.enable_sha1_local_anchors);
+  EXPECT_FALSE(report.disable_symantec_enforcement);
+
+  EXPECT_TRUE(report.primary_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.trial_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.unverified_cert->EqualsIncludingChain(leaf_cert_1_.get()));
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, Coalescing) {
+  // Primary verifier returns an error status.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  primary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               primary_result);
+
+  // Trial verifier has ok status.
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+
+  // Start first verification request.
+  CertVerifyResult result_1;
+  std::unique_ptr<CertVerifier::Request> request_1;
+  TestCompletionCallback callback_1;
+  int error = verifier.Verify(params, &result_1, callback_1.callback(),
+                              &request_1, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request_1);
+
+  // Start second verification request with same params.
+  CertVerifyResult result_2;
+  std::unique_ptr<CertVerifier::Request> request_2;
+  TestCompletionCallback callback_2;
+  error = verifier.Verify(params, &result_2, callback_2.callback(), &request_2,
+                          NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request_2);
+
+  // Both callbacks should be called with same error code.
+  error = callback_1.WaitForResult();
+  EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID));
+  error = callback_2.WaitForResult();
+  EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID));
+
+  // Trial verifier should run.
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a single report.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+
+  EXPECT_EQ(CERT_STATUS_DATE_INVALID, report.primary_result.cert_status);
+  EXPECT_EQ(0U, report.trial_result.cert_status);
+
+  EXPECT_TRUE(report.primary_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.trial_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.unverified_cert->EqualsIncludingChain(leaf_cert_1_.get()));
+
+  // Only one verification should be done by primary verifier.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  // Only one verification should be done by secondary verifier.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, CancelledDuringPrimaryVerification) {
+  // Primary verifier returns an error status.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  primary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               primary_result);
+
+  // Trial verifier has ok status.
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error =
+      verifier.Verify(params, &result, base::BindRepeating(&NotCalledCallback),
+                      &request, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  // Delete the request, cancelling it.
+  request.reset();
+
+  // The callback to the main verifier does not run. However, the verification
+  // still completes in the background and triggers the trial verification.
+
+  // Trial verifier should still run.
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+  const TrialReportInfo& report = reports[0];
+
+  EXPECT_EQ(CERT_STATUS_DATE_INVALID, report.primary_result.cert_status);
+  EXPECT_EQ(0U, report.trial_result.cert_status);
+
+  EXPECT_TRUE(report.primary_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.trial_result.verified_cert->EqualsIncludingChain(
+      cert_chain_1_.get()));
+  EXPECT_TRUE(report.unverified_cert->EqualsIncludingChain(leaf_cert_1_.get()));
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, DeletedDuringPrimaryVerification) {
+  // Primary verifier returns an error status.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  primary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               primary_result);
+
+  std::vector<TrialReportInfo> reports;
+  auto verifier = std::make_unique<TrialComparisonCertVerifier>(
+      true /* initial_allowed */, verify_proc1,
+      base::MakeRefCounted<NotCalledCertVerifyProc>(),
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error =
+      verifier->Verify(params, &result, base::BindRepeating(&NotCalledCallback),
+                       &request, NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  // Delete the TrialComparisonCertVerifier.
+  verifier.reset();
+
+  // The callback to the main verifier does not run. The verification task
+  // still completes in the background, but since the CertVerifier has been
+  // deleted, the result is ignored.
+
+  // Wait for any tasks to finish.
+  RunUntilIdle();
+
+  // Expect no report.
+  EXPECT_TRUE(reports.empty());
+
+  // Histograms should not be recorded.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 0);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 0);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               0);
+  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, DeletedAfterTrialVerificationStarted) {
+  // Primary verifier returns an error status.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain_1_;
+  primary_result.cert_status = CERT_STATUS_DATE_INVALID;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_DATE_INVALID,
+                                               primary_result);
+
+  // Trial verifier has ok status.
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  auto verifier = std::make_unique<TrialComparisonCertVerifier>(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier->Verify(params, &result, callback.callback(), &request,
+                               NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  // Wait for primary verifier to finish.
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(ERR_CERT_DATE_INVALID));
+
+  // Delete the TrialComparisonCertVerifier. The trial verification is still
+  // running on the task scheduler (or, depending on timing, has posted back
+  // to the IO thread after the Quit event).
+  verifier.reset();
+
+  // The callback to the trial verifier does not run. The verification task
+  // still completes in the background, but since the CertVerifier has been
+  // deleted, the result is ignored.
+
+  // Wait for any tasks to finish.
+  RunUntilIdle();
+
+  // Expect no report.
+  EXPECT_TRUE(reports.empty());
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  // Histograms for trial verifier should not be recorded.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               0);
+  histograms_.ExpectTotalCount("Net.CertVerifier_TrialComparisonResult", 0);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, MacUndesiredRevocationChecking) {
+  CertVerifyResult revoked_result;
+  revoked_result.verified_cert = cert_chain_1_;
+  revoked_result.cert_status = CERT_STATUS_REVOKED;
+
+  CertVerifyResult ok_result;
+  ok_result.verified_cert = cert_chain_1_;
+
+  // Primary verifier returns an error status.
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_REVOKED,
+                                               revoked_result);
+
+  scoped_refptr<MockCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<MockCertVerifyProc>();
+  // Secondary verifier returns ok status...
+  EXPECT_CALL(*verify_proc2, VerifyInternal(_, _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(ok_result), Return(OK)));
+  // ...unless it was called with REV_CHECKING_ENABLED.
+  EXPECT_CALL(
+      *verify_proc2,
+      VerifyInternal(_, _, _, CertVerifyProc::VERIFY_REV_CHECKING_ENABLED, _, _,
+                     _))
+      .WillRepeatedly(
+          DoAll(SetArgPointee<6>(revoked_result), Return(ERR_CERT_REVOKED)));
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+
+  RunUntilIdle();
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+#if defined(OS_MACOSX)
+  // Expect no report.
+  EXPECT_EQ(0U, reports.size());
+
+  // Secondary should have been called twice
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               2);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kIgnoredMacUndesiredRevocationChecking, 1);
+#else
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
+
+  // Expect a report.
+  EXPECT_EQ(1U, reports.size());
+#endif
+}
+
+TEST_F(TrialComparisonCertVerifierTest, PrimaryRevokedSecondaryOk) {
+  CertVerifyResult revoked_result;
+  revoked_result.verified_cert = cert_chain_1_;
+  revoked_result.cert_status = CERT_STATUS_REVOKED;
+
+  CertVerifyResult ok_result;
+  ok_result.verified_cert = cert_chain_1_;
+
+  // Primary verifier returns an error status.
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_REVOKED,
+                                               revoked_result);
+
+  // Secondary verifier returns ok status regardless of whether
+  // REV_CHECKING_ENABLED was passed.
+  scoped_refptr<MockCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<MockCertVerifyProc>();
+  EXPECT_CALL(*verify_proc2, VerifyInternal(_, _, _, _, _, _, _))
+      .WillRepeatedly(DoAll(SetArgPointee<6>(ok_result), Return(OK)));
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(ERR_CERT_REVOKED));
+
+  RunUntilIdle();
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+#if defined(OS_MACOSX)
+  // Secondary should have been called twice on mac due to attempting the
+  // kIgnoredMacUndesiredRevocationChecking workaround.
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               2);
+#else
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+
+#endif
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kPrimaryErrorSecondaryValid, 1);
+
+  // Expect a report.
+  EXPECT_EQ(1U, reports.size());
+}
+
+TEST_F(TrialComparisonCertVerifierTest, MultipleEVPolicies) {
+  base::FilePath certs_dir =
+      GetTestNetDataDirectory()
+          .AppendASCII("trial_comparison_cert_verifier_unittest")
+          .AppendASCII("target-multiple-policies");
+  scoped_refptr<X509Certificate> cert_chain = CreateCertificateChainFromFile(
+      certs_dir, "chain.pem", X509Certificate::FORMAT_AUTO);
+  ASSERT_TRUE(cert_chain);
+  ASSERT_EQ(2U, cert_chain->intermediate_buffers().size());
+
+  SHA256HashValue root_fingerprint;
+  crypto::SHA256HashString(x509_util::CryptoBufferAsStringPiece(
+                               cert_chain->intermediate_buffers().back().get()),
+                           root_fingerprint.data,
+                           sizeof(root_fingerprint.data));
+
+  // Both policies in the target are EV policies, but only 1.2.6.7 is valid for
+  // the root in this chain.
+  ScopedTestEVPolicy scoped_ev_policy_1(EVRootCAMetadata::GetInstance(),
+                                        root_fingerprint, "1.2.6.7");
+  ScopedTestEVPolicy scoped_ev_policy_2(EVRootCAMetadata::GetInstance(),
+                                        SHA256HashValue(), "1.2.3.4");
+
+  // Both verifiers return OK, but secondary returns EV status.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain;
+  secondary_result.cert_status =
+      CERT_STATUS_IS_EV | CERT_STATUS_REV_CHECKING_ENABLED;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect no report.
+  EXPECT_TRUE(reports.empty());
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kIgnoredMultipleEVPoliciesAndOneMatchesRoot,
+      1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, MultipleEVPoliciesNoneValidForRoot) {
+  base::FilePath certs_dir =
+      GetTestNetDataDirectory()
+          .AppendASCII("trial_comparison_cert_verifier_unittest")
+          .AppendASCII("target-multiple-policies");
+  scoped_refptr<X509Certificate> cert_chain = CreateCertificateChainFromFile(
+      certs_dir, "chain.pem", X509Certificate::FORMAT_AUTO);
+  ASSERT_TRUE(cert_chain);
+
+  // Both policies in the target are EV policies, but neither is valid for the
+  // root in this chain.
+  ScopedTestEVPolicy scoped_ev_policy_1(EVRootCAMetadata::GetInstance(), {1},
+                                        "1.2.6.7");
+  ScopedTestEVPolicy scoped_ev_policy_2(EVRootCAMetadata::GetInstance(), {2},
+                                        "1.2.3.4");
+
+  // Both verifiers return OK, but secondary returns EV status.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain;
+  secondary_result.cert_status =
+      CERT_STATUS_IS_EV | CERT_STATUS_REV_CHECKING_ENABLED;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, MultiplePoliciesOnlyOneIsEV) {
+  base::FilePath certs_dir =
+      GetTestNetDataDirectory()
+          .AppendASCII("trial_comparison_cert_verifier_unittest")
+          .AppendASCII("target-multiple-policies");
+  scoped_refptr<X509Certificate> cert_chain = CreateCertificateChainFromFile(
+      certs_dir, "chain.pem", X509Certificate::FORMAT_AUTO);
+  ASSERT_TRUE(cert_chain);
+  ASSERT_EQ(2U, cert_chain->intermediate_buffers().size());
+
+  SHA256HashValue root_fingerprint;
+  crypto::SHA256HashString(x509_util::CryptoBufferAsStringPiece(
+                               cert_chain->intermediate_buffers().back().get()),
+                           root_fingerprint.data,
+                           sizeof(root_fingerprint.data));
+
+  // One policy in the target is an EV policy and is valid for the root.
+  ScopedTestEVPolicy scoped_ev_policy_1(EVRootCAMetadata::GetInstance(),
+                                        root_fingerprint, "1.2.6.7");
+
+  // Both verifiers return OK, but secondary returns EV status.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = cert_chain;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  CertVerifyResult secondary_result;
+  secondary_result.verified_cert = cert_chain;
+  secondary_result.cert_status =
+      CERT_STATUS_IS_EV | CERT_STATUS_REV_CHECKING_ENABLED;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect a report.
+  ASSERT_EQ(1U, reports.size());
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kBothValidDifferentDetails, 1);
+}
+
+TEST_F(TrialComparisonCertVerifierTest, LocallyTrustedLeaf) {
+  // Platform verifier verifies the leaf directly.
+  CertVerifyResult primary_result;
+  primary_result.verified_cert = leaf_cert_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc1 =
+      base::MakeRefCounted<FakeCertVerifyProc>(OK, primary_result);
+
+  // Trial verifier does not support directly-trusted leaf certs.
+  CertVerifyResult secondary_result;
+  secondary_result.cert_status = CERT_STATUS_AUTHORITY_INVALID;
+  secondary_result.verified_cert = leaf_cert_1_;
+  scoped_refptr<FakeCertVerifyProc> verify_proc2 =
+      base::MakeRefCounted<FakeCertVerifyProc>(ERR_CERT_AUTHORITY_INVALID,
+                                               secondary_result);
+
+  std::vector<TrialReportInfo> reports;
+  TrialComparisonCertVerifier verifier(
+      true /* initial_allowed */, verify_proc1, verify_proc2,
+      base::BindRepeating(&RecordTrialReport, &reports));
+
+  CertVerifier::RequestParams params(leaf_cert_1_, "127.0.0.1", 0 /* flags */,
+                                     std::string() /* ocsp_response */);
+  CertVerifyResult result;
+  TestCompletionCallback callback;
+  std::unique_ptr<CertVerifier::Request> request;
+  int error = verifier.Verify(params, &result, callback.callback(), &request,
+                              NetLogWithSource());
+  ASSERT_THAT(error, IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(request);
+
+  error = callback.WaitForResult();
+  EXPECT_THAT(error, IsError(OK));
+
+  verify_proc2->WaitForVerifyCall();
+  RunUntilIdle();
+
+  // Expect no report.
+  EXPECT_TRUE(reports.empty());
+
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialPrimary", 1);
+  histograms_.ExpectTotalCount("Net.CertVerifier_Job_Latency_TrialSecondary",
+                               1);
+  histograms_.ExpectUniqueSample(
+      "Net.CertVerifier_TrialComparisonResult",
+      TrialComparisonCertVerifier::kIgnoredLocallyTrustedLeaf, 1);
+}
+
+}  // namespace net
diff --git a/chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/chain.pem b/net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/chain.pem
similarity index 100%
rename from chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/chain.pem
rename to net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/chain.pem
diff --git a/chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/generate-chains.py b/net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/generate-chains.py
similarity index 100%
rename from chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/generate-chains.py
rename to net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/generate-chains.py
diff --git a/chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Intermediate.key b/net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Intermediate.key
similarity index 100%
rename from chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Intermediate.key
rename to net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Intermediate.key
diff --git a/chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Root.key b/net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Root.key
similarity index 100%
rename from chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Root.key
rename to net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Root.key
diff --git a/chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Target.key b/net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Target.key
similarity index 100%
rename from chrome/test/data/net/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Target.key
rename to net/data/trial_comparison_cert_verifier_unittest/target-multiple-policies/keys/Target.key
diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc
index f16d7dd..34cd1a925 100644
--- a/net/disk_cache/simple/simple_entry_impl.cc
+++ b/net/disk_cache/simple/simple_entry_impl.cc
@@ -167,15 +167,8 @@
       path_(path),
       entry_hash_(entry_hash),
       use_optimistic_operations_(operations_mode == OPTIMISTIC_OPERATIONS),
-      is_initial_stream1_read_(true),
       last_used_(Time::Now()),
       last_modified_(last_used_),
-      sparse_data_size_(0),
-      open_count_(0),
-      doom_state_(DOOM_NONE),
-      optimistic_create_pending_doom_state_(CREATE_NORMAL),
-      state_(STATE_UNINITIALIZED),
-      synchronous_entry_(NULL),
       prioritized_task_runner_(backend_->prioritized_task_runner()),
       net_log_(
           net::NetLogWithSource::Make(net_log,
diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h
index 3110cb5..524b823 100644
--- a/net/disk_cache/simple/simple_entry_impl.h
+++ b/net/disk_cache/simple/simple_entry_impl.h
@@ -376,7 +376,7 @@
   const base::FilePath path_;
   const uint64_t entry_hash_;
   const bool use_optimistic_operations_;
-  bool is_initial_stream1_read_;  // used for metrics only.
+  bool is_initial_stream1_read_ = true;  // used for metrics only.
   std::string key_;
 
   // |last_used_|, |last_modified_| and |data_size_| are copied from the
@@ -385,22 +385,22 @@
   base::Time last_used_;
   base::Time last_modified_;
   int32_t data_size_[kSimpleEntryStreamCount];
-  int32_t sparse_data_size_;
+  int32_t sparse_data_size_ = 0;
 
   // Number of times this object has been returned from Backend::OpenEntry() and
   // Backend::CreateEntry() without subsequent Entry::Close() calls. Used to
   // notify the backend when this entry not used by any callers.
-  int open_count_;
+  int open_count_ = 0;
 
-  DoomState doom_state_;
+  DoomState doom_state_ = DOOM_NONE;
 
   enum {
     CREATE_NORMAL,
     CREATE_OPTIMISTIC_PENDING_DOOM,
     CREATE_OPTIMISTIC_PENDING_DOOM_FOLLOWED_BY_DOOM,
-  } optimistic_create_pending_doom_state_;
+  } optimistic_create_pending_doom_state_ = CREATE_NORMAL;
 
-  State state_;
+  State state_ = STATE_UNINITIALIZED;
 
   // When possible, we compute a crc32, for the data in each entry as we read or
   // write. For each stream, |crc32s_[index]| is the crc32 of that stream from
@@ -427,7 +427,7 @@
   // an operation is being executed no one owns the synchronous entry. Therefore
   // SimpleEntryImpl should not be deleted while an operation is running as that
   // would leak the SimpleSynchronousEntry.
-  SimpleSynchronousEntry* synchronous_entry_;
+  SimpleSynchronousEntry* synchronous_entry_ = nullptr;
 
   scoped_refptr<net::PrioritizedTaskRunner> prioritized_task_runner_;
 
diff --git a/net/disk_cache/simple/simple_file_tracker.cc b/net/disk_cache/simple/simple_file_tracker.cc
index 4f841d4..3f769bd 100644
--- a/net/disk_cache/simple/simple_file_tracker.cc
+++ b/net/disk_cache/simple/simple_file_tracker.cc
@@ -18,7 +18,7 @@
 namespace disk_cache {
 
 SimpleFileTracker::SimpleFileTracker(int file_limit)
-    : file_limit_(file_limit), open_files_(0) {}
+    : file_limit_(file_limit) {}
 
 SimpleFileTracker::~SimpleFileTracker() {
   DCHECK(lru_.empty());
@@ -305,8 +305,7 @@
   DCHECK_EQ(*owners_files->position_in_lru, owners_files);
 }
 
-SimpleFileTracker::FileHandle::FileHandle()
-    : file_tracker_(nullptr), entry_(nullptr), file_(nullptr) {}
+SimpleFileTracker::FileHandle::FileHandle() = default;
 
 SimpleFileTracker::FileHandle::FileHandle(SimpleFileTracker* file_tracker,
                                           const SimpleSynchronousEntry* entry,
diff --git a/net/disk_cache/simple/simple_file_tracker.h b/net/disk_cache/simple/simple_file_tracker.h
index 2f590a2..d47ab03 100644
--- a/net/disk_cache/simple/simple_file_tracker.h
+++ b/net/disk_cache/simple/simple_file_tracker.h
@@ -63,26 +63,25 @@
                base::File* file);
 
     // All the pointer fields are nullptr in the default/moved away from form.
-    SimpleFileTracker* file_tracker_;
-    const SimpleSynchronousEntry* entry_;
+    SimpleFileTracker* file_tracker_ = nullptr;
+    const SimpleSynchronousEntry* entry_ = nullptr;
     SimpleFileTracker::SubFile subfile_;
-    base::File* file_;
+    base::File* file_ = nullptr;
     DISALLOW_COPY_AND_ASSIGN(FileHandle);
   };
 
   struct EntryFileKey {
-    EntryFileKey() : entry_hash(0), doom_generation(0) {}
-    explicit EntryFileKey(uint64_t hash)
-        : entry_hash(hash), doom_generation(0) {}
+    EntryFileKey() {}
+    explicit EntryFileKey(uint64_t hash) : entry_hash(hash) {}
 
-    uint64_t entry_hash;
+    uint64_t entry_hash = 0;
 
     // 0 means this a non-doomed, active entry, for its backend that will be
     // checked on OpenEntry(key) where hash(key) = entry_hash.  Other values of
     // |doom_generation| are used to generate distinct file names for entries
     // that have been Doom()ed, either by explicit API call by the client or
     // internal operation (eviction, collisions, etc.)
-    uint64_t doom_generation;
+    uint64_t doom_generation = 0;
   };
 
   // The default limit here is half of what's available on our target OS where
@@ -218,7 +217,7 @@
   // conservatively, considering SimpleCache's parallelism is bounded by a low
   // number of threads, and getting it exact would require re-acquiring the
   // lock after closing the file.
-  int open_files_;
+  int open_files_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(SimpleFileTracker);
 };
diff --git a/net/disk_cache/simple/simple_index.cc b/net/disk_cache/simple/simple_index.cc
index 17dcf89..cd12bb20 100644
--- a/net/disk_cache/simple/simple_index.cc
+++ b/net/disk_cache/simple/simple_index.cc
@@ -185,21 +185,13 @@
     : cleanup_tracker_(std::move(cleanup_tracker)),
       delegate_(delegate),
       cache_type_(cache_type),
-      cache_size_(0),
-      max_size_(0),
-      high_watermark_(0),
-      low_watermark_(0),
-      eviction_in_progress_(false),
-      initialized_(false),
-      init_method_(INITIALIZE_METHOD_MAX),
       index_file_(std::move(index_file)),
       io_thread_(io_thread),
       // Creating the callback once so it is reused every time
       // write_to_disk_timer_.Start() is called.
       write_to_disk_cb_(base::Bind(&SimpleIndex::WriteToDisk,
                                    AsWeakPtr(),
-                                   INDEX_WRITE_REASON_IDLE)),
-      app_on_background_(false) {}
+                                   INDEX_WRITE_REASON_IDLE)) {}
 
 SimpleIndex::~SimpleIndex() {
   DCHECK(io_thread_checker_.CalledOnValidThread());
diff --git a/net/disk_cache/simple/simple_index.h b/net/disk_cache/simple/simple_index.h
index bd0a57d..c3e3e02 100644
--- a/net/disk_cache/simple/simple_index.h
+++ b/net/disk_cache/simple/simple_index.h
@@ -274,18 +274,18 @@
   EntrySet entries_set_;
 
   const net::CacheType cache_type_;
-  uint64_t cache_size_;  // Total cache storage size in bytes.
-  uint64_t max_size_;
-  uint64_t high_watermark_;
-  uint64_t low_watermark_;
-  bool eviction_in_progress_;
+  uint64_t cache_size_ = 0;  // Total cache storage size in bytes.
+  uint64_t max_size_ = 0;
+  uint64_t high_watermark_ = 0;
+  uint64_t low_watermark_ = 0;
+  bool eviction_in_progress_ = false;
   base::TimeTicks eviction_start_time_;
 
   // This stores all the entry_hash of entries that are removed during
   // initialization.
   std::unordered_set<uint64_t> removed_entries_;
-  bool initialized_;
-  IndexInitMethod init_method_;
+  bool initialized_ = false;
+  IndexInitMethod init_method_ = INITIALIZE_METHOD_MAX;
 
   std::unique_ptr<SimpleIndexFile> index_file_;
 
@@ -309,7 +309,7 @@
   // Set to true when the app is on the background. When the app is in the
   // background we can write the index much more frequently, to insure fresh
   // index on next startup.
-  bool app_on_background_;
+  bool app_on_background_ = false;
 };
 
 }  // namespace disk_cache
diff --git a/net/disk_cache/simple/simple_index_file.cc b/net/disk_cache/simple/simple_index_file.cc
index 1702cb2a..f377c41 100644
--- a/net/disk_cache/simple/simple_index_file.cc
+++ b/net/disk_cache/simple/simple_index_file.cc
@@ -19,7 +19,6 @@
 #include "base/strings/string_util.h"
 #include "base/task_runner_util.h"
 #include "base/threading/thread_restrictions.h"
-#include "net/disk_cache/simple/simple_backend_version.h"
 #include "net/disk_cache/simple/simple_entry_format.h"
 #include "net/disk_cache/simple/simple_histogram_macros.h"
 #include "net/disk_cache/simple/simple_index.h"
@@ -253,9 +252,7 @@
 const char SimpleIndexFile::kTempIndexFileName[] = "temp-index";
 
 SimpleIndexFile::IndexMetadata::IndexMetadata()
-    : magic_number_(kSimpleIndexMagicNumber),
-      version_(kSimpleVersion),
-      reason_(SimpleIndex::INDEX_WRITE_REASON_MAX),
+    : reason_(SimpleIndex::INDEX_WRITE_REASON_MAX),
       entry_count_(0),
       cache_size_(0) {}
 
@@ -263,11 +260,7 @@
     SimpleIndex::IndexWriteToDiskReason reason,
     uint64_t entry_count,
     uint64_t cache_size)
-    : magic_number_(kSimpleIndexMagicNumber),
-      version_(kSimpleVersion),
-      reason_(reason),
-      entry_count_(entry_count),
-      cache_size_(cache_size) {}
+    : reason_(reason), entry_count_(entry_count), cache_size_(cache_size) {}
 
 void SimpleIndexFile::IndexMetadata::Serialize(base::Pickle* pickle) const {
   DCHECK(pickle);
diff --git a/net/disk_cache/simple/simple_index_file.h b/net/disk_cache/simple/simple_index_file.h
index 47d5251..b8272fb5 100644
--- a/net/disk_cache/simple/simple_index_file.h
+++ b/net/disk_cache/simple/simple_index_file.h
@@ -18,6 +18,7 @@
 #include "base/pickle.h"
 #include "net/base/cache_type.h"
 #include "net/base/net_export.h"
+#include "net/disk_cache/simple/simple_backend_version.h"
 #include "net/disk_cache/simple/simple_index.h"
 
 namespace base {
@@ -81,8 +82,8 @@
     friend class V7IndexMetadataForTest;
     friend class V8IndexMetadataForTest;
 
-    uint64_t magic_number_;
-    uint32_t version_;
+    uint64_t magic_number_ = kSimpleIndexMagicNumber;
+    uint32_t version_ = kSimpleVersion;
     SimpleIndex::IndexWriteToDiskReason reason_;
     uint64_t entry_count_;
     uint64_t cache_size_;  // Total cache storage size in bytes.
diff --git a/net/disk_cache/simple/simple_index_unittest.cc b/net/disk_cache/simple/simple_index_unittest.cc
index b65b0ae..e849802 100644
--- a/net/disk_cache/simple/simple_index_unittest.cc
+++ b/net/disk_cache/simple/simple_index_unittest.cc
@@ -68,10 +68,7 @@
                             public base::SupportsWeakPtr<MockSimpleIndexFile> {
  public:
   explicit MockSimpleIndexFile(net::CacheType cache_type)
-      : SimpleIndexFile(NULL, NULL, cache_type, base::FilePath()),
-        load_result_(NULL),
-        load_index_entries_calls_(0),
-        disk_writes_(0) {}
+      : SimpleIndexFile(nullptr, nullptr, cache_type, base::FilePath()) {}
 
   void LoadIndexEntries(base::Time cache_last_modified,
                         const base::Closure& callback,
@@ -103,18 +100,16 @@
 
  private:
   base::Closure load_callback_;
-  SimpleIndexLoadResult* load_result_;
-  int load_index_entries_calls_;
-  int disk_writes_;
+  SimpleIndexLoadResult* load_result_ = nullptr;
+  int load_index_entries_calls_ = 0;
+  int disk_writes_ = 0;
   SimpleIndex::EntrySet disk_write_entry_set_;
 };
 
 class SimpleIndexTest : public net::TestWithScopedTaskEnvironment,
                         public SimpleIndexDelegate {
  protected:
-  SimpleIndexTest()
-      : hashes_(base::Bind(&HashesInitializer)),
-        doom_entries_calls_(0) {}
+  SimpleIndexTest() : hashes_(base::BindRepeating(&HashesInitializer)) {}
 
   static uint64_t HashesInitializer(size_t hash_index) {
     return disk_cache::simple_util::GetEntryHashKey(
@@ -190,7 +185,7 @@
   base::test::ScopedFeatureList scoped_feature_list_;
 
   std::vector<uint64_t> last_doom_entry_hashes_;
-  int doom_entries_calls_;
+  int doom_entries_calls_ = 0;
 };
 
 class SimpleIndexAppCacheTest : public SimpleIndexTest {
diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc
index 1e7fef1a..336f166 100644
--- a/net/disk_cache/simple/simple_synchronous_entry.cc
+++ b/net/disk_cache/simple/simple_synchronous_entry.cc
@@ -1117,12 +1117,8 @@
       entry_file_key_(entry_hash),
       had_index_(had_index),
       key_(key),
-      have_open_files_(false),
-      initialized_(false),
       file_tracker_(file_tracker),
-      trailer_prefetch_size_(trailer_prefetch_size),
-      computed_trailer_prefetch_size_(-1),
-      sparse_file_open_(false) {
+      trailer_prefetch_size_(trailer_prefetch_size) {
   for (int i = 0; i < kSimpleEntryNormalFileCount; ++i)
     empty_file_omitted_[i] = false;
 }
diff --git a/net/disk_cache/simple/simple_synchronous_entry.h b/net/disk_cache/simple/simple_synchronous_entry.h
index 28faa355..59b3740 100644
--- a/net/disk_cache/simple/simple_synchronous_entry.h
+++ b/net/disk_cache/simple/simple_synchronous_entry.h
@@ -485,8 +485,8 @@
   const bool had_index_;
   std::string key_;
 
-  bool have_open_files_;
-  bool initialized_;
+  bool have_open_files_ = false;
+  bool initialized_ = false;
 
   // Normally false. This is set to true when an entry is opened without
   // checking the file headers. Any subsequent read will perform the check
@@ -507,7 +507,7 @@
   // EOF record and stream 0 when the entry was actually opened.  This
   // may be different from the trailer_prefetch_size_ hint and is
   // propagated back to the index in order to optimize the next open.
-  int32_t computed_trailer_prefetch_size_;
+  int32_t computed_trailer_prefetch_size_ = -1;
 
   // True if the corresponding stream is empty and therefore no on-disk file
   // was created to store it.
@@ -516,7 +516,7 @@
   typedef std::map<int64_t, SparseRange> SparseRangeOffsetMap;
   typedef SparseRangeOffsetMap::iterator SparseRangeIterator;
   SparseRangeOffsetMap sparse_ranges_;
-  bool sparse_file_open_;
+  bool sparse_file_open_ = false;
 
   // Offset of the end of the sparse file (where the next sparse range will be
   // written).
diff --git a/net/dns/address_sorter_posix_unittest.cc b/net/dns/address_sorter_posix_unittest.cc
index e4b6120..5e8eabc 100644
--- a/net/dns/address_sorter_posix_unittest.cc
+++ b/net/dns/address_sorter_posix_unittest.cc
@@ -166,6 +166,14 @@
     NOTIMPLEMENTED();
     return std::unique_ptr<SSLClientSocket>();
   }
+  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
+      std::unique_ptr<StreamSocket>,
+      const HostPortPair&,
+      const SSLConfig&,
+      const SSLClientSocketContext&) override {
+    NOTIMPLEMENTED();
+    return std::unique_ptr<SSLClientSocket>();
+  }
   std::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<ClientSocketHandle> transport_socket,
       const std::string& user_agent,
diff --git a/net/dns/dns_session_unittest.cc b/net/dns/dns_session_unittest.cc
index 8e1d709..80d4d4d 100644
--- a/net/dns/dns_session_unittest.cc
+++ b/net/dns/dns_session_unittest.cc
@@ -53,6 +53,15 @@
     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,
diff --git a/net/features.gni b/net/features.gni
index 346111fd..c6e373d 100644
--- a/net/features.gni
+++ b/net/features.gni
@@ -39,4 +39,8 @@
   # willing to take the responsibility to make sure that all important
   # connections use HTTPS.
   include_transport_security_state_preload_list = true
+
+  # Platforms where the cert verifier comparison trial is supported.
+  # See https://crbug.com/649026.
+  trial_comparison_cert_verifier_supported = is_desktop_linux || is_mac
 }
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 0143fa39..89c2c7c 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -306,11 +306,6 @@
   return GetSocketPoolManager(pool_type)->GetTransportSocketPool();
 }
 
-TransportClientSocketPool* HttpNetworkSession::GetSSLSocketPool(
-    SocketPoolType pool_type) {
-  return GetSocketPoolManager(pool_type)->GetSSLSocketPool();
-}
-
 TransportClientSocketPool* HttpNetworkSession::GetSocketPoolForSOCKSProxy(
     SocketPoolType pool_type,
     const ProxyServer& socks_proxy) {
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 20691e6..b2e9bfa8 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -292,7 +292,6 @@
   void RemoveResponseDrainer(HttpResponseBodyDrainer* drainer);
 
   TransportClientSocketPool* GetTransportSocketPool(SocketPoolType pool_type);
-  TransportClientSocketPool* GetSSLSocketPool(SocketPoolType pool_type);
   // Currently only works for SOCKS proxies.
   TransportClientSocketPool* GetSocketPoolForSOCKSProxy(
       SocketPoolType pool_type,
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index e545204..9b6c22f 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -153,11 +153,6 @@
       ->IdleSocketCount();
 }
 
-int GetIdleSocketCountInSSLSocketPool(HttpNetworkSession* session) {
-  return session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
-      ->IdleSocketCount();
-}
-
 bool IsTransportSocketPoolStalled(HttpNetworkSession* session) {
   return session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
       ->IsStalled();
@@ -1820,7 +1815,7 @@
   // Wait for the preconnect to complete.
   // TODO(davidben): Some way to wait for an idle socket count might be handy.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session.get()));
 
   // Make the request.
   TestCompletionCallback callback;
@@ -8017,7 +8012,7 @@
   base::RunLoop().RunUntilIdle();
 
   // We now check to make sure the socket was added back to the pool.
-  EXPECT_EQ(1, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session.get()));
 }
 
 // Grab a SSL socket, use it, and put it back into the pool.  Then, reuse it
@@ -8082,7 +8077,7 @@
   base::RunLoop().RunUntilIdle();
 
   // We now check to make sure the socket was added back to the pool.
-  EXPECT_EQ(1, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session.get()));
 
   // Now start the second transaction, which should reuse the previous socket.
 
@@ -8110,7 +8105,7 @@
   base::RunLoop().RunUntilIdle();
 
   // We now check to make sure the socket was added back to the pool.
-  EXPECT_EQ(1, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session.get()));
 }
 
 // Grab a socket, use it, and put it back into the pool. Then, make
@@ -8290,7 +8285,7 @@
   std::unique_ptr<HttpNetworkSession> session(CreateSession(&session_deps_));
   HttpNetworkTransaction trans(DEFAULT_PRIORITY, session.get());
 
-  EXPECT_EQ(0, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(0, GetIdleSocketCountInTransportSocketPool(session.get()));
   int rv = trans.Start(&request, callback.callback(), NetLogWithSource());
 
   EXPECT_THAT(callback.GetResult(rv), IsOk());
@@ -8306,7 +8301,7 @@
       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(0, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(0, GetIdleSocketCountInTransportSocketPool(session.get()));
 
   std::string response_data;
   rv = ReadTransaction(&trans, &response_data);
@@ -8318,14 +8313,14 @@
   base::RunLoop().RunUntilIdle();
 
   // We now check to make sure the socket was added back to the pool.
-  EXPECT_EQ(1, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(1, GetIdleSocketCountInTransportSocketPool(session.get()));
 
   // Make memory notification once again and ensure idle socket is closed.
   base::MemoryPressureListener::NotifyMemoryPressure(
       base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL);
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(0, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(0, GetIdleSocketCountInTransportSocketPool(session.get()));
 }
 
 // Make sure that we recycle a socket after a zero-length response.
@@ -11058,27 +11053,15 @@
     HttpNetworkSessionPeer peer(session.get());
     CaptureGroupNameTransportSocketPool* transport_conn_pool =
         new CaptureGroupNameTransportSocketPool(nullptr, nullptr);
-    CaptureGroupNameTransportSocketPool* ssl_conn_pool =
-        new CaptureGroupNameTransportSocketPool(nullptr, nullptr);
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
     mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
-    mock_pool_manager->SetSSLSocketPool(ssl_conn_pool);
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
 
     EXPECT_EQ(ERR_IO_PENDING,
               GroupNameTransactionHelper(tests[i].url, session.get()));
-    if (tests[i].ssl) {
-      EXPECT_EQ(tests[i].expected_group_name,
-                ssl_conn_pool->last_group_name_received());
-    } else {
-      EXPECT_EQ(tests[i].expected_group_name,
-                transport_conn_pool->last_group_name_received());
-    }
-    // When SSL proxy is in use, socket must be requested from |ssl_conn_pool|.
-    EXPECT_EQ(tests[i].ssl, ssl_conn_pool->socket_requested());
-    // When SSL proxy is not in use, socket must be requested from
-    // |transport_conn_pool|.
-    EXPECT_EQ(!tests[i].ssl, transport_conn_pool->socket_requested());
+    EXPECT_EQ(tests[i].expected_group_name,
+              transport_conn_pool->last_group_name_received());
+    EXPECT_TRUE(transport_conn_pool->socket_requested());
   }
 }
 
@@ -17016,7 +16999,7 @@
 
   // The SSL socket should automatically be closed, so the HTTP request can
   // start.
-  EXPECT_EQ(0, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(0, GetIdleSocketCountInTransportSocketPool(session.get()));
   ASSERT_FALSE(IsTransportSocketPoolStalled(session.get()));
 
   // The HTTP request can now complete.
@@ -17080,7 +17063,7 @@
   // cancelled when a normal transaction is cancelled.
   HttpStreamFactory* http_stream_factory = session->http_stream_factory();
   http_stream_factory->PreconnectStreams(1, ssl_request);
-  EXPECT_EQ(0, GetIdleSocketCountInSSLSocketPool(session.get()));
+  EXPECT_EQ(0, GetIdleSocketCountInTransportSocketPool(session.get()));
 
   // Start the HTTP request.  Pool should stall.
   TestCompletionCallback http_callback;
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index 64ddf53..4d5eaeb4 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -102,10 +102,7 @@
                          session_deps_.ssl_config_service.get(),
                          nullptr /* socket_performance_watcher_factory */,
                          nullptr /* network_quality_estimator */,
-                         nullptr /* net_log */,
-                         &transport_socket_pool_,
-                         nullptr /* socks_pool */,
-                         nullptr /* http_proxy_pool */),
+                         nullptr /* net_log */),
         field_trial_list_(nullptr),
         pool_(
             std::make_unique<HttpProxyClientSocketPool>(kMaxSockets,
diff --git a/net/http/http_stream_factory_unittest.cc b/net/http/http_stream_factory_unittest.cc
index 8488072..193b301 100644
--- a/net/http/http_stream_factory_unittest.cc
+++ b/net/http/http_stream_factory_unittest.cc
@@ -384,6 +384,12 @@
   PreconnectHelperForURL(test.num_streams, url, session);
 }
 
+std::string GetGroupName(const TestCase& test) {
+  if (test.ssl)
+    return "ssl/www.google.com:443";
+  return "www.google.com:80";
+}
+
 template <typename ParentPool>
 class CapturePreconnectsSocketPool : public ParentPool {
  public:
@@ -394,6 +400,7 @@
                                CTPolicyEnforcer* ct_policy_enforcer);
 
   int last_num_streams() const { return last_num_streams_; }
+  const std::string& last_group_name() const { return last_group_name_; }
 
   // Resets |last_num_streams_| to its default value.
   void reset_last_num_streams() { last_num_streams_ = -1; }
@@ -415,6 +422,7 @@
                       int num_sockets,
                       const NetLogWithSource& net_log) override {
     last_num_streams_ = num_sockets;
+    last_group_name_ = group_name;
   }
 
   void CancelRequest(const std::string& group_name,
@@ -443,6 +451,7 @@
 
  private:
   int last_num_streams_;
+  std::string last_group_name_;
 };
 
 typedef CapturePreconnectsSocketPool<TransportClientSocketPool>
@@ -505,21 +514,12 @@
             session_deps.transport_security_state.get(),
             session_deps.cert_transparency_verifier.get(),
             session_deps.ct_policy_enforcer.get());
-    CapturePreconnectsTransportSocketPool* ssl_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());
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
     mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
-    mock_pool_manager->SetSSLSocketPool(ssl_conn_pool);
     peer.SetClientSocketPoolManager(std::move(mock_pool_manager));
     PreconnectHelper(kTests[i], session.get());
-    if (kTests[i].ssl)
-      EXPECT_EQ(kTests[i].num_streams, ssl_conn_pool->last_num_streams());
-    else
-      EXPECT_EQ(kTests[i].num_streams, transport_conn_pool->last_num_streams());
+    EXPECT_EQ(kTests[i].num_streams, transport_conn_pool->last_num_streams());
+    EXPECT_EQ(GetGroupName(kTests[i]), transport_conn_pool->last_group_name());
   }
 }
 
@@ -614,21 +614,14 @@
             session_deps.transport_security_state.get(),
             session_deps.cert_transparency_verifier.get(),
             session_deps.ct_policy_enforcer.get());
-    CapturePreconnectsTransportSocketPool* ssl_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());
     auto mock_pool_manager = std::make_unique<MockClientSocketPoolManager>();
     mock_pool_manager->SetTransportSocketPool(transport_conn_pool);
-    mock_pool_manager->SetSSLSocketPool(ssl_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
     // the case for https://www.google.com.
     if (kTests[i].ssl)
-      EXPECT_EQ(-1, ssl_conn_pool->last_num_streams());
+      EXPECT_EQ(-1, transport_conn_pool->last_num_streams());
     else
       EXPECT_EQ(kTests[i].num_streams, transport_conn_pool->last_num_streams());
   }
@@ -1524,7 +1517,7 @@
   std::unique_ptr<HttpNetworkSession> session(
       SpdySessionDependencies::SpdyCreateSession(&session_deps));
   TransportClientSocketPool* ssl_pool =
-      session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL);
+      session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL);
 
   EXPECT_EQ(GetSocketPoolGroupCount(ssl_pool), 0);
 
@@ -1631,12 +1624,6 @@
   EXPECT_EQ(0, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -1731,12 +1718,6 @@
   EXPECT_EQ(0, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -1775,8 +1756,6 @@
   EXPECT_EQ(0, GetSpdySessionCount(session.get()));
   EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
                    HttpNetworkSession::NORMAL_SOCKET_POOL,
                    ProxyServer(ProxyServer::SCHEME_HTTP,
@@ -1925,10 +1904,6 @@
             waiter.websocket_stream()->type());
   EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -1972,10 +1947,6 @@
             waiter.websocket_stream()->type());
   EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -2017,8 +1988,6 @@
             waiter.websocket_stream()->type());
   EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSocketPoolForHTTPLikeProxy(
                    HttpNetworkSession::NORMAL_SOCKET_POOL,
                    ProxyServer(ProxyServer::SCHEME_HTTP,
@@ -2078,12 +2047,6 @@
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -2137,12 +2100,6 @@
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
   EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_FALSE(waiter.used_proxy_info().is_direct());
   EXPECT_TRUE(http_server_properties->GetSupportsSpdy(scheme_host_port));
 }
@@ -2192,7 +2149,7 @@
             ssl_params),
         MEDIUM, SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
         callback.callback(),
-        session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL),
+        session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL),
         NetLogWithSource());
     rv = callback.GetResult(rv);
     handles.push_back(std::move(connection));
@@ -2200,9 +2157,10 @@
 
   // Releases handles now, and these sockets should go into the socket pool.
   handles.clear();
-  EXPECT_EQ(kNumIdleSockets,
-            session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
-                ->IdleSocketCount());
+  EXPECT_EQ(
+      kNumIdleSockets,
+      session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
+          ->IdleSocketCount());
 
   // Request two streams at once and make sure they use the same connection.
   HttpRequestInfo request_info;
@@ -2234,8 +2192,9 @@
   ASSERT_NE(waiter1.stream(), waiter2.stream());
 
   // Establishing the SpdySession will close idle H2 sockets.
-  EXPECT_EQ(0, session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
-                   ->IdleSocketCount());
+  EXPECT_EQ(
+      0, session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
+             ->IdleSocketCount());
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
 }
 
@@ -2295,8 +2254,9 @@
   ASSERT_NE(waiter1.stream(), waiter2.stream());
 
   // Establishing the SpdySession will close the extra H2 socket.
-  EXPECT_EQ(0, session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
-                   ->IdleSocketCount());
+  EXPECT_EQ(
+      0, session->GetTransportSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL)
+             ->IdleSocketCount());
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
   EXPECT_TRUE(data0.AllReadDataConsumed());
   EXPECT_TRUE(data1.AllReadDataConsumed());
@@ -2340,12 +2300,6 @@
   ASSERT_TRUE(waiter.bidirectional_stream_impl());
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -2554,12 +2508,6 @@
   EXPECT_EQ("200", delegate.response_headers().find(":status")->second);
   EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -2689,12 +2637,6 @@
   // There is no Http2 socket pool.
   EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session()->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_TRUE(waiter.used_proxy_info().is_direct());
 }
 
@@ -2739,12 +2681,6 @@
   ASSERT_FALSE(waiter.bidirectional_stream_impl());
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
 }
 
 #if defined(OS_ANDROID)
@@ -2814,16 +2750,8 @@
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
   // Verify socket tagged appropriately.
   EXPECT_TRUE(tag1 == socket_factory->GetLastProducedTCPSocket()->tag());
   EXPECT_TRUE(
@@ -2845,16 +2773,8 @@
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
   // Verify socket tagged appropriately.
   EXPECT_TRUE(tag2 == socket_factory->GetLastProducedTCPSocket()->tag());
   EXPECT_TRUE(
@@ -2876,16 +2796,8 @@
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
 }
 
 // Verify HttpStreamFactory::Job passes socket tag along properly to QUIC
@@ -3092,16 +3004,8 @@
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
   // Verify socket tagged appropriately.
   MockTaggingStreamSocket* socket = socket_factory->GetLastProducedTCPSocket();
   EXPECT_TRUE(tag1 == socket->tag());
@@ -3122,16 +3026,8 @@
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
   // Verify no new sockets created.
   EXPECT_EQ(socket, socket_factory->GetLastProducedTCPSocket());
   // Verify socket tag changed.
@@ -3162,16 +3058,8 @@
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
   // Verify no new sockets created.
   EXPECT_EQ(socket, socket_factory->GetLastProducedTCPSocket());
   // Verify socket tag changed.
@@ -3202,16 +3090,8 @@
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
   // Verify a new socket was created.
   MockTaggingStreamSocket* socket2 = socket_factory->GetLastProducedTCPSocket();
   EXPECT_NE(socket, socket2);
@@ -3296,16 +3176,8 @@
   EXPECT_EQ(1, GetSpdySessionCount(session.get()));
   EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(1, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(1, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
 
   // Open another session to same IP but with different privacy mode.
   StreamRequestWaiter waiter2;
@@ -3323,16 +3195,8 @@
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
   EXPECT_EQ(2, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
 
   // Open a third session that IP aliases first session.
   StreamRequestWaiter waiter3;
@@ -3353,16 +3217,8 @@
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
   EXPECT_EQ(2, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
 
   // Open a fourth session that IP aliases the second session.
   StreamRequestWaiter waiter4;
@@ -3382,16 +3238,8 @@
   EXPECT_EQ(2, GetSpdySessionCount(session.get()));
   EXPECT_EQ(2, GetSocketPoolGroupCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetTransportSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
-  EXPECT_EQ(0, GetSocketPoolGroupCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::WEBSOCKET_SOCKET_POOL)));
   EXPECT_EQ(2, GetHandedOutSocketCount(session->GetTransportSocketPool(
                    HttpNetworkSession::NORMAL_SOCKET_POOL)));
-  EXPECT_EQ(2, GetHandedOutSocketCount(session->GetSSLSocketPool(
-                   HttpNetworkSession::NORMAL_SOCKET_POOL)));
 }
 
 }  // namespace
diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json
index 1eb2cd3..750bd1a3 100644
--- a/net/http/transport_security_state_static.json
+++ b/net/http/transport_security_state_static.json
@@ -4763,7 +4763,6 @@
     { "name": "sumoscout.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sweetstreats.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "sykepleien.no", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "synchtu.be", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tacticalsquare.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tazj.in", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "tdelmas.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -5241,7 +5240,6 @@
     { "name": "maxwell-english.co.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mazz-tech.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mcc.re", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "mcgarderen.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mcpart.land", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "medirich.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "medo64.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -7533,7 +7531,6 @@
     { "name": "ts2.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ulmo.dk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "umidev.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "undernet.uy", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "upboard.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ur-lauber.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "usakitchensandflooring.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -11277,13 +11274,11 @@
     { "name": "bacontreeconsulting.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "azino777.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "badoo.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "azazy.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ballmerpeak.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bandb.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bangzafran.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "barrett.ag", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "baffinlee.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bcswampcabins.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bancoctt.pt", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bcchack.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "barbate.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -11320,17 +11315,13 @@
     { "name": "booked.holiday", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bourasse.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bodrumfarm.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "boris64.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "boxing-austria.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "btio.pw", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "btcontract.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "brilliantbuilders.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "brigidaarie.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bubulazy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "bubulazi.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "brefy.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "boxpirates.to", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "businessloanconnection.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "budaev-shop.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "buhler.pro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "callhub.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -11414,7 +11405,6 @@
     { "name": "cross-view.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cuonic.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cup.al", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "danielheal.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "creativephysics.ml", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dad256.tk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cujba.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -18198,7 +18188,6 @@
     { "name": "brooklynrealestateblog.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "benjamin-suess.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "btorrent.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "branw.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "berlin.dating", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "black-octopus.ru", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "bonqoeur.ca", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -20824,7 +20813,6 @@
     { "name": "crashsec.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "commoncode.com.au", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "childrendeservebetter.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "comfintouch.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "citylights.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cinq-elements.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "cpy.pt", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -20988,7 +20976,6 @@
     { "name": "csgoshifter.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "demarle.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dracisvet.cz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "earvinkayonga.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "educationevolving.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dprb.biz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "doli.se", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -21045,7 +21032,6 @@
     { "name": "dungeon-bbs.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "elektro-woerdehoff.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "energyled.com.br", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "enginsight.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ericjohnltd.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "erwinvanlonden.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "erinn.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -21199,7 +21185,6 @@
     { "name": "funideas.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "froggitt.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fritteli.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "freend.me", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "furnfurs.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "freebetoffers.co.uk", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "fstfy.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -21345,7 +21330,6 @@
     { "name": "greatsong.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "helpmij.cf", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "holoxplor.space", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "harschnitz.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "haus-garten-test.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hardyboyplant.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "heckelektro.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -23567,7 +23551,6 @@
     { "name": "lickthesalt.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lihaul.dnsalias.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lily-bearing.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "lindy.co", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "litcomphonors.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "litemind.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "lithan.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -25237,7 +25220,6 @@
     { "name": "equalcloud.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "equipedefrance.tv", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "er.tl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "erikwalther.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "erinaceinae.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "eriser.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "erotic4me.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -26476,7 +26458,6 @@
     { "name": "mstdn.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mstdn.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mstdn.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "mstdn.onl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mtb.wtf", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mtd.ovh", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "mtdn.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -29060,7 +29041,6 @@
     { "name": "dugnet.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dugnet.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dontpayfull.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "dugnet.io", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "droidim.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dugnet.tech", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dr-becarelli-philippe.chirurgiens-dentistes.fr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -29924,7 +29904,6 @@
     { "name": "kissgyms.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kosaki.moe", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kevyn.lu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "kanzlei-wirtschaftsrecht.berlin", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "kogak.ninja", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "katata-kango.ac.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "konosuke.jp", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -35032,7 +35011,6 @@
     { "name": "highlatitudestravel.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hill.selfip.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hippo.ge", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "hlacosedora.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hoast.xyz", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "hoflerlawfirm.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "holistichealer.in", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -35571,7 +35549,6 @@
     { "name": "mysize-condooms.nl", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nabaleka.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "naturalspacesdomes.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "newbietech.cn", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "nextrobotics.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "novelabs.eu", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "o-sp.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -35906,7 +35883,6 @@
     { "name": "chocolatier-tristan.ch", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "chowii.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "christian-liebel.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "christiancleva.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "christianfaq.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "christopher-simon.de", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "chupadelfrasco.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -36074,7 +36050,6 @@
     { "name": "edenvalerubbleremovals.co.za", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "edhesive.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "edstep.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "eggert.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ejusu.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "ekobudisantoso.net", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "eldrid.ge", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -37528,7 +37503,6 @@
     { "name": "datenschutzhelden.org", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dekasiba.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "dellipaoli.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
-    { "name": "dentfix.ro", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "depotsquarekerrville.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "depthe.gr", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
     { "name": "di2pra.com", "policy": "bulk-18-weeks", "mode": "force-https", "include_subdomains": true },
@@ -39862,7 +39836,6 @@
     { "name": "fashion24.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "feuerwehr-heiligenberg.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fialat.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "figinstitute.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "finch.am", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "findthere.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fireworksshowvr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -41357,7 +41330,6 @@
     { "name": "robotattack.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rockthebabybump.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rubenkruisselbrink.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "ruri.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "rybox.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "safeinfra.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "safesecret.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -44053,7 +44025,6 @@
     { "name": "latable-bowling-vire.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "latiendadelbebefeliz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "latintoy.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "launayflorian.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "leadinfo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "led-tl-wereld.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ledscontato.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -47367,7 +47338,6 @@
     { "name": "mapeo.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marclay.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mariereichl.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "marketinggenerators.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "marshallwilson.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mateuszpilszek.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "mattatoio.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -47926,7 +47896,6 @@
     { "name": "faraonplay8.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "favirei.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "feelmom.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "feuerwehr-offenbach-bieber.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fibretv.co.nz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "fibretv.tv", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "files.from-me.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -54477,7 +54446,6 @@
     { "name": "electricfencingballito.co.za", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "elektro-doerr.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "eluvio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "elvn.tokyo", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "embudospro.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "ememsei.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "emvoice.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -66256,7 +66224,6 @@
     { "name": "yachtlettering.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yateshomesales.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yesornut.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
-    { "name": "yolandgao.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "yuhindo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zadania.wiki", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zeno-dev.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
@@ -66264,6 +66231,372 @@
     { "name": "zhina.wiki", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zistemo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     { "name": "zxssl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1236.be", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "162632.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "1plus-agency.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3d1t0r4.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "3xm.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "42l.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4o5.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4x4-27mc.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "4x4coatingen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "7milesglobal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "80bin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ac-elektro.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "accessgaragedoors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "acinq.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "admind.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adv.cr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "adventurecreators.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "airport-charlotte.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aivan.ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "amir-heinisch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "andrewtasso.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "apviz.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "archivium.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ardadanal.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "arnaudlanna.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "asylbarn.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "aulica-conseil.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "avelinodiaz.gal", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "baileybae.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bee-removal-dublin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "benediktgeissler.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bereginy.com.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bettercareclinic.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bhserralheria.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "biomathalliance.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bjoe2k4.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "blopezabogado.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bluicraft.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bluinet.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bodemplaten4x4.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "boutoncoupdepoing.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bryantzheng.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bt780.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "budolangnau.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "bytegoing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "caffeinefiend.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "camshowdir.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "camshowhub.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "camshowstorage.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "camshowverse.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "car-insurance-quotes.biz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cardioc.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "casjenprome.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chainels.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chateroids.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cheem.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chimho.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "chrisgruber.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cinicloud.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ciniticket.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cloneuniverse.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cna5.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "coffeist.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "complexorganization.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "continuumrecoverycenter.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "contunda.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "conxcon.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "crimbotrees.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cristoraciones.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cxfinancia.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "cyberdean.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "d9c.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "damianus.hr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dashdrive.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "data.bayern", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dermopigmentista.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "diethood.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "distinctdesign2009.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "diysec.tk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dizzie.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dizzieforums.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dnastatic.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "doc.ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "doeren.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dommelschbierfusten.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "domop.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "domop.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "domovitae.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "domovitae.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "donation.ph", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "dormkitty.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "draadloos-besturen.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drphillipsmwc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "drumlines.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "duesterhus.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edas.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "edv-ringhofer.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ekeblock.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ekpj.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elitepaintingsa.com.au", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "elon-musk.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "emergeandsee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "energysolutionstech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "esu.wiki", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "etnoria.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "evoting-test.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "farvisun.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "feestbierfusten.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "firstnetworksouth.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "firstsecurity.cl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "foroaranda.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "foxbnc.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "foyer-laique-segre.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frankfurt-coworking.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "frietzombie.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "funkfernbedienung-industrie.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "funknotaus.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "galvingao.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gitecolombedesbois.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "githubapp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "goeb.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "goeb.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "gooroosmarketplace.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "grid.studio", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hammerpondkennels.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "handicaps-ensemble.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hansgoes.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hansminten.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hauora.fyi", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "healike.hk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hemkoll.nu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "heretic-guild.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hguandl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "historiasdepueblo.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hktech.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "homeland.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hoxo.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "hrebecek.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "iinix.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ikparis.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "industrial-remote-control.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infoteka.pw", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "infravoce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "intellihr.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "isdr-bukavu.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jaamaa.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "janz.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jcvidroseespelhos.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jeremy.codes", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "joelving.dk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jogjacar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "jungidee.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "junta.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "juyunce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kappharn.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "karenwillisholmes.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "khg-orchester.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kibbesfusion.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kindesfreude.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kolektivbrand.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "krrn.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kt3i.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "kys.host", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "laceysfarm.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lagranmoon.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leaseourthings.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leeks.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "legoutcheznous.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "legyenkianegykereked.hu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "leignier.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lenostech.gr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lepourquoiducomment.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "les-explos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "letsprint3d.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "liaronce.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lieren4x4.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "life29.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lifewithdyna.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "littlebirds.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lkbk.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "loca-voiture.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "locald.at", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "loqyu.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lovebeingsexy.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lowbidders.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "lucid-reality.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "luda.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ludum-polus.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "luxecalendar.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "machcz.eu", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "malkoun.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mandiblackburnphoto.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mathes.berlin", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "medsblalabs.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "meidev.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "melefo.ddns.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "minapin.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "minhyukpark.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "miniaturepets.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mirrordream.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mobl.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "moego.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "moki.org.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monerogamez.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "monitoringd.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mozilla-hispano.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mpu-ibbi.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mrhee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "muffs.ru", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mussalains.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mxdvl.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mycamshowhub.to", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "mystagic.cloud", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "neat-patch.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "neckbeard.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nelflex.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "neosey.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "neppglobal.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "newflavor.design", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nishimebistro.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nothingprivate.ml", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "nowarning.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ntcp.ph", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "okada-touki.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "opinio.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "opus-consulting.no", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "overnightglasses.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pandaltd.nl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "parnizaziteksasko.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "patrol-x.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "payments.gy", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pc-warriors.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pc28yc.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "pcmobile.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "petaouchnok.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "petrsvec.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "phils1990.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "phographer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "plantdaddie.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "practixdevelopment.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "praderarestaurant.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "prajwal-koirala.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "praxistipp24.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "premierdisco.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "premiermaldives.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "puertasautomaticasgi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "quaxio.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "queropescar.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "radiocommande-industrielle.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rajastore.ma", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ranasinha.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ravenrockrp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reactivemarkets.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reformation.financial", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "reitstall-goettingen.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "relvan.tech", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "remeb.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "remedyrecoverymat.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "riskcategory.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ristorantesamarkand.it", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "robertsonsalts.info", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "robsalmon.me.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "root-login.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "rp2018.co.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "s92.io", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "s92.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "safetyrange.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sajjadzaidi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "saksonski-szlak-parowozow.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sancdz.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sandrabernardo.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "scalpel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "schoenstatt-fathers.us", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sestolab.pp.ua", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sharecrypted.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shelike.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shellcode.com.br", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shimonfly.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shiqi.online", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shiqi1.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "shoes-mori.co.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sich-fight.club", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sigcafe.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "simonmanuel.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sipyuru.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sistemhane.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sitanleta.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sloanrealtygroup.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "socialtraderpartner.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sofgen.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sos.vg", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sos.yt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "speeder.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "speeders.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "spirit-of-sahara.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ssld.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "steam-route-saxony.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sticky.ai", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stmarkcharlotte.org", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stoneproperty.ie", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stopoverconnections.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "stopthinkconnect.jp", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "suncity288.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "suncity818.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "surmountsoft.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "sweetbabyjesus.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "symposium.beer", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "teamtmgb.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "techgadgetry.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tekanswer.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "termee.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tessierashpool.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "texasabrasiveblasting.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tharuka-app.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tharuka.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tharuka.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thebiggive.org.uk", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thehardylawfirm.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "theshots.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "thetassos.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tinapoethe.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tipocloud.cf", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "travelmexico42.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "trueseeing.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tubebegana.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "twmartin.codes", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "tziyona.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "uns.ac.id", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unti.me", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "unusedrooms.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vairuok.lt", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "verlagdrkovac.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "view-page-source.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vincent-haupert.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vitlproducts.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vnctdj.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vsoy.co.th", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "vvave.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "waterseal.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wayuanma.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wbinnssmith.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webetnet.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webgeneric.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webgeneric.in", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webgeneric.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webhopp.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webstaff.xyz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "webtoro.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weedelec.pl", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weeka.cc", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weknowhowtodoit.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "weltderangebote.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "whatsapp.net", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "wyatttauber.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xgadget.de", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xms66.top", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--12cg9bnm5ci2ag9hbcs17a.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "xn--pn1am9c.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yogaemmental.ch", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "youhabitat.es", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yourpersonalfrance.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "ypfr.fr", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yunsoupian.vip", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "yuucchi.com", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zeit.co", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
+    { "name": "zstgmnachod.cz", "policy": "bulk-1-year", "mode": "force-https", "include_subdomains": true },
     // END OF 1-YEAR BULK HSTS ENTRIES
 
     // Only eTLD+1 domains can be submitted automatically to hstspreload.org,
diff --git a/net/proxy_resolution/pac_library_unittest.cc b/net/proxy_resolution/pac_library_unittest.cc
index 0c44e91..e78b84b 100644
--- a/net/proxy_resolution/pac_library_unittest.cc
+++ b/net/proxy_resolution/pac_library_unittest.cc
@@ -261,6 +261,14 @@
     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,
diff --git a/net/quic/quic_flags_list.h b/net/quic/quic_flags_list.h
index 384214a..f0b9217 100644
--- a/net/quic/quic_flags_list.h
+++ b/net/quic/quic_flags_list.h
@@ -160,7 +160,7 @@
 // If true, increase size of random bytes in IETF stateless reset packet.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_more_random_bytes_in_stateless_reset,
-          false)
+          true)
 
 // If true, use new, lower-overhead implementation of LRU cache for compressed
 // certificates.
@@ -237,7 +237,7 @@
 
 // If true, make GeneralLossAlgorithm::DetectLosses faster by never rescanning
 // the same packet in QuicUnackedPacketMap.
-QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_faster_detect_loss, false)
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_faster_detect_loss, true)
 
 // If true, use common code for checking whether a new stream ID may be
 // allocated.
@@ -315,7 +315,7 @@
 // type of the next successfully added frame.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_set_transmission_type_for_next_frame,
-          false)
+          true)
 // If true, always send connection close/reset for IETF connections.
 QUIC_FLAG(bool,
           FLAGS_quic_reloadable_flag_quic_always_reset_ietf_connections,
@@ -342,3 +342,11 @@
     bool,
     FLAGS_quic_reloadable_flag_quic_close_connection_with_zero_least_unacked_stop_waiting,
     true)
+
+// If true, QuicCryptoServerConfig will correctly rotate configs based on
+// primary time.
+QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_config_rotation, false)
+
+// If true, use numeric_limits<uint64_t>::max() to represent uninitialized
+// packet number.
+QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_uint64max_uninitialized_pn, false)
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 7214cd9..3cba8fb 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -411,7 +411,7 @@
       QuicChromiumClientSession* session = session_;
       session_ = nullptr;
       session->CloseSessionOnErrorLater(
-          ERR_ABORTED, quic::QUIC_CONNECTION_CANCELLED,
+          ERR_ABORTED, quic::QUIC_STALE_CONNECTION_CANCELLED,
           quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
     }
   }
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index 3dde527e..0609b5a 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -9141,8 +9141,9 @@
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddWrite(
-      SYNCHRONOUS, client_maker_.MakeConnectionClosePacket(
-                       2, true, quic::QUIC_CONNECTION_CANCELLED, "net error"));
+      SYNCHRONOUS,
+      client_maker_.MakeConnectionClosePacket(
+          2, true, quic::QUIC_STALE_CONNECTION_CANCELLED, "net error"));
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
   // Socket for the new connection.
@@ -9214,8 +9215,9 @@
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddWrite(
-      SYNCHRONOUS, client_maker_.MakeConnectionClosePacket(
-                       2, true, quic::QUIC_CONNECTION_CANCELLED, "net error"));
+      SYNCHRONOUS,
+      client_maker_.MakeConnectionClosePacket(
+          2, true, quic::QUIC_STALE_CONNECTION_CANCELLED, "net error"));
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
   MockQuicData quic_data2;
@@ -9287,8 +9289,9 @@
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_INITIAL);
   quic_data.AddWrite(
-      SYNCHRONOUS, client_maker_.MakeConnectionClosePacket(
-                       1, true, quic::QUIC_CONNECTION_CANCELLED, "net error"));
+      SYNCHRONOUS,
+      client_maker_.MakeConnectionClosePacket(
+          1, true, quic::QUIC_STALE_CONNECTION_CANCELLED, "net error"));
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
   MockQuicData quic_data2;
@@ -9408,8 +9411,9 @@
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   quic_data.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket());
   quic_data.AddWrite(
-      SYNCHRONOUS, client_maker_.MakeConnectionClosePacket(
-                       2, true, quic::QUIC_CONNECTION_CANCELLED, "net error"));
+      SYNCHRONOUS,
+      client_maker_.MakeConnectionClosePacket(
+          2, true, quic::QUIC_STALE_CONNECTION_CANCELLED, "net error"));
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
   QuicStreamRequest request(factory_.get());
@@ -9628,8 +9632,9 @@
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_INITIAL);
   quic_data.AddWrite(
-      SYNCHRONOUS, client_maker_.MakeConnectionClosePacket(
-                       1, true, quic::QUIC_CONNECTION_CANCELLED, "net error"));
+      SYNCHRONOUS,
+      client_maker_.MakeConnectionClosePacket(
+          1, true, quic::QUIC_STALE_CONNECTION_CANCELLED, "net error"));
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
   QuicStreamRequest request(factory_.get());
@@ -9680,8 +9685,9 @@
   quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);
   client_maker_.SetEncryptionLevel(quic::ENCRYPTION_INITIAL);
   quic_data.AddWrite(
-      SYNCHRONOUS, client_maker_.MakeConnectionClosePacket(
-                       1, true, quic::QUIC_CONNECTION_CANCELLED, "net error"));
+      SYNCHRONOUS,
+      client_maker_.MakeConnectionClosePacket(
+          1, true, quic::QUIC_STALE_CONNECTION_CANCELLED, "net error"));
   quic_data.AddSocketDataToFactory(socket_factory_.get());
 
   QuicStreamRequest request(factory_.get());
diff --git a/net/socket/client_socket_factory.cc b/net/socket/client_socket_factory.cc
index 66a2d45..62c1579 100644
--- a/net/socket/client_socket_factory.cc
+++ b/net/socket/client_socket_factory.cc
@@ -53,6 +53,15 @@
         std::move(transport_socket), host_and_port, ssl_config, context));
   }
 
+  std::unique_ptr<SSLClientSocket> CreateSSLClientSocket(
+      std::unique_ptr<StreamSocket> nested_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::unique_ptr<ProxyClientSocket> CreateProxyClientSocket(
       std::unique_ptr<ClientSocketHandle> transport_socket,
       const std::string& user_agent,
diff --git a/net/socket/client_socket_factory.h b/net/socket/client_socket_factory.h
index 4d72619..ad90eaa 100644
--- a/net/socket/client_socket_factory.h
+++ b/net/socket/client_socket_factory.h
@@ -53,11 +53,20 @@
   // 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.
   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,
+      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,
diff --git a/net/socket/client_socket_pool_base_unittest.cc b/net/socket/client_socket_pool_base_unittest.cc
index 8816f82a1..540945e 100644
--- a/net/socket/client_socket_pool_base_unittest.cc
+++ b/net/socket/client_socket_pool_base_unittest.cc
@@ -240,6 +240,16 @@
     NOTIMPLEMENTED();
     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,
diff --git a/net/socket/client_socket_pool_manager.cc b/net/socket/client_socket_pool_manager.cc
index c0f628c2..cbb30de 100644
--- a/net/socket/client_socket_pool_manager.cc
+++ b/net/socket/client_socket_pool_manager.cc
@@ -199,7 +199,7 @@
         ssl_config_for_origin, privacy_mode);
     TransportClientSocketPool* ssl_pool = nullptr;
     if (proxy_info.is_direct()) {
-      ssl_pool = session->GetSSLSocketPool(socket_pool_type);
+      ssl_pool = session->GetTransportSocketPool(socket_pool_type);
     } else {
       ssl_pool = session->GetSocketPoolForSSLWithProxy(
           socket_pool_type, proxy_info.proxy_server());
diff --git a/net/socket/client_socket_pool_manager.h b/net/socket/client_socket_pool_manager.h
index b65dcaa..3e8c651 100644
--- a/net/socket/client_socket_pool_manager.h
+++ b/net/socket/client_socket_pool_manager.h
@@ -77,8 +77,8 @@
 
   virtual void FlushSocketPoolsWithError(int error) = 0;
   virtual void CloseIdleSockets() = 0;
+  // Returns the socket pool for direct HTTP and SSL connections.
   virtual TransportClientSocketPool* GetTransportSocketPool() = 0;
-  virtual TransportClientSocketPool* GetSSLSocketPool() = 0;
   virtual TransportClientSocketPool* GetSocketPoolForSOCKSProxy(
       const ProxyServer& socks_proxy) = 0;
   // Returns the HttpProxyClientSocketPool for a ProxyServer that uses an
diff --git a/net/socket/client_socket_pool_manager_impl.cc b/net/socket/client_socket_pool_manager_impl.cc
index b9801a7..d6eaec05 100644
--- a/net/socket/client_socket_pool_manager_impl.cc
+++ b/net/socket/client_socket_pool_manager_impl.cc
@@ -102,24 +102,7 @@
                                        ssl_config_service,
                                        socket_performance_watcher_factory_,
                                        network_quality_estimator,
-                                       net_log)),
-      ssl_socket_pool_(
-          new TransportClientSocketPool(max_sockets_per_pool(pool_type),
-                                        max_sockets_per_group(pool_type),
-                                        socket_factory_,
-                                        host_resolver,
-                                        cert_verifier,
-                                        channel_id_service,
-                                        transport_security_state,
-                                        cert_transparency_verifier,
-                                        ct_policy_enforcer,
-                                        ssl_client_session_cache,
-                                        ssl_session_cache_shard_,
-                                        ssl_config_service,
-                                        socket_performance_watcher_factory_,
-                                        network_quality_estimator,
-                                        net_log,
-                                        transport_socket_pool_.get())) {
+                                       net_log)) {
   CertDatabase::GetInstance()->AddObserver(this);
 }
 
@@ -144,10 +127,6 @@
     it.second->FlushWithError(error);
   }
 
-  for (const auto& it : transport_socket_pools_for_https_proxies_) {
-    it.second->FlushWithError(error);
-  }
-
   for (const auto& it : transport_socket_pools_for_http_proxies_) {
     it.second->FlushWithError(error);
   }
@@ -156,7 +135,6 @@
     it.second->FlushWithError(error);
   }
 
-  ssl_socket_pool_->FlushWithError(error);
   transport_socket_pool_->FlushWithError(error);
 }
 
@@ -175,10 +153,6 @@
     it.second->CloseIdleSockets();
   }
 
-  for (const auto& it : transport_socket_pools_for_https_proxies_) {
-    it.second->CloseIdleSockets();
-  }
-
   for (const auto& it : transport_socket_pools_for_http_proxies_) {
     it.second->CloseIdleSockets();
   }
@@ -187,7 +161,6 @@
     it.second->CloseIdleSockets();
   }
 
-  ssl_socket_pool_->CloseIdleSockets();
   transport_socket_pool_->CloseIdleSockets();
 }
 
@@ -196,10 +169,6 @@
   return transport_socket_pool_.get();
 }
 
-TransportClientSocketPool* ClientSocketPoolManagerImpl::GetSSLSocketPool() {
-  return ssl_socket_pool_.get();
-}
-
 TransportClientSocketPool*
 ClientSocketPoolManagerImpl::GetSocketPoolForSOCKSProxy(
     const ProxyServer& proxy_server) {
@@ -239,16 +208,12 @@
   if (it != http_proxy_socket_pools_.end()) {
     DCHECK(base::ContainsKey(transport_socket_pools_for_http_proxies_,
                              http_proxy));
-    DCHECK(base::ContainsKey(transport_socket_pools_for_https_proxies_,
-                             http_proxy));
     DCHECK(base::ContainsKey(ssl_socket_pools_for_https_proxies_, http_proxy));
     return it->second.get();
   }
 
   DCHECK(
       !base::ContainsKey(transport_socket_pools_for_http_proxies_, http_proxy));
-  DCHECK(!base::ContainsKey(transport_socket_pools_for_https_proxies_,
-                            http_proxy));
   DCHECK(!base::ContainsKey(ssl_socket_pools_for_https_proxies_, http_proxy));
 
   int sockets_per_proxy_server = max_sockets_per_proxy_server(pool_type_);
@@ -268,19 +233,6 @@
               net_log_)));
   DCHECK(tcp_http_ret.second);
 
-  std::pair<TransportSocketPoolMap::iterator, bool> tcp_https_ret =
-      transport_socket_pools_for_https_proxies_.insert(std::make_pair(
-          http_proxy,
-          std::make_unique<TransportClientSocketPool>(
-              sockets_per_proxy_server, sockets_per_group, socket_factory_,
-              host_resolver_, cert_verifier_, channel_id_service_,
-              transport_security_state_, cert_transparency_verifier_,
-              ct_policy_enforcer_, ssl_client_session_cache_,
-              ssl_session_cache_shard_, ssl_config_service_,
-              socket_performance_watcher_factory_, network_quality_estimator_,
-              net_log_)));
-  DCHECK(tcp_https_ret.second);
-
   std::pair<TransportSocketPoolMap::iterator, bool> ssl_https_ret =
       ssl_socket_pools_for_https_proxies_.insert(std::make_pair(
           http_proxy,
@@ -291,9 +243,9 @@
               ct_policy_enforcer_, ssl_client_session_cache_,
               ssl_session_cache_shard_, ssl_config_service_,
               socket_performance_watcher_factory_, network_quality_estimator_,
-              net_log_, tcp_https_ret.first->second.get() /* https proxy */,
-              nullptr /* no socks proxy */, nullptr /* no http proxy */)));
-  DCHECK(tcp_https_ret.second);
+              net_log_, nullptr /* no socks proxy */,
+              nullptr /* no http proxy */)));
+  DCHECK(ssl_https_ret.second);
 
   std::pair<HTTPProxySocketPoolMap::iterator, bool> ret =
       http_proxy_socket_pools_.insert(std::make_pair(
@@ -328,7 +280,7 @@
               ct_policy_enforcer_, ssl_client_session_cache_,
               ssl_session_cache_shard_, ssl_config_service_,
               socket_performance_watcher_factory_, network_quality_estimator_,
-              net_log_, nullptr, /* no tcp pool, we always go through a proxy */
+              net_log_,
               proxy_server.is_socks() ? GetSocketPoolForSOCKSProxy(proxy_server)
                                       : nullptr,
               proxy_server.is_http_like()
@@ -344,12 +296,6 @@
   list->Append(transport_socket_pool_->GetInfoAsValue("transport_socket_pool",
                                                 "transport_socket_pool",
                                                 false));
-  // Third parameter is false because |ssl_socket_pool_| uses
-  // |transport_socket_pool_| internally, and do not want to add it a second
-  // time.
-  list->Append(ssl_socket_pool_->GetInfoAsValue("ssl_socket_pool",
-                                                "ssl_socket_pool",
-                                                false));
   AddSocketPoolsToList(list.get(), http_proxy_socket_pools_,
                        "http_proxy_socket_pool", true);
   AddSocketPoolsToList(list.get(), proxy_socket_pools_, "proxy_socket_pools",
@@ -369,7 +315,8 @@
 void ClientSocketPoolManagerImpl::DumpMemoryStats(
     base::trace_event::ProcessMemoryDump* pmd,
     const std::string& parent_dump_absolute_name) const {
-  return ssl_socket_pool_->DumpMemoryStats(pmd, parent_dump_absolute_name);
+  return transport_socket_pool_->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 9b87884..4796e94 100644
--- a/net/socket/client_socket_pool_manager_impl.h
+++ b/net/socket/client_socket_pool_manager_impl.h
@@ -71,8 +71,6 @@
 
   TransportClientSocketPool* GetTransportSocketPool() override;
 
-  TransportClientSocketPool* GetSSLSocketPool() override;
-
   TransportClientSocketPool* GetSocketPoolForSOCKSProxy(
       const ProxyServer& proxy_server) override;
 
@@ -117,7 +115,6 @@
   // Note: this ordering is important.
 
   std::unique_ptr<TransportClientSocketPool> transport_socket_pool_;
-  std::unique_ptr<TransportClientSocketPool> ssl_socket_pool_;
 
   // Currently only contains socket pools for SOCKS proxies (With SSL over SOCKS
   // connections layered on top of it, and appearing in
@@ -126,7 +123,6 @@
   TransportSocketPoolMap proxy_socket_pools_;
 
   TransportSocketPoolMap transport_socket_pools_for_http_proxies_;
-  TransportSocketPoolMap transport_socket_pools_for_https_proxies_;
   TransportSocketPoolMap ssl_socket_pools_for_https_proxies_;
   HTTPProxySocketPoolMap http_proxy_socket_pools_;
   TransportSocketPoolMap ssl_socket_pools_for_proxies_;
diff --git a/net/socket/fuzzed_socket_factory.cc b/net/socket/fuzzed_socket_factory.cc
index 21cd1656..5003a10 100644
--- a/net/socket/fuzzed_socket_factory.cc
+++ b/net/socket/fuzzed_socket_factory.cc
@@ -152,6 +152,14 @@
   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,
diff --git a/net/socket/fuzzed_socket_factory.h b/net/socket/fuzzed_socket_factory.h
index 3f179727..695bbbcc 100644
--- a/net/socket/fuzzed_socket_factory.h
+++ b/net/socket/fuzzed_socket_factory.h
@@ -52,6 +52,12 @@
       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,
diff --git a/net/socket/mock_client_socket_pool_manager.cc b/net/socket/mock_client_socket_pool_manager.cc
index 5e56681..7f5ae755 100644
--- a/net/socket/mock_client_socket_pool_manager.cc
+++ b/net/socket/mock_client_socket_pool_manager.cc
@@ -18,11 +18,6 @@
   transport_socket_pool_.reset(pool);
 }
 
-void MockClientSocketPoolManager::SetSSLSocketPool(
-    TransportClientSocketPool* pool) {
-  ssl_socket_pool_.reset(pool);
-}
-
 void MockClientSocketPoolManager::SetSocketPoolForProxy(
     const ProxyServer& proxy_server,
     std::unique_ptr<TransportClientSocketPool> pool) {
@@ -55,10 +50,6 @@
   return transport_socket_pool_.get();
 }
 
-TransportClientSocketPool* MockClientSocketPoolManager::GetSSLSocketPool() {
-  return ssl_socket_pool_.get();
-}
-
 TransportClientSocketPool*
 MockClientSocketPoolManager::GetSocketPoolForSOCKSProxy(
     const ProxyServer& proxy_server) {
diff --git a/net/socket/mock_client_socket_pool_manager.h b/net/socket/mock_client_socket_pool_manager.h
index 7fd81bf..a08fa67 100644
--- a/net/socket/mock_client_socket_pool_manager.h
+++ b/net/socket/mock_client_socket_pool_manager.h
@@ -23,7 +23,6 @@
 
   // Sets "override" socket pools that get used instead.
   void SetTransportSocketPool(TransportClientSocketPool* pool);
-  void SetSSLSocketPool(TransportClientSocketPool* pool);
   // Currently only works for SOCKS proxies.
   void SetSocketPoolForProxy(const ProxyServer& proxy_server,
                              std::unique_ptr<TransportClientSocketPool> pool);
@@ -38,7 +37,6 @@
   void FlushSocketPoolsWithError(int error) override;
   void CloseIdleSockets() override;
   TransportClientSocketPool* GetTransportSocketPool() override;
-  TransportClientSocketPool* GetSSLSocketPool() override;
   TransportClientSocketPool* GetSocketPoolForSOCKSProxy(
       const ProxyServer& socks_proxy) override;
   HttpProxyClientSocketPool* GetSocketPoolForHTTPLikeProxy(
@@ -57,7 +55,6 @@
       std::map<ProxyServer, std::unique_ptr<HttpProxyClientSocketPool>>;
 
   std::unique_ptr<TransportClientSocketPool> transport_socket_pool_;
-  std::unique_ptr<TransportClientSocketPool> ssl_socket_pool_;
   TransportClientSocketPoolMap proxy_socket_pools_;
   HTTPProxySocketPoolMap http_proxy_socket_pools_;
   TransportClientSocketPoolMap ssl_socket_pools_for_proxies_;
diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc
index f8722109..1b0db40 100644
--- a/net/socket/socket_test_util.cc
+++ b/net/socket/socket_test_util.cc
@@ -38,6 +38,7 @@
 #include "net/log/net_log_source.h"
 #include "net/log/net_log_source_type.h"
 #include "net/socket/socket.h"
+#include "net/socket/stream_socket.h"
 #include "net/socket/websocket_endpoint_lock_manager.h"
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/ssl/ssl_connection_status_flags.h"
@@ -791,6 +792,26 @@
       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,
@@ -1434,7 +1455,22 @@
     const SSLConfig& ssl_config,
     SSLSocketDataProvider* data)
     : net_log_(transport_socket->socket()->NetLog()),
-      transport_(std::move(transport_socket)),
+      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,
+    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()),
       data_(data),
       weak_factory_(this) {
   DCHECK(data_);
@@ -1448,13 +1484,13 @@
 int MockSSLClientSocket::Read(IOBuffer* buf,
                               int buf_len,
                               CompletionOnceCallback callback) {
-  return transport_->socket()->Read(buf, buf_len, std::move(callback));
+  return stream_socket_->Read(buf, buf_len, std::move(callback));
 }
 
 int MockSSLClientSocket::ReadIfReady(IOBuffer* buf,
                                      int buf_len,
                                      CompletionOnceCallback callback) {
-  return transport_->socket()->ReadIfReady(buf, buf_len, std::move(callback));
+  return stream_socket_->ReadIfReady(buf, buf_len, std::move(callback));
 }
 
 int MockSSLClientSocket::Write(
@@ -1462,16 +1498,16 @@
     int buf_len,
     CompletionOnceCallback callback,
     const NetworkTrafficAnnotationTag& traffic_annotation) {
-  return transport_->socket()->Write(buf, buf_len, std::move(callback),
-                                     traffic_annotation);
+  return stream_socket_->Write(buf, buf_len, std::move(callback),
+                               traffic_annotation);
 }
 
 int MockSSLClientSocket::CancelReadIfReady() {
-  return transport_->socket()->CancelReadIfReady();
+  return stream_socket_->CancelReadIfReady();
 }
 
 int MockSSLClientSocket::Connect(CompletionOnceCallback callback) {
-  DCHECK(transport_->socket()->IsConnected());
+  DCHECK(stream_socket_->IsConnected());
   data_->is_connect_data_consumed = true;
   if (data_->connect.result == OK)
     connected_ = true;
@@ -1483,20 +1519,20 @@
 }
 
 void MockSSLClientSocket::Disconnect() {
-  if (transport_->socket() != NULL)
-    transport_->socket()->Disconnect();
+  if (stream_socket_ != NULL)
+    stream_socket_->Disconnect();
 }
 
 bool MockSSLClientSocket::IsConnected() const {
-  return transport_->socket()->IsConnected();
+  return stream_socket_->IsConnected();
 }
 
 bool MockSSLClientSocket::IsConnectedAndIdle() const {
-  return transport_->socket()->IsConnectedAndIdle();
+  return stream_socket_->IsConnectedAndIdle();
 }
 
 bool MockSSLClientSocket::WasEverUsed() const {
-  return transport_->socket()->WasEverUsed();
+  return stream_socket_->WasEverUsed();
 }
 
 int MockSSLClientSocket::GetLocalAddress(IPEndPoint* address) const {
@@ -1505,7 +1541,7 @@
 }
 
 int MockSSLClientSocket::GetPeerAddress(IPEndPoint* address) const {
-  return transport_->socket()->GetPeerAddress(address);
+  return stream_socket_->GetPeerAddress(address);
 }
 
 bool MockSSLClientSocket::WasAlpnNegotiated() const {
@@ -1523,7 +1559,7 @@
 }
 
 void MockSSLClientSocket::ApplySocketTag(const SocketTag& tag) {
-  return transport_->socket()->ApplySocketTag(tag);
+  return stream_socket_->ApplySocketTag(tag);
 }
 
 const NetLogWithSource& MockSSLClientSocket::NetLog() const {
@@ -2083,7 +2119,7 @@
           nullptr /* cert_transparency_verifier */,
           nullptr /* ct_policy_enforcer */,
           nullptr /* ssl_client_session_cache */,
-          std::string() /* ssl_session_cache_shard */,
+          std::string() /* ssl_client_session_cache */,
           nullptr /* ssl_config_service */,
           nullptr /* socket_performance_watcher_factory */,
           nullptr /* network_quality_estimator */,
diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h
index 899543c..8031e6b 100644
--- a/net/socket/socket_test_util.h
+++ b/net/socket/socket_test_util.h
@@ -626,6 +626,11 @@
       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,
@@ -878,10 +883,15 @@
 
 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,
+                      const HostPortPair& host_and_port,
+                      const SSLConfig& ssl_config,
+                      SSLSocketDataProvider* socket);
   ~MockSSLClientSocket() override;
 
   // Socket implementation.
@@ -947,7 +957,11 @@
 
   bool connected_ = false;
   NetLogWithSource net_log_;
-  std::unique_ptr<ClientSocketHandle> transport_;
+  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_;
   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 0533c90..1acd754 100644
--- a/net/socket/ssl_client_socket_impl.cc
+++ b/net/socket/ssl_client_socket_impl.cc
@@ -414,7 +414,8 @@
       cert_verifier_(context.cert_verifier),
       cert_verification_result_(kCertVerifyPending),
       cert_transparency_verifier_(context.cert_transparency_verifier),
-      transport_(std::move(transport_socket)),
+      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),
@@ -429,7 +430,43 @@
       policy_enforcer_(context.ct_policy_enforcer),
       pkp_bypassed_(false),
       is_fatal_cert_error_(false),
-      net_log_(transport_->socket()->NetLog()),
+      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()),
+      host_and_port_(host_and_port),
+      ssl_config_(ssl_config),
+      ssl_client_session_cache_(context.ssl_client_session_cache),
+      ssl_session_cache_shard_(context.ssl_session_cache_shard),
+      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_);
@@ -516,7 +553,7 @@
   user_write_buf_ = NULL;
   user_write_buf_len_ = 0;
 
-  transport_->socket()->Disconnect();
+  stream_socket_->Disconnect();
 }
 
 // ConfirmHandshake may only be called on a connected socket and, like other
@@ -552,7 +589,7 @@
   if (user_read_buf_.get() || user_write_buf_.get())
     return true;
 
-  return transport_->socket()->IsConnected();
+  return stream_socket_->IsConnected();
 }
 
 bool SSLClientSocketImpl::IsConnectedAndIdle() const {
@@ -574,15 +611,15 @@
   if (transport_adapter_->HasPendingReadData())
     return false;
 
-  return transport_->socket()->IsConnectedAndIdle();
+  return stream_socket_->IsConnectedAndIdle();
 }
 
 int SSLClientSocketImpl::GetPeerAddress(IPEndPoint* addressList) const {
-  return transport_->socket()->GetPeerAddress(addressList);
+  return stream_socket_->GetPeerAddress(addressList);
 }
 
 int SSLClientSocketImpl::GetLocalAddress(IPEndPoint* addressList) const {
-  return transport_->socket()->GetLocalAddress(addressList);
+  return stream_socket_->GetLocalAddress(addressList);
 }
 
 const NetLogWithSource& SSLClientSocketImpl::NetLog() const {
@@ -645,7 +682,7 @@
 }
 
 int64_t SSLClientSocketImpl::GetTotalReceivedBytes() const {
-  return transport_->socket()->GetTotalReceivedBytes();
+  return stream_socket_->GetTotalReceivedBytes();
 }
 
 void SSLClientSocketImpl::DumpMemoryStats(SocketMemoryStats* stats) const {
@@ -691,7 +728,7 @@
 }
 
 void SSLClientSocketImpl::ApplySocketTag(const SocketTag& tag) {
-  return transport_->socket()->ApplySocketTag(tag);
+  return stream_socket_->ApplySocketTag(tag);
 }
 
 int SSLClientSocketImpl::Read(IOBuffer* buf,
@@ -720,7 +757,7 @@
 }
 
 int SSLClientSocketImpl::CancelReadIfReady() {
-  int result = transport_->socket()->CancelReadIfReady();
+  int result = stream_socket_->CancelReadIfReady();
   // Cancel |user_read_callback_|, because caller does not expect the callback
   // to be invoked after they have canceled the ReadIfReady.
   user_read_callback_.Reset();
@@ -750,11 +787,11 @@
 }
 
 int SSLClientSocketImpl::SetReceiveBufferSize(int32_t size) {
-  return transport_->socket()->SetReceiveBufferSize(size);
+  return stream_socket_->SetReceiveBufferSize(size);
 }
 
 int SSLClientSocketImpl::SetSendBufferSize(int32_t size) {
-  return transport_->socket()->SetSendBufferSize(size);
+  return stream_socket_->SetSendBufferSize(size);
 }
 
 void SSLClientSocketImpl::OnReadReady() {
@@ -804,7 +841,7 @@
   }
 
   transport_adapter_.reset(
-      new SocketBIOAdapter(transport_->socket(), kDefaultOpenSSLBufferSize,
+      new SocketBIOAdapter(stream_socket_, 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 0dda5dd..3148190 100644
--- a/net/socket/ssl_client_socket_impl.h
+++ b/net/socket/ssl_client_socket_impl.h
@@ -27,6 +27,7 @@
 #include "net/socket/next_proto.h"
 #include "net/socket/socket_bio_adapter.h"
 #include "net/socket/ssl_client_socket.h"
+#include "net/socket/stream_socket.h"
 #include "net/ssl/openssl_ssl_util.h"
 #include "net/ssl/ssl_client_cert_type.h"
 #include "net/ssl/ssl_config.h"
@@ -51,12 +52,21 @@
  public:
   // Takes ownership of the transport_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
+  // 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,
+                      const HostPortPair& host_and_port,
+                      const SSLConfig& ssl_config,
+                      const SSLClientSocketContext& context);
   ~SSLClientSocketImpl() override;
 
   const HostPortPair& host_and_port() const { return host_and_port_; }
@@ -261,7 +271,11 @@
   // OpenSSL stuff
   bssl::UniquePtr<SSL> ssl_;
 
-  std::unique_ptr<ClientSocketHandle> transport_;
+  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<SocketBIOAdapter> transport_adapter_;
   const HostPortPair host_and_port_;
   SSLConfig ssl_config_;
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
deleted file mode 100644
index fe44613d..0000000
--- a/net/socket/ssl_client_socket_pool.cc
+++ /dev/null
@@ -1,236 +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 "net/socket/ssl_client_socket_pool.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/trace_event/trace_event.h"
-#include "base/values.h"
-#include "net/base/host_port_pair.h"
-#include "net/base/net_errors.h"
-#include "net/http/http_proxy_client_socket_pool.h"
-#include "net/log/net_log_with_source.h"
-#include "net/socket/client_socket_handle.h"
-#include "net/socket/transport_client_socket_pool.h"
-
-namespace net {
-
-SSLClientSocketPool::SSLConnectJobFactory::SSLConnectJobFactory(
-    TransportClientSocketPool* transport_pool,
-    TransportClientSocketPool* socks_pool,
-    HttpProxyClientSocketPool* http_proxy_pool,
-    ClientSocketFactory* client_socket_factory,
-    const SSLClientSocketContext& context,
-    NetworkQualityEstimator* network_quality_estimator,
-    NetLog* net_log)
-    : transport_pool_(transport_pool),
-      socks_pool_(socks_pool),
-      http_proxy_pool_(http_proxy_pool),
-      client_socket_factory_(client_socket_factory),
-      context_(context),
-      network_quality_estimator_(network_quality_estimator),
-      net_log_(net_log) {}
-
-SSLClientSocketPool::SSLConnectJobFactory::~SSLConnectJobFactory() = default;
-
-SSLClientSocketPool::SSLClientSocketPool(
-    int max_sockets,
-    int max_sockets_per_group,
-    CertVerifier* cert_verifier,
-    ChannelIDService* channel_id_service,
-    TransportSecurityState* transport_security_state,
-    CTVerifier* cert_transparency_verifier,
-    CTPolicyEnforcer* ct_policy_enforcer,
-    SSLClientSessionCache* ssl_client_session_cache,
-    ClientSocketFactory* client_socket_factory,
-    TransportClientSocketPool* transport_pool,
-    TransportClientSocketPool* socks_pool,
-    HttpProxyClientSocketPool* http_proxy_pool,
-    SSLConfigService* ssl_config_service,
-    NetworkQualityEstimator* network_quality_estimator,
-    NetLog* net_log)
-    : transport_pool_(transport_pool),
-      socks_pool_(socks_pool),
-      http_proxy_pool_(http_proxy_pool),
-      base_(this,
-            max_sockets,
-            max_sockets_per_group,
-            ClientSocketPool::unused_idle_socket_timeout(),
-            ClientSocketPool::used_idle_socket_timeout(),
-            new SSLConnectJobFactory(
-                transport_pool,
-                socks_pool,
-                http_proxy_pool,
-                client_socket_factory,
-                SSLClientSocketContext(cert_verifier,
-                                       channel_id_service,
-                                       transport_security_state,
-                                       cert_transparency_verifier,
-                                       ct_policy_enforcer,
-                                       ssl_client_session_cache,
-                                       "shardkey"),
-                network_quality_estimator,
-                net_log)),
-      ssl_config_service_(ssl_config_service) {
-  if (ssl_config_service_)
-    ssl_config_service_->AddObserver(this);
-  if (transport_pool_)
-    base_.AddLowerLayeredPool(transport_pool_);
-  if (socks_pool_)
-    base_.AddLowerLayeredPool(socks_pool_);
-  if (http_proxy_pool_)
-    base_.AddLowerLayeredPool(http_proxy_pool_);
-}
-
-SSLClientSocketPool::~SSLClientSocketPool() {
-  if (ssl_config_service_)
-    ssl_config_service_->RemoveObserver(this);
-}
-
-std::unique_ptr<ConnectJob>
-SSLClientSocketPool::SSLConnectJobFactory::NewConnectJob(
-    const std::string& group_name,
-    const PoolBase::Request& request,
-    ConnectJob::Delegate* delegate) const {
-  return std::make_unique<SSLConnectJob>(
-      request.priority(),
-      CommonConnectJobParams(
-          group_name, request.socket_tag(),
-          request.respect_limits() == ClientSocketPool::RespectLimits::ENABLED,
-          client_socket_factory_, nullptr /* host_resolver */, context_,
-          nullptr /* socket_performance_watcher_factory */,
-          network_quality_estimator_, net_log_,
-          nullptr /* websocket_endpoint_lock_manager */),
-      request.params(), transport_pool_, socks_pool_, http_proxy_pool_,
-      delegate);
-}
-
-int SSLClientSocketPool::RequestSocket(const std::string& group_name,
-                                       const void* socket_params,
-                                       RequestPriority priority,
-                                       const SocketTag& socket_tag,
-                                       RespectLimits respect_limits,
-                                       ClientSocketHandle* handle,
-                                       CompletionOnceCallback callback,
-                                       const NetLogWithSource& net_log) {
-  const scoped_refptr<SSLSocketParams>* casted_socket_params =
-      static_cast<const scoped_refptr<SSLSocketParams>*>(socket_params);
-
-  return base_.RequestSocket(group_name, *casted_socket_params, priority,
-                             socket_tag, respect_limits, handle,
-                             std::move(callback), net_log);
-}
-
-void SSLClientSocketPool::RequestSockets(const std::string& group_name,
-                                         const void* params,
-                                         int num_sockets,
-                                         const NetLogWithSource& net_log) {
-  const scoped_refptr<SSLSocketParams>* casted_params =
-      static_cast<const scoped_refptr<SSLSocketParams>*>(params);
-
-  base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
-}
-
-void SSLClientSocketPool::SetPriority(const std::string& group_name,
-                                      ClientSocketHandle* handle,
-                                      RequestPriority priority) {
-  base_.SetPriority(group_name, handle, priority);
-}
-
-void SSLClientSocketPool::CancelRequest(const std::string& group_name,
-                                        ClientSocketHandle* handle) {
-  base_.CancelRequest(group_name, handle);
-}
-
-void SSLClientSocketPool::ReleaseSocket(const std::string& group_name,
-                                        std::unique_ptr<StreamSocket> socket,
-                                        int id) {
-  base_.ReleaseSocket(group_name, std::move(socket), id);
-}
-
-void SSLClientSocketPool::FlushWithError(int error) {
-  base_.FlushWithError(error);
-}
-
-void SSLClientSocketPool::CloseIdleSockets() {
-  base_.CloseIdleSockets();
-}
-
-void SSLClientSocketPool::CloseIdleSocketsInGroup(
-    const std::string& group_name) {
-  base_.CloseIdleSocketsInGroup(group_name);
-}
-
-int SSLClientSocketPool::IdleSocketCount() const {
-  return base_.idle_socket_count();
-}
-
-int SSLClientSocketPool::IdleSocketCountInGroup(
-    const std::string& group_name) const {
-  return base_.IdleSocketCountInGroup(group_name);
-}
-
-LoadState SSLClientSocketPool::GetLoadState(
-    const std::string& group_name, const ClientSocketHandle* handle) const {
-  return base_.GetLoadState(group_name, handle);
-}
-
-void SSLClientSocketPool::DumpMemoryStats(
-    base::trace_event::ProcessMemoryDump* pmd,
-    const std::string& parent_dump_absolute_name) const {
-  base_.DumpMemoryStats(pmd, parent_dump_absolute_name);
-}
-
-std::unique_ptr<base::DictionaryValue> SSLClientSocketPool::GetInfoAsValue(
-    const std::string& name,
-    const std::string& type,
-    bool include_nested_pools) const {
-  std::unique_ptr<base::DictionaryValue> dict(base_.GetInfoAsValue(name, type));
-  if (include_nested_pools) {
-    auto list = std::make_unique<base::ListValue>();
-    if (transport_pool_) {
-      list->Append(transport_pool_->GetInfoAsValue("transport_socket_pool",
-                                                   "transport_socket_pool",
-                                                   false));
-    }
-    if (socks_pool_) {
-      list->Append(socks_pool_->GetInfoAsValue("socks_pool",
-                                               "socks_pool",
-                                               true));
-    }
-    if (http_proxy_pool_) {
-      list->Append(http_proxy_pool_->GetInfoAsValue("http_proxy_pool",
-                                                    "http_proxy_pool",
-                                                    true));
-    }
-    dict->Set("nested_pools", std::move(list));
-  }
-  return dict;
-}
-
-bool SSLClientSocketPool::IsStalled() const {
-  return base_.IsStalled();
-}
-
-void SSLClientSocketPool::AddHigherLayeredPool(HigherLayeredPool* higher_pool) {
-  base_.AddHigherLayeredPool(higher_pool);
-}
-
-void SSLClientSocketPool::RemoveHigherLayeredPool(
-    HigherLayeredPool* higher_pool) {
-  base_.RemoveHigherLayeredPool(higher_pool);
-}
-
-bool SSLClientSocketPool::CloseOneIdleConnection() {
-  if (base_.CloseOneIdleSocket())
-    return true;
-  return base_.CloseOneIdleConnectionInHigherLayeredPool();
-}
-
-void SSLClientSocketPool::OnSSLConfigChanged() {
-  FlushWithError(ERR_NETWORK_CHANGED);
-}
-
-}  // namespace net
diff --git a/net/socket/ssl_client_socket_pool.h b/net/socket/ssl_client_socket_pool.h
deleted file mode 100644
index 682e66d..0000000
--- a/net/socket/ssl_client_socket_pool.h
+++ /dev/null
@@ -1,173 +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.
-
-#ifndef NET_SOCKET_SSL_CLIENT_SOCKET_POOL_H_
-#define NET_SOCKET_SSL_CLIENT_SOCKET_POOL_H_
-
-#include <memory>
-#include <string>
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/time/time.h"
-#include "net/base/completion_once_callback.h"
-#include "net/base/completion_repeating_callback.h"
-#include "net/base/net_export.h"
-#include "net/base/privacy_mode.h"
-#include "net/http/http_response_info.h"
-#include "net/socket/client_socket_pool.h"
-#include "net/socket/client_socket_pool_base.h"
-#include "net/socket/ssl_client_socket.h"
-#include "net/socket/ssl_connect_job.h"
-#include "net/ssl/ssl_config_service.h"
-
-namespace net {
-
-class CTPolicyEnforcer;
-class CertVerifier;
-class ClientSocketFactory;
-class ConnectJobFactory;
-class CTVerifier;
-class HttpProxyClientSocketPool;
-class NetworkQualityEstimator;
-class TransportClientSocketPool;
-class TransportSecurityState;
-
-class NET_EXPORT_PRIVATE SSLClientSocketPool
-    : public ClientSocketPool,
-      public HigherLayeredPool,
-      public SSLConfigService::Observer {
- public:
-  typedef SSLSocketParams SocketParams;
-
-  // Only the pools that will be used are required. i.e. if you never
-  // try to create an SSL over SOCKS socket, |socks_pool| may be NULL.
-  SSLClientSocketPool(int max_sockets,
-                      int max_sockets_per_group,
-                      CertVerifier* cert_verifier,
-                      ChannelIDService* channel_id_service,
-                      TransportSecurityState* transport_security_state,
-                      CTVerifier* cert_transparency_verifier,
-                      CTPolicyEnforcer* ct_policy_enforcer,
-                      SSLClientSessionCache* ssl_client_session_cache,
-                      ClientSocketFactory* client_socket_factory,
-                      TransportClientSocketPool* transport_pool,
-                      TransportClientSocketPool* socks_pool,
-                      HttpProxyClientSocketPool* http_proxy_pool,
-                      SSLConfigService* ssl_config_service,
-                      NetworkQualityEstimator* network_quality_estimator,
-                      NetLog* net_log);
-
-  ~SSLClientSocketPool() override;
-
-  // ClientSocketPool implementation.
-  int RequestSocket(const std::string& group_name,
-                    const void* connect_params,
-                    RequestPriority priority,
-                    const SocketTag& socket_tag,
-                    RespectLimits respect_limits,
-                    ClientSocketHandle* handle,
-                    CompletionOnceCallback callback,
-                    const NetLogWithSource& net_log) override;
-
-  void RequestSockets(const std::string& group_name,
-                      const void* params,
-                      int num_sockets,
-                      const NetLogWithSource& net_log) override;
-
-  void SetPriority(const std::string& group_name,
-                   ClientSocketHandle* handle,
-                   RequestPriority priority) override;
-
-  void CancelRequest(const std::string& group_name,
-                     ClientSocketHandle* handle) override;
-
-  void ReleaseSocket(const std::string& group_name,
-                     std::unique_ptr<StreamSocket> socket,
-                     int id) override;
-
-  void FlushWithError(int error) override;
-
-  void CloseIdleSockets() override;
-
-  void CloseIdleSocketsInGroup(const std::string& group_name) override;
-
-  int IdleSocketCount() const override;
-
-  int IdleSocketCountInGroup(const std::string& group_name) const override;
-
-  LoadState GetLoadState(const std::string& group_name,
-                         const ClientSocketHandle* handle) const override;
-
-  // Dumps memory allocation stats. |parent_dump_absolute_name| is the name
-  // used by the parent MemoryAllocatorDump in the memory dump hierarchy.
-  void DumpMemoryStats(base::trace_event::ProcessMemoryDump* pmd,
-                       const std::string& parent_dump_absolute_name) const;
-
-  std::unique_ptr<base::DictionaryValue> GetInfoAsValue(
-      const std::string& name,
-      const std::string& type,
-      bool include_nested_pools) 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;
-
- private:
-  typedef ClientSocketPoolBase<SSLSocketParams> PoolBase;
-
-  // SSLConfigService::Observer implementation.
-
-  // When the user changes the SSL config, we flush all idle sockets so they
-  // won't get re-used.
-  void OnSSLConfigChanged() override;
-
-  class SSLConnectJobFactory : public PoolBase::ConnectJobFactory {
-   public:
-    SSLConnectJobFactory(TransportClientSocketPool* transport_pool,
-                         TransportClientSocketPool* socks_pool,
-                         HttpProxyClientSocketPool* http_proxy_pool,
-                         ClientSocketFactory* client_socket_factory,
-                         const SSLClientSocketContext& context,
-                         NetworkQualityEstimator* network_quality_estimator,
-                         NetLog* net_log);
-
-    ~SSLConnectJobFactory() override;
-
-    // ClientSocketPoolBase::ConnectJobFactory methods.
-    std::unique_ptr<ConnectJob> NewConnectJob(
-        const std::string& group_name,
-        const PoolBase::Request& request,
-        ConnectJob::Delegate* delegate) const override;
-
-   private:
-    TransportClientSocketPool* const transport_pool_;
-    TransportClientSocketPool* const socks_pool_;
-    HttpProxyClientSocketPool* const http_proxy_pool_;
-    ClientSocketFactory* const client_socket_factory_;
-    const SSLClientSocketContext context_;
-    NetworkQualityEstimator* const network_quality_estimator_;
-    NetLog* net_log_;
-
-    DISALLOW_COPY_AND_ASSIGN(SSLConnectJobFactory);
-  };
-
-  TransportClientSocketPool* const transport_pool_;
-  TransportClientSocketPool* const socks_pool_;
-  HttpProxyClientSocketPool* const http_proxy_pool_;
-  PoolBase base_;
-  SSLConfigService* const ssl_config_service_;
-
-  DISALLOW_COPY_AND_ASSIGN(SSLClientSocketPool);
-};
-
-}  // namespace net
-
-#endif  // NET_SOCKET_SSL_CLIENT_SOCKET_POOL_H_
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc
index 820ac86..9ec8143c 100644
--- a/net/socket/ssl_client_socket_pool_unittest.cc
+++ b/net/socket/ssl_client_socket_pool_unittest.cc
@@ -103,18 +103,16 @@
     ssl_config_service_->GetSSLConfig(&ssl_config_);
   }
 
-  void CreatePool(bool transport_pool, bool http_proxy_pool) {
+  void CreatePool(bool http_proxy_pool) {
     pool_.reset(new TransportClientSocketPool(
-        kMaxSockets, kMaxSocketsPerGroup, &socket_factory_,
-        nullptr /* host_resolver */, cert_verifier_.get(),
-        NULL /* channel_id_service */, transport_security_state_.get(),
-        &ct_verifier_, &ct_policy_enforcer_,
+        kMaxSockets, kMaxSocketsPerGroup, &socket_factory_, &host_resolver_,
+        cert_verifier_.get(), NULL /* channel_id_service */,
+        transport_security_state_.get(), &ct_verifier_, &ct_policy_enforcer_,
         nullptr /* ssl_client_session_cache */,
         std::string() /* ssl_session_cache_shard */,
         nullptr /* ssl_config_service */,
         nullptr /* socket_performance_watcher_factory */,
         nullptr /* network_quality_estimator */, nullptr /* net_log */,
-        transport_pool ? &transport_socket_pool_ : nullptr,
         nullptr /* socks_pool */,
         http_proxy_pool ? &http_proxy_socket_pool_ : nullptr));
   }
@@ -186,7 +184,7 @@
   SSLSocketDataProvider ssl(ASYNC, ERR_CERT_COMMON_NAME_INVALID);
   socket_factory_.AddSSLSocketDataProvider(&ssl);
 
-  CreatePool(true /* tcp pool */, false);
+  CreatePool(false /* http_proxy_pool */);
   scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_DIRECT);
 
   ClientSocketHandle handle;
@@ -224,7 +222,7 @@
   SSLSocketDataProvider ssl(ASYNC, OK);
   socket_factory_.AddSSLSocketDataProvider(&ssl);
 
-  CreatePool(false, true /* http proxy pool */);
+  CreatePool(true /* http proxy pool */);
   scoped_refptr<SSLSocketParams> params = SSLParams(ProxyServer::SCHEME_HTTP);
 
   ClientSocketHandle handle;
@@ -253,281 +251,6 @@
 
 // It would be nice to also test the timeouts in SSLClientSocketPool.
 
-// Test that SocketTag passed into SSLClientSocketPool is applied to returned
-// sockets.
-#if defined(OS_ANDROID)
-TEST_F(SSLClientSocketPoolTest, Tag) {
-  // Start test server.
-  EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, SSLServerConfig());
-  test_server.AddDefaultHandlers(base::FilePath());
-  ASSERT_TRUE(test_server.Start());
-
-  // TLS 1.3 sockets aren't reused until the read side has been pumped.
-  // TODO(crbug.com/906668): Support pumping the read side and setting the
-  // socket to be reusable.
-  ssl_config_.version_max = SSL_PROTOCOL_VERSION_TLS1_2;
-
-  TransportClientSocketPool tcp_pool(
-      kMaxSockets, kMaxSocketsPerGroup,
-      ClientSocketFactory::GetDefaultFactory(), &host_resolver_,
-      nullptr /* cert_verifier */, nullptr /* channel_id_server */,
-      nullptr /* transport_security_state */,
-      nullptr /* cert_transparency_verifier */,
-      nullptr /* ct_policy_enforcer */, nullptr /* ssl_client_session_cache */,
-      std::string() /* ssl_session_cache_shard */,
-      nullptr /* ssl_config_service */,
-      nullptr /* socket_performance_watcher_factory */,
-      nullptr /* network_quality_estimator */, nullptr /* netlog */);
-  cert_verifier_->set_default_result(OK);
-  TransportClientSocketPool pool(
-      kMaxSockets, kMaxSocketsPerGroup,
-      ClientSocketFactory::GetDefaultFactory(), &host_resolver_,
-      cert_verifier_.get() /* cert_verifier */, nullptr /* channel_id_server */,
-      transport_security_state_.get(), &ct_verifier_, &ct_policy_enforcer_,
-      nullptr /* ssl_client_session_cache */,
-      std::string() /* ssl_session_cache_shard */,
-      nullptr /* ssl_config_service */,
-      nullptr /* socket_performance_watcher_factory */,
-      nullptr /* network_quality_estimator */, nullptr /* netlog */, &tcp_pool,
-      nullptr /* socks_pool */, nullptr /* http_proxy_pool */);
-  TestCompletionCallback callback;
-  ClientSocketHandle handle;
-  int32_t tag_val1 = 0x12345678;
-  SocketTag tag1(SocketTag::UNSET_UID, tag_val1);
-  int32_t tag_val2 = 0x87654321;
-  SocketTag tag2(getuid(), tag_val2);
-  scoped_refptr<TransportSocketParams> tcp_params(new TransportSocketParams(
-      test_server.host_port_pair(), false, OnHostResolutionCallback()));
-  scoped_refptr<SSLSocketParams> params(
-      new SSLSocketParams(tcp_params, NULL, NULL, test_server.host_port_pair(),
-                          ssl_config_, PRIVACY_MODE_DISABLED));
-
-  // Test socket is tagged before connected.
-  uint64_t old_traffic = GetTaggedBytes(tag_val1);
-  int rv = handle.Init(
-      kGroupName,
-      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
-          params),
-      LOW, tag1, ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
-      &pool, NetLogWithSource());
-  EXPECT_THAT(callback.GetResult(rv), IsOk());
-  EXPECT_TRUE(handle.socket());
-  EXPECT_TRUE(handle.socket()->IsConnected());
-  EXPECT_GT(GetTaggedBytes(tag_val1), old_traffic);
-
-  // Test reused socket is retagged.
-  StreamSocket* socket = handle.socket();
-  handle.Reset();
-  old_traffic = GetTaggedBytes(tag_val2);
-  TestCompletionCallback callback2;
-  rv = handle.Init(
-      kGroupName,
-      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
-          params),
-      LOW, tag2, ClientSocketPool::RespectLimits::ENABLED, callback2.callback(),
-      &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsOk());
-  EXPECT_TRUE(handle.socket());
-  EXPECT_TRUE(handle.socket()->IsConnected());
-  EXPECT_EQ(handle.socket(), socket);
-  const char kRequest[] = "GET / HTTP/1.1\r\n\r\n";
-  scoped_refptr<IOBuffer> write_buffer =
-      base::MakeRefCounted<StringIOBuffer>(kRequest);
-  rv =
-      handle.socket()->Write(write_buffer.get(), strlen(kRequest),
-                             callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
-  EXPECT_EQ(static_cast<int>(strlen(kRequest)), callback.GetResult(rv));
-  scoped_refptr<IOBufferWithSize> read_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(1);
-  rv = handle.socket()->Read(read_buffer.get(), read_buffer->size(),
-                             callback.callback());
-  EXPECT_EQ(read_buffer->size(), callback.GetResult(rv));
-  EXPECT_GT(GetTaggedBytes(tag_val2), old_traffic);
-  // Disconnect socket to prevent reuse.
-  handle.socket()->Disconnect();
-  handle.Reset();
-}
-
-TEST_F(SSLClientSocketPoolTest, TagTwoSockets) {
-  // Start test server.
-  EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, SSLServerConfig());
-  test_server.AddDefaultHandlers(base::FilePath());
-  ASSERT_TRUE(test_server.Start());
-
-  TransportClientSocketPool tcp_pool(
-      kMaxSockets, kMaxSocketsPerGroup,
-      ClientSocketFactory::GetDefaultFactory(), &host_resolver_,
-      nullptr /* cert_verifier */, nullptr /* channel_id_server */,
-      nullptr /* transport_security_state */,
-      nullptr /* cert_transparency_verifier */,
-      nullptr /* ct_policy_enforcer */, nullptr /* ssl_client_session_cache */,
-      std::string() /* ssl_session_cache_shard */,
-      nullptr /* ssl_config_service */,
-      nullptr /* socket_performance_watcher_factory */,
-      nullptr /* network_quality_estimator */, nullptr /* netlog */);
-  cert_verifier_->set_default_result(OK);
-  TransportClientSocketPool pool(
-      kMaxSockets, kMaxSocketsPerGroup,
-      ClientSocketFactory::GetDefaultFactory(), &host_resolver_,
-      cert_verifier_.get() /* cert_verifier */, nullptr /* channel_id_server */,
-      transport_security_state_.get(), &ct_verifier_, &ct_policy_enforcer_,
-      nullptr /* ssl_client_session_cache */,
-      std::string() /* ssl_session_cache_shard */,
-      nullptr /* ssl_config_service */,
-      nullptr /* socket_performance_watcher_factory */,
-      nullptr /* network_quality_estimator */, nullptr /* netlog */, &tcp_pool,
-      nullptr /* socks_pool */, nullptr /* http_proxy_pool */);
-  ClientSocketHandle handle;
-  int32_t tag_val1 = 0x12345678;
-  SocketTag tag1(SocketTag::UNSET_UID, tag_val1);
-  int32_t tag_val2 = 0x87654321;
-  SocketTag tag2(getuid(), tag_val2);
-  scoped_refptr<TransportSocketParams> tcp_params(new TransportSocketParams(
-      test_server.host_port_pair(), false, OnHostResolutionCallback()));
-  scoped_refptr<SSLSocketParams> params(
-      new SSLSocketParams(tcp_params, NULL, NULL, test_server.host_port_pair(),
-                          ssl_config_, PRIVACY_MODE_DISABLED));
-
-  // Test connect jobs that are orphaned and then adopted, appropriately apply
-  // new tag. Request socket with |tag1|.
-  TestCompletionCallback callback;
-  int rv = handle.Init(
-      kGroupName,
-      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
-          params),
-      LOW, tag1, ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
-      &pool, NetLogWithSource());
-  EXPECT_TRUE(rv == OK || rv == ERR_IO_PENDING) << "Result: " << rv;
-  // Abort and request socket with |tag2|.
-  handle.Reset();
-  TestCompletionCallback callback2;
-  rv = handle.Init(
-      kGroupName,
-      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
-          params),
-      LOW, tag2, ClientSocketPool::RespectLimits::ENABLED, callback2.callback(),
-      &pool, NetLogWithSource());
-  EXPECT_THAT(callback2.GetResult(rv), IsOk());
-  EXPECT_TRUE(handle.socket());
-  EXPECT_TRUE(handle.socket()->IsConnected());
-  // Verify socket has |tag2| applied.
-  uint64_t old_traffic = GetTaggedBytes(tag_val2);
-  const char kRequest[] = "GET / HTTP/1.1\r\n\r\n";
-  scoped_refptr<IOBuffer> write_buffer =
-      base::MakeRefCounted<StringIOBuffer>(kRequest);
-  rv = handle.socket()->Write(write_buffer.get(), strlen(kRequest),
-                              callback2.callback(),
-                              TRAFFIC_ANNOTATION_FOR_TESTS);
-  EXPECT_EQ(static_cast<int>(strlen(kRequest)), callback2.GetResult(rv));
-  scoped_refptr<IOBufferWithSize> read_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(1);
-  rv = handle.socket()->Read(read_buffer.get(), read_buffer->size(),
-                             callback2.callback());
-  EXPECT_EQ(read_buffer->size(), callback2.GetResult(rv));
-  EXPECT_GT(GetTaggedBytes(tag_val2), old_traffic);
-}
-
-TEST_F(SSLClientSocketPoolTest, TagTwoSocketsFullPool) {
-  // Start test server.
-  EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
-  test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, SSLServerConfig());
-  test_server.AddDefaultHandlers(base::FilePath());
-  ASSERT_TRUE(test_server.Start());
-
-  TransportClientSocketPool tcp_pool(
-      kMaxSockets, kMaxSocketsPerGroup,
-      ClientSocketFactory::GetDefaultFactory(), &host_resolver_,
-      nullptr /* cert_verifier */, nullptr /* channel_id_server */,
-      nullptr /* transport_security_state */,
-      nullptr /* cert_transparency_verifier */,
-      nullptr /* ct_policy_enforcer */, nullptr /* ssl_client_session_cache */,
-      std::string() /* ssl_session_cache_shard */,
-      nullptr /* ssl_config_service */,
-      nullptr /* socket_performance_watcher_factory */,
-      nullptr /* network_quality_estimator */, nullptr /* netlog */);
-  cert_verifier_->set_default_result(OK);
-  TransportClientSocketPool pool(
-      kMaxSockets, kMaxSocketsPerGroup,
-      ClientSocketFactory::GetDefaultFactory(), &host_resolver_,
-      cert_verifier_.get() /* cert_verifier */, nullptr /* channel_id_server */,
-      transport_security_state_.get(), &ct_verifier_, &ct_policy_enforcer_,
-      nullptr /* ssl_client_session_cache */,
-      std::string() /* ssl_session_cache_shard */,
-      nullptr /* ssl_config_service */,
-      nullptr /* socket_performance_watcher_factory */,
-      nullptr /* network_quality_estimator */, nullptr /* netlog */, &tcp_pool,
-      nullptr /* socks_pool */, nullptr /* http_proxy_pool */);
-  TestCompletionCallback callback;
-  ClientSocketHandle handle;
-  int32_t tag_val1 = 0x12345678;
-  SocketTag tag1(SocketTag::UNSET_UID, tag_val1);
-  int32_t tag_val2 = 0x87654321;
-  SocketTag tag2(getuid(), tag_val2);
-  scoped_refptr<TransportSocketParams> tcp_params(new TransportSocketParams(
-      test_server.host_port_pair(), false, OnHostResolutionCallback()));
-  scoped_refptr<SSLSocketParams> params(
-      new SSLSocketParams(tcp_params, NULL, NULL, test_server.host_port_pair(),
-                          ssl_config_, PRIVACY_MODE_DISABLED));
-
-  // Test that sockets paused by a full underlying socket pool are properly
-  // connected and tagged when underlying pool is freed up.
-  // Fill up all slots in TCP pool.
-  ClientSocketHandle tcp_handles[kMaxSocketsPerGroup];
-  int rv;
-  for (auto& tcp_handle : tcp_handles) {
-    rv = tcp_handle.Init(kGroupName,
-                         TransportClientSocketPool::SocketParams::
-                             CreateFromTransportSocketParams(tcp_params),
-                         LOW, tag1, ClientSocketPool::RespectLimits::ENABLED,
-                         callback.callback(), &tcp_pool, NetLogWithSource());
-    EXPECT_THAT(callback.GetResult(rv), IsOk());
-    EXPECT_TRUE(tcp_handle.socket());
-    EXPECT_TRUE(tcp_handle.socket()->IsConnected());
-  }
-  // Request two SSL sockets.
-  ClientSocketHandle handle_to_be_canceled;
-  rv = handle_to_be_canceled.Init(
-      kGroupName,
-      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
-          params),
-      LOW, tag1, ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
-      &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  rv = handle.Init(
-      kGroupName,
-      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
-          params),
-      LOW, tag2, ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
-      &pool, NetLogWithSource());
-  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
-  // Cancel first request.
-  handle_to_be_canceled.Reset();
-  // Disconnect a TCP socket to free up a slot.
-  tcp_handles[0].socket()->Disconnect();
-  tcp_handles[0].Reset();
-  // Verify |handle| gets a valid tagged socket.
-  EXPECT_THAT(callback.WaitForResult(), IsOk());
-  EXPECT_TRUE(handle.socket());
-  EXPECT_TRUE(handle.socket()->IsConnected());
-  uint64_t old_traffic = GetTaggedBytes(tag_val2);
-  const char kRequest[] = "GET / HTTP/1.1\r\n\r\n";
-  scoped_refptr<IOBuffer> write_buffer =
-      base::MakeRefCounted<StringIOBuffer>(kRequest);
-  rv =
-      handle.socket()->Write(write_buffer.get(), strlen(kRequest),
-                             callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
-  EXPECT_EQ(static_cast<int>(strlen(kRequest)), callback.GetResult(rv));
-  scoped_refptr<IOBufferWithSize> read_buffer =
-      base::MakeRefCounted<IOBufferWithSize>(1);
-  EXPECT_EQ(handle.socket()->Read(read_buffer.get(), read_buffer->size(),
-                                  callback.callback()),
-            ERR_IO_PENDING);
-  EXPECT_THAT(callback.WaitForResult(), read_buffer->size());
-  EXPECT_GT(GetTaggedBytes(tag_val2), old_traffic);
-}
-#endif
 }  // namespace
 
 }  // namespace net
diff --git a/net/socket/ssl_connect_job.cc b/net/socket/ssl_connect_job.cc
index df71b53..529ff81f 100644
--- a/net/socket/ssl_connect_job.cc
+++ b/net/socket/ssl_connect_job.cc
@@ -95,10 +95,9 @@
     RequestPriority priority,
     const CommonConnectJobParams& common_connect_job_params,
     const scoped_refptr<SSLSocketParams>& params,
-    TransportClientSocketPool* transport_pool,
     TransportClientSocketPool* socks_pool,
     HttpProxyClientSocketPool* http_proxy_pool,
-    Delegate* delegate)
+    ConnectJob::Delegate* delegate)
     : ConnectJob(priority,
                  ConnectionTimeout(
                      *params,
@@ -108,7 +107,6 @@
                  NetLogWithSource::Make(common_connect_job_params.net_log,
                                         NetLogSourceType::SSL_CONNECT_JOB)),
       params_(params),
-      transport_pool_(transport_pool),
       socks_pool_(socks_pool),
       http_proxy_pool_(http_proxy_pool),
       callback_(base::BindRepeating(&SSLConnectJob::OnIOComplete,
@@ -118,15 +116,17 @@
 
 LoadState SSLConnectJob::GetLoadState() const {
   switch (next_state_) {
+    case STATE_TRANSPORT_CONNECT:
+    case STATE_SOCKS_CONNECT:
+    case STATE_TUNNEL_CONNECT:
+      return LOAD_STATE_IDLE;
+    case STATE_TRANSPORT_CONNECT_COMPLETE:
+      return nested_connect_job_->GetLoadState();
+    case STATE_SOCKS_CONNECT_COMPLETE:
+      return transport_socket_handle_->GetLoadState();
     case STATE_TUNNEL_CONNECT_COMPLETE:
       if (transport_socket_handle_->socket())
         return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
-      FALLTHROUGH;
-    case STATE_TRANSPORT_CONNECT:
-    case STATE_TRANSPORT_CONNECT_COMPLETE:
-    case STATE_SOCKS_CONNECT:
-    case STATE_SOCKS_CONNECT_COMPLETE:
-    case STATE_TUNNEL_CONNECT:
       return transport_socket_handle_->GetLoadState();
     case STATE_SSL_CONNECT:
     case STATE_SSL_CONNECT_COMPLETE:
@@ -138,10 +138,21 @@
 }
 
 bool SSLConnectJob::HasEstablishedConnection() const {
-  // Return true to prevent creating any backup jobs when this is used in a
-  // TransportClientSocketPool.
-  // TODO(mmenke): Implement this, as nested pools are removed.
-  return true;
+  // Return true to prevent creating any backup jobs when this is used on top of
+  // another socket pool type.
+  if (socks_pool_ || http_proxy_pool_)
+    return true;
+
+  // If waiting on a nested ConnectJob, defer to that ConnectJob's state.
+  if (nested_connect_job_)
+    return nested_connect_job_->HasEstablishedConnection();
+  // Otherwise, return true if a socket has been created.
+  return nested_socket_ || ssl_socket_;
+}
+
+void SSLConnectJob::OnConnectJobComplete(int result, ConnectJob* job) {
+  DCHECK_EQ(job, nested_connect_job_.get());
+  OnIOComplete(result);
 }
 
 void SSLConnectJob::GetAdditionalErrorState(ClientSocketHandle* handle) {
@@ -234,26 +245,27 @@
 }
 
 int SSLConnectJob::DoTransportConnect() {
-  DCHECK(transport_pool_);
+  DCHECK(!nested_connect_job_);
+  DCHECK(params_->GetDirectConnectionParams());
 
   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
-  transport_socket_handle_.reset(new ClientSocketHandle());
-  scoped_refptr<TransportClientSocketPool::SocketParams> direct_params =
-      TransportClientSocketPool::SocketParams::CreateFromTransportSocketParams(
-          params_->GetDirectConnectionParams());
-  return transport_socket_handle_->Init(group_name(), direct_params, priority(),
-                                        socket_tag(), respect_limits(),
-                                        callback_, transport_pool_, net_log());
+  nested_connect_job_ = TransportConnectJob::CreateTransportConnectJob(
+      params_->GetDirectConnectionParams(), priority(),
+      common_connect_job_params(), this);
+  return nested_connect_job_->Connect();
 }
 
 int SSLConnectJob::DoTransportConnectComplete(int result) {
-  connection_attempts_.insert(
-      connection_attempts_.end(),
-      transport_socket_handle_->connection_attempts().begin(),
-      transport_socket_handle_->connection_attempts().end());
+  // TODO(mmenke): Implement a better API to get this information.
+  ClientSocketHandle bogus_handle;
+  nested_connect_job_->GetAdditionalErrorState(&bogus_handle);
+  connection_attempts_.insert(connection_attempts_.end(),
+                              bogus_handle.connection_attempts().begin(),
+                              bogus_handle.connection_attempts().end());
   if (result == OK) {
     next_state_ = STATE_SSL_CONNECT;
-    transport_socket_handle_->socket()->GetPeerAddress(&server_address_);
+    nested_socket_ = nested_connect_job_->PassSocket();
+    nested_socket_->GetPeerAddress(&server_address_);
   }
 
   return result;
@@ -319,17 +331,24 @@
   ResetTimer(base::TimeDelta::FromSeconds(kSSLHandshakeTimeoutInSeconds));
 
   // If the handle has a fresh socket, get its connect start and DNS times.
-  // This should always be the case.
-  const LoadTimingInfo::ConnectTiming& socket_connect_timing =
-      transport_socket_handle_->connect_timing();
-  if (!transport_socket_handle_->is_reused() &&
-      !socket_connect_timing.connect_start.is_null()) {
+  // This should always be the case in the nested socket pool path, and
+  // *will* always be the case in the non-nested socket pool path.
+  const LoadTimingInfo::ConnectTiming* socket_connect_timing = nullptr;
+  if (nested_connect_job_) {
+    socket_connect_timing = &nested_connect_job_->connect_timing();
+  } else if (!transport_socket_handle_->is_reused() &&
+             !transport_socket_handle_->connect_timing()
+                  .connect_start.is_null()) {
+    socket_connect_timing = &transport_socket_handle_->connect_timing();
+  }
+
+  if (socket_connect_timing) {
     // Overwriting |connect_start| serves two purposes - it adjusts timing so
     // |connect_start| doesn't include dns times, and it adjusts the time so
     // as not to include time spent waiting for an idle socket.
-    connect_timing_.connect_start = socket_connect_timing.connect_start;
-    connect_timing_.dns_start = socket_connect_timing.dns_start;
-    connect_timing_.dns_end = socket_connect_timing.dns_end;
+    connect_timing_.connect_start = socket_connect_timing->connect_start;
+    connect_timing_.dns_start = socket_connect_timing->dns_start;
+    connect_timing_.dns_end = socket_connect_timing->dns_end;
   }
 
   connect_timing_.ssl_start = base::TimeTicks::Now();
@@ -347,9 +366,17 @@
       ssl_client_socket_context().ssl_client_session_cache,
       (params_->privacy_mode() == PRIVACY_MODE_ENABLED ? "pm/" : "nopm/") +
           ssl_client_socket_context().ssl_session_cache_shard);
-  ssl_socket_ = client_socket_factory()->CreateSSLClientSocket(
-      std::move(transport_socket_handle_), params_->host_and_port(),
-      params_->ssl_config(), context_with_privacy_mode);
+  if (nested_socket_.get()) {
+    DCHECK(!transport_socket_handle_);
+    ssl_socket_ = client_socket_factory()->CreateSSLClientSocket(
+        std::move(nested_socket_), params_->host_and_port(),
+        params_->ssl_config(), context_with_privacy_mode);
+    nested_connect_job_.reset();
+  } else {
+    ssl_socket_ = client_socket_factory()->CreateSSLClientSocket(
+        std::move(transport_socket_handle_), params_->host_and_port(),
+        params_->ssl_config(), context_with_privacy_mode);
+  }
   return ssl_socket_->Connect(callback_);
 }
 
@@ -453,6 +480,8 @@
 void SSLConnectJob::ChangePriorityInternal(RequestPriority priority) {
   if (transport_socket_handle_)
     transport_socket_handle_->SetPriority(priority);
+  if (nested_connect_job_)
+    nested_connect_job_->ChangePriority(priority);
 }
 
 }  // namespace net
diff --git a/net/socket/ssl_connect_job.h b/net/socket/ssl_connect_job.h
index fb4e9d6..49f7a83 100644
--- a/net/socket/ssl_connect_job.h
+++ b/net/socket/ssl_connect_job.h
@@ -77,23 +77,26 @@
 
 // SSLConnectJob establishes a connection, through a proxy if needed, and then
 // handles the SSL handshake. It returns an SSLClientSocket on success.
-class NET_EXPORT_PRIVATE SSLConnectJob : public ConnectJob {
+class NET_EXPORT_PRIVATE SSLConnectJob : public ConnectJob,
+                                         public ConnectJob::Delegate {
  public:
   // Note: the SSLConnectJob does not own |messenger| so it must outlive the
   // job.
   SSLConnectJob(RequestPriority priority,
                 const CommonConnectJobParams& common_connect_job_params,
                 const scoped_refptr<SSLSocketParams>& params,
-                TransportClientSocketPool* transport_pool,
                 TransportClientSocketPool* socks_pool,
                 HttpProxyClientSocketPool* http_proxy_pool,
-                Delegate* delegate);
+                ConnectJob::Delegate* delegate);
   ~SSLConnectJob() override;
 
   // ConnectJob methods.
   LoadState GetLoadState() const override;
   bool HasEstablishedConnection() const override;
 
+  // ConnectJob::Delegate methods.
+  void OnConnectJobComplete(int result, ConnectJob* job) override;
+
   void GetAdditionalErrorState(ClientSocketHandle* handle) override;
 
   // Returns the connection timeout that will be used by a HttpProxyConnectJob
@@ -141,12 +144,13 @@
   void ChangePriorityInternal(RequestPriority priority) override;
 
   scoped_refptr<SSLSocketParams> params_;
-  TransportClientSocketPool* const transport_pool_;
   TransportClientSocketPool* const socks_pool_;
   HttpProxyClientSocketPool* const http_proxy_pool_;
 
   State next_state_;
   CompletionRepeatingCallback callback_;
+  std::unique_ptr<ConnectJob> nested_connect_job_;
+  std::unique_ptr<StreamSocket> nested_socket_;
   std::unique_ptr<ClientSocketHandle> transport_socket_handle_;
   std::unique_ptr<SSLClientSocket> ssl_socket_;
 
diff --git a/net/socket/ssl_connect_job_unittest.cc b/net/socket/ssl_connect_job_unittest.cc
index caa5cb2..978978f4 100644
--- a/net/socket/ssl_connect_job_unittest.cc
+++ b/net/socket/ssl_connect_job_unittest.cc
@@ -167,8 +167,6 @@
             nullptr /* network_quality_estimator */, nullptr /* net_log */,
             nullptr /* websocket_lock_endpoint_manager */),
         SSLParams(proxy_scheme),
-        proxy_scheme == ProxyServer::SCHEME_DIRECT ? &transport_socket_pool_
-                                                   : nullptr,
         proxy_scheme == ProxyServer::SCHEME_SOCKS5 ? &transport_socket_pool_
                                                    : nullptr,
         proxy_scheme == ProxyServer::SCHEME_HTTP ? &http_proxy_socket_pool_
@@ -320,6 +318,40 @@
             ssl_connect_job->connect_timing().connect_end);
 }
 
+TEST_F(SSLConnectJobTest, DirectHasEstablishedConnection) {
+  host_resolver_.set_ondemand_mode(true);
+  StaticSocketDataProvider data;
+  data.set_connect_data(MockConnect(ASYNC, OK));
+  socket_factory_.AddSocketDataProvider(&data);
+
+  // SSL negotiation hangs. Value returned after SSL negotiation is complete
+  // doesn't matter, as HasEstablishedConnection() may only be used between job
+  // start and job complete.
+  SSLSocketDataProvider ssl(SYNCHRONOUS, ERR_IO_PENDING);
+  socket_factory_.AddSSLSocketDataProvider(&ssl);
+
+  TestConnectJobDelegate test_delegate;
+  std::unique_ptr<ConnectJob> ssl_connect_job =
+      CreateConnectJob(&test_delegate, ProxyServer::SCHEME_DIRECT, MEDIUM);
+  EXPECT_THAT(ssl_connect_job->Connect(), test::IsError(ERR_IO_PENDING));
+  EXPECT_TRUE(host_resolver_.has_pending_requests());
+  EXPECT_EQ(LOAD_STATE_RESOLVING_HOST, ssl_connect_job->GetLoadState());
+  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());
+
+  // DNS resolution completes, and then the ConnectJob tries to connect the
+  // socket, which should succeed asynchronously.
+  host_resolver_.ResolveNow(1);
+  EXPECT_EQ(LOAD_STATE_CONNECTING, ssl_connect_job->GetLoadState());
+  EXPECT_FALSE(ssl_connect_job->HasEstablishedConnection());
+
+  // Spinning the message loop causes the socket to finish connecting. The SSL
+  // handshake should start and hang.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_FALSE(test_delegate.has_result());
+  EXPECT_EQ(LOAD_STATE_SSL_HANDSHAKE, ssl_connect_job->GetLoadState());
+  EXPECT_TRUE(ssl_connect_job->HasEstablishedConnection());
+}
+
 TEST_F(SSLConnectJobTest, RequestPriority) {
   host_resolver_.set_ondemand_mode(true);
   // Make resolution eventually fail, so old jobs can easily be removed from the
diff --git a/net/socket/transport_client_socket_pool.cc b/net/socket/transport_client_socket_pool.cc
index 12b7daf..5bd2e69e 100644
--- a/net/socket/transport_client_socket_pool.cc
+++ b/net/socket/transport_client_socket_pool.cc
@@ -38,10 +38,8 @@
     RequestPriority priority,
     const CommonConnectJobParams& common_connect_job_params,
     ConnectJob::Delegate* delegate,
-    TransportClientSocketPool* transport_pool,
     TransportClientSocketPool* socks_pool,
     HttpProxyClientSocketPool* http_proxy_pool) {
-  DCHECK(!transport_pool);
   DCHECK(!socks_pool);
   DCHECK(!http_proxy_pool);
   return TransportConnectJob::CreateTransportConnectJob(
@@ -54,10 +52,8 @@
     RequestPriority priority,
     const CommonConnectJobParams& common_connect_job_params,
     ConnectJob::Delegate* delegate,
-    TransportClientSocketPool* transport_pool,
     TransportClientSocketPool* socks_pool,
     HttpProxyClientSocketPool* http_proxy_pool) {
-  DCHECK(!transport_pool);
   DCHECK(!socks_pool);
   DCHECK(!http_proxy_pool);
   return std::make_unique<SOCKSConnectJob>(priority, common_connect_job_params,
@@ -70,12 +66,11 @@
     RequestPriority priority,
     const CommonConnectJobParams& common_connect_job_params,
     ConnectJob::Delegate* delegate,
-    TransportClientSocketPool* transport_pool,
     TransportClientSocketPool* socks_pool,
     HttpProxyClientSocketPool* http_proxy_pool) {
-  return std::make_unique<SSLConnectJob>(
-      priority, common_connect_job_params, std::move(ssl_socket_params),
-      transport_pool, socks_pool, http_proxy_pool, delegate);
+  return std::make_unique<SSLConnectJob>(priority, common_connect_job_params,
+                                         std::move(ssl_socket_params),
+                                         socks_pool, http_proxy_pool, delegate);
 }
 
 }  // namespace
@@ -118,7 +113,6 @@
         SocketPerformanceWatcherFactory* socket_performance_watcher_factory,
         NetworkQualityEstimator* network_quality_estimator,
         NetLog* net_log,
-        TransportClientSocketPool* transport_pool,
         TransportClientSocketPool* socks_pool,
         HttpProxyClientSocketPool* http_proxy_pool)
     : client_socket_factory_(client_socket_factory),
@@ -127,7 +121,6 @@
       socket_performance_watcher_factory_(socket_performance_watcher_factory),
       network_quality_estimator_(network_quality_estimator),
       net_log_(net_log),
-      transport_pool_(transport_pool),
       socks_pool_(socks_pool),
       http_proxy_pool_(http_proxy_pool) {}
 
@@ -147,7 +140,7 @@
           client_socket_factory_, host_resolver_, ssl_client_socket_context_,
           socket_performance_watcher_factory_, network_quality_estimator_,
           net_log_, nullptr /* websocket_endpoint_lock_manager */),
-      delegate, transport_pool_, socks_pool_, http_proxy_pool_);
+      delegate, socks_pool_, http_proxy_pool_);
 }
 
 TransportClientSocketPool::TransportClientSocketPool(
@@ -166,7 +159,6 @@
     SocketPerformanceWatcherFactory* socket_performance_watcher_factory,
     NetworkQualityEstimator* network_quality_estimator,
     NetLog* net_log,
-    TransportClientSocketPool* transport_pool,
     TransportClientSocketPool* socks_pool,
     HttpProxyClientSocketPool* http_proxy_pool)
     : base_(this,
@@ -187,7 +179,6 @@
                 socket_performance_watcher_factory,
                 network_quality_estimator,
                 net_log,
-                transport_pool,
                 socks_pool,
                 http_proxy_pool)),
       client_socket_factory_(client_socket_factory),
@@ -196,8 +187,6 @@
   if (ssl_config_service_)
     ssl_config_service_->AddObserver(this);
 
-  if (transport_pool)
-    base_.AddLowerLayeredPool(transport_pool);
   if (socks_pool)
     base_.AddLowerLayeredPool(socks_pool);
   if (http_proxy_pool)
diff --git a/net/socket/transport_client_socket_pool.h b/net/socket/transport_client_socket_pool.h
index a93531d..10d59b8 100644
--- a/net/socket/transport_client_socket_pool.h
+++ b/net/socket/transport_client_socket_pool.h
@@ -48,15 +48,14 @@
   // |websocket_endpoint_lock_manager| is non-null, a ConnectJob for use by
   // WebSockets should be created.
   //
-  // |transport_pool|, |socks_pool|, and |http_proxy_pool| are for the case of
-  // SSLConnectJobs that will be layered on top of other socket pool types.
+  // |socks_pool| and |http_proxy_pool| are for the case of SSLConnectJobs that
+  // will be layered on top of other socket pool types.
   // TODO(mmenke): Remove them.
   using CreateConnectJobCallback =
       base::RepeatingCallback<std::unique_ptr<ConnectJob>(
           RequestPriority priority,
           const CommonConnectJobParams& common_connect_job_params,
           ConnectJob::Delegate* delegate,
-          TransportClientSocketPool* transport_pool,
           TransportClientSocketPool* socks_pool,
           HttpProxyClientSocketPool* http_proxy_pool)>;
 
@@ -93,9 +92,8 @@
     DISALLOW_COPY_AND_ASSIGN(SocketParams);
   };
 
-  // If this is being used for an SSL socket pool, |transport_pool|,
-  // |socks_pool|, and |http_proxy_pool| socket pools beneath the SSL socket
-  // pool.
+  // If this is being used for an SSL socket pool, |socks_pool| and
+  // |http_proxy_pool| socket pools beneath the SSL socket pool.
   //
   // TODO(mmenke): Remove those socket pools.
   TransportClientSocketPool(
@@ -114,7 +112,6 @@
       SocketPerformanceWatcherFactory* socket_performance_watcher_factory,
       NetworkQualityEstimator* network_quality_estimator,
       NetLog* net_log,
-      TransportClientSocketPool* transport_pool = nullptr,
       TransportClientSocketPool* socks_pool = nullptr,
       HttpProxyClientSocketPool* http_proxy_pool = nullptr);
 
@@ -187,7 +184,6 @@
         SocketPerformanceWatcherFactory* socket_performance_watcher_factory,
         NetworkQualityEstimator* network_quality_estimator,
         NetLog* net_log,
-        TransportClientSocketPool* transport_pool = nullptr,
         TransportClientSocketPool* socks_pool = nullptr,
         HttpProxyClientSocketPool* http_proxy_pool = nullptr);
 
@@ -208,7 +204,6 @@
     NetworkQualityEstimator* const network_quality_estimator_;
     NetLog* const net_log_;
 
-    TransportClientSocketPool* const transport_pool_;
     TransportClientSocketPool* const socks_pool_;
     HttpProxyClientSocketPool* const http_proxy_pool_;
 
diff --git a/net/socket/transport_client_socket_pool_test_util.cc b/net/socket/transport_client_socket_pool_test_util.cc
index d192fff..6eef9bf 100644
--- a/net/socket/transport_client_socket_pool_test_util.cc
+++ b/net/socket/transport_client_socket_pool_test_util.cc
@@ -452,6 +452,16 @@
   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,
diff --git a/net/socket/transport_client_socket_pool_test_util.h b/net/socket/transport_client_socket_pool_test_util.h
index da6146cd..71b4a72 100644
--- a/net/socket/transport_client_socket_pool_test_util.h
+++ b/net/socket/transport_client_socket_pool_test_util.h
@@ -91,6 +91,13 @@
       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,
diff --git a/net/socket/transport_client_socket_pool_unittest.cc b/net/socket/transport_client_socket_pool_unittest.cc
index 7d4be654..b96aae8 100644
--- a/net/socket/transport_client_socket_pool_unittest.cc
+++ b/net/socket/transport_client_socket_pool_unittest.cc
@@ -17,13 +17,18 @@
 #include "net/base/load_timing_info_test_util.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/transport_security_state.h"
 #include "net/log/net_log_with_source.h"
 #include "net/log/test_net_log.h"
 #include "net/socket/client_socket_handle.h"
 #include "net/socket/socket_tag.h"
 #include "net/socket/socket_test_util.h"
 #include "net/socket/socks_connect_job.h"
+#include "net/socket/ssl_connect_job.h"
 #include "net/socket/stream_socket.h"
 #include "net/socket/transport_client_socket_pool_test_util.h"
 #include "net/socket/transport_connect_job.h"
@@ -92,17 +97,32 @@
               kMaxSocketsPerGroup,
               &client_socket_factory_,
               host_resolver_.get(),
-              nullptr /* cert_verifier */,
+              &cert_verifier_,
               nullptr /* channel_id_server */,
-              nullptr /* transport_security_state */,
-              nullptr /* cert_transparency_verifier */,
-              nullptr /* ct_policy_enforcer */,
+              &transport_security_state_,
+              &ct_verifier_,
+              &ct_policy_enforcer_,
               nullptr /* ssl_client_session_cache */,
               std::string() /* ssl_session_cache_shard */,
               ssl_config_service_.get(),
               nullptr /* socket_performance_watcher_factory */,
               nullptr /* network_quality_estimator */,
-              nullptr /* net_log */) {}
+              nullptr /* net_log */),
+        pool_for_real_sockets_(kMaxSockets,
+                               kMaxSocketsPerGroup,
+                               ClientSocketFactory::GetDefaultFactory(),
+                               host_resolver_.get(),
+                               &cert_verifier_,
+                               nullptr /* channel_id_server */,
+                               &transport_security_state_,
+                               &ct_verifier_,
+                               &ct_policy_enforcer_,
+                               nullptr /* ssl_client_session_cache */,
+                               std::string() /* ssl_session_cache_shard */,
+                               ssl_config_service_.get(),
+                               nullptr /* socket_performance_watcher_factory */,
+                               nullptr /* network_quality_estimator */,
+                               nullptr /* net_log */) {}
 
   ~TransportClientSocketPoolTest() override {
     internal::ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(
@@ -138,13 +158,26 @@
   }
   size_t completion_count() const { return test_base_.completion_count(); }
 
+  SSLConfig GetSSLConfig() const {
+    SSLConfig ssl_config;
+    ssl_config_service_->GetSSLConfig(&ssl_config);
+    return ssl_config;
+  }
+
   bool connect_backup_jobs_enabled_;
   TestNetLog net_log_;
   std::unique_ptr<SSLConfigService> ssl_config_service_;
   scoped_refptr<TransportClientSocketPool::SocketParams> params_;
   std::unique_ptr<MockHostResolver> host_resolver_;
   MockTransportClientSocketFactory client_socket_factory_;
+  MockCertVerifier cert_verifier_;
+  TransportSecurityState transport_security_state_;
+  MultiLogCTVerifier ct_verifier_;
+  DefaultCTPolicyEnforcer ct_policy_enforcer_;
   TransportClientSocketPool pool_;
+  // Just like |pool_|, except it uses a real ClientSocketFactory instead of
+  // |client_socket_factory_|.
+  TransportClientSocketPool pool_for_real_sockets_;
   ClientSocketPoolTest test_base_;
 
  private:
@@ -1134,8 +1167,9 @@
         nullptr /* socket_performance_watcher_factory */,
         nullptr /* network_quality_estimator */, nullptr /* netlog */);
 
-    scoped_refptr<TransportSocketParams> tcp_params(new TransportSocketParams(
-        HostPortPair("proxy", 80), false, OnHostResolutionCallback()));
+    scoped_refptr<TransportSocketParams> tcp_params =
+        base::MakeRefCounted<TransportSocketParams>(
+            HostPortPair("proxy", 80), false, OnHostResolutionCallback());
     scoped_refptr<TransportClientSocketPool::SocketParams> socks_params(
         TransportClientSocketPool::SocketParams::CreateFromSOCKSSocketParams(
             base::MakeRefCounted<SOCKSSocketParams>(
@@ -1306,8 +1340,9 @@
 
   SocketTag tag1(SocketTag::UNSET_UID, 0x12345678);
   SocketTag tag2(getuid(), 0x87654321);
-  scoped_refptr<TransportSocketParams> tcp_params(new TransportSocketParams(
-      HostPortPair("proxy", 80), false, OnHostResolutionCallback()));
+  scoped_refptr<TransportSocketParams> tcp_params =
+      base::MakeRefCounted<TransportSocketParams>(
+          HostPortPair("proxy", 80), false, OnHostResolutionCallback());
   scoped_refptr<TransportClientSocketPool::SocketParams> socks_params(
       TransportClientSocketPool::SocketParams::CreateFromSOCKSSocketParams(
           base::MakeRefCounted<SOCKSSocketParams>(
@@ -1371,7 +1406,224 @@
   EXPECT_EQ(socket_factory.GetLastProducedTCPSocket()->tag(), tag2);
 }
 
-#endif
+TEST_F(TransportClientSocketPoolTest, TagSSLDirect) {
+  const char kGroupName[] = "group_name";
+
+  // Start test server.
+  EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, SSLServerConfig());
+  test_server.AddDefaultHandlers(base::FilePath());
+  ASSERT_TRUE(test_server.Start());
+
+  // TLS 1.3 sockets aren't reused until the read side has been pumped.
+  // TODO(crbug.com/906668): Support pumping the read side and setting the
+  // socket to be reusable.
+  SSLConfig ssl_config = GetSSLConfig();
+  ssl_config.version_max = SSL_PROTOCOL_VERSION_TLS1_2;
+
+  cert_verifier_.set_default_result(OK);
+  TestCompletionCallback callback;
+  ClientSocketHandle handle;
+  int32_t tag_val1 = 0x12345678;
+  SocketTag tag1(SocketTag::UNSET_UID, tag_val1);
+  int32_t tag_val2 = 0x87654321;
+  SocketTag tag2(getuid(), tag_val2);
+  scoped_refptr<TransportSocketParams> tcp_params =
+      base::MakeRefCounted<TransportSocketParams>(
+          test_server.host_port_pair(), false, OnHostResolutionCallback());
+  scoped_refptr<SSLSocketParams> params(new SSLSocketParams(
+      tcp_params, nullptr, nullptr, test_server.host_port_pair(), ssl_config,
+      PRIVACY_MODE_DISABLED));
+
+  // Test socket is tagged before connected.
+  uint64_t old_traffic = GetTaggedBytes(tag_val1);
+  int rv = handle.Init(
+      kGroupName,
+      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
+          params),
+      LOW, tag1, ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
+      &pool_for_real_sockets_, NetLogWithSource());
+  EXPECT_THAT(callback.GetResult(rv), IsOk());
+  EXPECT_TRUE(handle.socket());
+  EXPECT_TRUE(handle.socket()->IsConnected());
+  EXPECT_GT(GetTaggedBytes(tag_val1), old_traffic);
+
+  // Test reused socket is retagged.
+  StreamSocket* socket = handle.socket();
+  handle.Reset();
+  old_traffic = GetTaggedBytes(tag_val2);
+  TestCompletionCallback callback2;
+  rv = handle.Init(
+      kGroupName,
+      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
+          params),
+      LOW, tag2, ClientSocketPool::RespectLimits::ENABLED, callback2.callback(),
+      &pool_for_real_sockets_, NetLogWithSource());
+  EXPECT_THAT(rv, IsOk());
+  EXPECT_TRUE(handle.socket());
+  EXPECT_TRUE(handle.socket()->IsConnected());
+  EXPECT_EQ(handle.socket(), socket);
+  const char kRequest[] = "GET / HTTP/1.1\r\n\r\n";
+  scoped_refptr<IOBuffer> write_buffer =
+      base::MakeRefCounted<StringIOBuffer>(kRequest);
+  rv =
+      handle.socket()->Write(write_buffer.get(), strlen(kRequest),
+                             callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(static_cast<int>(strlen(kRequest)), callback.GetResult(rv));
+  scoped_refptr<IOBufferWithSize> read_buffer =
+      base::MakeRefCounted<IOBufferWithSize>(1);
+  rv = handle.socket()->Read(read_buffer.get(), read_buffer->size(),
+                             callback.callback());
+  EXPECT_EQ(read_buffer->size(), callback.GetResult(rv));
+  EXPECT_GT(GetTaggedBytes(tag_val2), old_traffic);
+  // Disconnect socket to prevent reuse.
+  handle.socket()->Disconnect();
+  handle.Reset();
+}
+
+TEST_F(TransportClientSocketPoolTest, TagSSLDirectTwoSockets) {
+  const char kGroupName[] = "group_name";
+
+  // Start test server.
+  EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, SSLServerConfig());
+  test_server.AddDefaultHandlers(base::FilePath());
+  ASSERT_TRUE(test_server.Start());
+
+  cert_verifier_.set_default_result(OK);
+  ClientSocketHandle handle;
+  int32_t tag_val1 = 0x12345678;
+  SocketTag tag1(SocketTag::UNSET_UID, tag_val1);
+  int32_t tag_val2 = 0x87654321;
+  SocketTag tag2(getuid(), tag_val2);
+  scoped_refptr<TransportSocketParams> tcp_params =
+      base::MakeRefCounted<TransportSocketParams>(
+          test_server.host_port_pair(), false, OnHostResolutionCallback());
+  scoped_refptr<SSLSocketParams> params(new SSLSocketParams(
+      tcp_params, nullptr, nullptr, test_server.host_port_pair(),
+      GetSSLConfig(), PRIVACY_MODE_DISABLED));
+
+  // Test connect jobs that are orphaned and then adopted, appropriately apply
+  // new tag. Request socket with |tag1|.
+  TestCompletionCallback callback;
+  int rv = handle.Init(
+      kGroupName,
+      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
+          params),
+      LOW, tag1, ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
+      &pool_for_real_sockets_, NetLogWithSource());
+  EXPECT_TRUE(rv == OK || rv == ERR_IO_PENDING) << "Result: " << rv;
+  // Abort and request socket with |tag2|.
+  handle.Reset();
+  TestCompletionCallback callback2;
+  rv = handle.Init(
+      kGroupName,
+      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
+          params),
+      LOW, tag2, ClientSocketPool::RespectLimits::ENABLED, callback2.callback(),
+      &pool_for_real_sockets_, NetLogWithSource());
+  EXPECT_THAT(callback2.GetResult(rv), IsOk());
+  EXPECT_TRUE(handle.socket());
+  EXPECT_TRUE(handle.socket()->IsConnected());
+  // Verify socket has |tag2| applied.
+  uint64_t old_traffic = GetTaggedBytes(tag_val2);
+  const char kRequest[] = "GET / HTTP/1.1\r\n\r\n";
+  scoped_refptr<IOBuffer> write_buffer =
+      base::MakeRefCounted<StringIOBuffer>(kRequest);
+  rv = handle.socket()->Write(write_buffer.get(), strlen(kRequest),
+                              callback2.callback(),
+                              TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(static_cast<int>(strlen(kRequest)), callback2.GetResult(rv));
+  scoped_refptr<IOBufferWithSize> read_buffer =
+      base::MakeRefCounted<IOBufferWithSize>(1);
+  rv = handle.socket()->Read(read_buffer.get(), read_buffer->size(),
+                             callback2.callback());
+  EXPECT_EQ(read_buffer->size(), callback2.GetResult(rv));
+  EXPECT_GT(GetTaggedBytes(tag_val2), old_traffic);
+}
+
+TEST_F(TransportClientSocketPoolTest, TagSSLDirectTwoSocketsFullPool) {
+  const char kGroupName[] = "group_name";
+
+  // Start test server.
+  EmbeddedTestServer test_server(net::EmbeddedTestServer::TYPE_HTTPS);
+  test_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, SSLServerConfig());
+  test_server.AddDefaultHandlers(base::FilePath());
+  ASSERT_TRUE(test_server.Start());
+
+  cert_verifier_.set_default_result(OK);
+  TestCompletionCallback callback;
+  ClientSocketHandle handle;
+  int32_t tag_val1 = 0x12345678;
+  SocketTag tag1(SocketTag::UNSET_UID, tag_val1);
+  int32_t tag_val2 = 0x87654321;
+  SocketTag tag2(getuid(), tag_val2);
+  scoped_refptr<TransportSocketParams> tcp_params =
+      base::MakeRefCounted<TransportSocketParams>(
+          test_server.host_port_pair(), false, OnHostResolutionCallback());
+  scoped_refptr<SSLSocketParams> params(new SSLSocketParams(
+      tcp_params, nullptr, nullptr, test_server.host_port_pair(),
+      GetSSLConfig(), PRIVACY_MODE_DISABLED));
+
+  // Test that sockets paused by a full underlying socket pool are properly
+  // connected and tagged when underlying pool is freed up.
+  // Fill up all slots in TCP pool.
+  ClientSocketHandle tcp_handles[kMaxSocketsPerGroup];
+  int rv;
+  for (auto& tcp_handle : tcp_handles) {
+    rv = tcp_handle.Init(kGroupName,
+                         TransportClientSocketPool::SocketParams::
+                             CreateFromTransportSocketParams(tcp_params),
+                         LOW, tag1, ClientSocketPool::RespectLimits::ENABLED,
+                         callback.callback(), &pool_for_real_sockets_,
+                         NetLogWithSource());
+    EXPECT_THAT(callback.GetResult(rv), IsOk());
+    EXPECT_TRUE(tcp_handle.socket());
+    EXPECT_TRUE(tcp_handle.socket()->IsConnected());
+  }
+  // Request two SSL sockets.
+  ClientSocketHandle handle_to_be_canceled;
+  rv = handle_to_be_canceled.Init(
+      kGroupName,
+      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
+          params),
+      LOW, tag1, ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
+      &pool_for_real_sockets_, NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+  rv = handle.Init(
+      kGroupName,
+      TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
+          params),
+      LOW, tag2, ClientSocketPool::RespectLimits::ENABLED, callback.callback(),
+      &pool_for_real_sockets_, NetLogWithSource());
+  EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
+  // Cancel first request.
+  handle_to_be_canceled.Reset();
+  // Disconnect a TCP socket to free up a slot.
+  tcp_handles[0].socket()->Disconnect();
+  tcp_handles[0].Reset();
+  // Verify |handle| gets a valid tagged socket.
+  EXPECT_THAT(callback.WaitForResult(), IsOk());
+  EXPECT_TRUE(handle.socket());
+  EXPECT_TRUE(handle.socket()->IsConnected());
+  uint64_t old_traffic = GetTaggedBytes(tag_val2);
+  const char kRequest[] = "GET / HTTP/1.1\r\n\r\n";
+  scoped_refptr<IOBuffer> write_buffer =
+      base::MakeRefCounted<StringIOBuffer>(kRequest);
+  rv =
+      handle.socket()->Write(write_buffer.get(), strlen(kRequest),
+                             callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
+  EXPECT_EQ(static_cast<int>(strlen(kRequest)), callback.GetResult(rv));
+  scoped_refptr<IOBufferWithSize> read_buffer =
+      base::MakeRefCounted<IOBufferWithSize>(1);
+  EXPECT_EQ(handle.socket()->Read(read_buffer.get(), read_buffer->size(),
+                                  callback.callback()),
+            ERR_IO_PENDING);
+  EXPECT_THAT(callback.WaitForResult(), read_buffer->size());
+  EXPECT_GT(GetTaggedBytes(tag_val2), old_traffic);
+}
+
+#endif  // defined(OS_ANDROID)
 
 }  // namespace
 
diff --git a/net/socket/websocket_transport_client_socket_pool.cc b/net/socket/websocket_transport_client_socket_pool.cc
index e1647eb..e791b2f6 100644
--- a/net/socket/websocket_transport_client_socket_pool.cc
+++ b/net/socket/websocket_transport_client_socket_pool.cc
@@ -147,7 +147,7 @@
                                  nullptr /* SocketPerformanceWatcherFactory */,
                                  network_quality_estimator_, pool_net_log_,
                                  websocket_endpoint_lock_manager_),
-          connect_job_delegate.get(), nullptr, nullptr, nullptr);
+          connect_job_delegate.get(), nullptr, nullptr);
 
   int result = connect_job_delegate->Connect(std::move(connect_job));
 
diff --git a/net/spdy/spdy_test_util_common.cc b/net/spdy/spdy_test_util_common.cc
index 951f4a4..b3007f34 100644
--- a/net/spdy/spdy_test_util_common.cc
+++ b/net/spdy/spdy_test_util_common.cc
@@ -502,7 +502,8 @@
           ssl_params),
       MEDIUM, key.socket_tag(), ClientSocketPool::RespectLimits::ENABLED,
       callback.callback(),
-      http_session->GetSSLSocketPool(HttpNetworkSession::NORMAL_SOCKET_POOL),
+      http_session->GetTransportSocketPool(
+          HttpNetworkSession::NORMAL_SOCKET_POOL),
       net_log);
   rv = callback.GetResult(rv);
   EXPECT_THAT(rv, IsOk());
diff --git a/net/third_party/quic/core/crypto/ephemeral_key_source.h b/net/third_party/quic/core/crypto/ephemeral_key_source.h
deleted file mode 100644
index 0248891..0000000
--- a/net/third_party/quic/core/crypto/ephemeral_key_source.h
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (c) 2013 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef NET_THIRD_PARTY_QUIC_CORE_CRYPTO_EPHEMERAL_KEY_SOURCE_H_
-#define NET_THIRD_PARTY_QUIC_CORE_CRYPTO_EPHEMERAL_KEY_SOURCE_H_
-
-#include "net/third_party/quic/core/crypto/key_exchange.h"
-#include "net/third_party/quic/core/quic_time.h"
-#include "net/third_party/quic/platform/api/quic_export.h"
-#include "net/third_party/quic/platform/api/quic_string.h"
-#include "net/third_party/quic/platform/api/quic_string_piece.h"
-
-namespace quic {
-
-class QuicRandom;
-
-// EphemeralKeySource manages and rotates ephemeral keys as they can be reused
-// for several connections in a short space of time. Since the implementation
-// of this may involve locking or thread-local data, this interface abstracts
-// that away.
-class QUIC_EXPORT_PRIVATE EphemeralKeySource {
- public:
-  virtual ~EphemeralKeySource() {}
-
-  // CalculateForwardSecureKey generates an ephemeral public/private key pair
-  // using the algorithm represented by |key_exchange_factory|, sets
-  // |*public_value| to the public key and returns the shared key between
-  // |peer_public_value| and the private key. |*public_value| will be sent to
-  // the peer to be used with the peer's private key.
-  virtual QuicString CalculateForwardSecureKey(
-      const KeyExchange::Factory& key_exchange_factory,
-      QuicRandom* rand,
-      QuicTime now,
-      QuicStringPiece peer_public_value,
-      QuicString* public_value) = 0;
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_CORE_CRYPTO_EPHEMERAL_KEY_SOURCE_H_
diff --git a/net/third_party/quic/core/crypto/quic_crypto_server_config.cc b/net/third_party/quic/core/crypto/quic_crypto_server_config.cc
index a54f320..be7c105 100644
--- a/net/third_party/quic/core/crypto/quic_crypto_server_config.cc
+++ b/net/third_party/quic/core/crypto/quic_crypto_server_config.cc
@@ -18,7 +18,6 @@
 #include "net/third_party/quic/core/crypto/crypto_handshake_message.h"
 #include "net/third_party/quic/core/crypto/crypto_utils.h"
 #include "net/third_party/quic/core/crypto/curve25519_key_exchange.h"
-#include "net/third_party/quic/core/crypto/ephemeral_key_source.h"
 #include "net/third_party/quic/core/crypto/key_exchange.h"
 #include "net/third_party/quic/core/crypto/p256_key_exchange.h"
 #include "net/third_party/quic/core/crypto/proof_source.h"
@@ -512,13 +511,11 @@
   QuicReferenceCountedPointer<Config> primary_config;
   {
     QuicReaderMutexLock locked(&configs_lock_);
-
     if (!primary_config_.get()) {
       result->error_code = QUIC_CRYPTO_INTERNAL_ERROR;
       result->error_details = "No configurations loaded";
     } else {
-      if (!next_config_promotion_time_.IsZero() &&
-          next_config_promotion_time_.IsAfter(now)) {
+      if (IsNextConfigReady(now)) {
         configs_lock_.ReaderUnlock();
         configs_lock_.WriterLock();
         SelectNewPrimaryConfig(now);
@@ -785,8 +782,7 @@
     if (!primary_config_) {
       no_primary_config = true;
     } else {
-      if (!next_config_promotion_time_.IsZero() &&
-          next_config_promotion_time_.IsAfter(now)) {
+      if (IsNextConfigReady(now)) {
         configs_lock_.ReaderUnlock();
         configs_lock_.WriterLock();
         SelectNewPrimaryConfig(now);
@@ -1136,30 +1132,14 @@
   }
 
   QuicString forward_secure_public_value;
-  if (GetQuicRestartFlag(quic_no_ephemeral_key_source)) {
-    if (ephemeral_key_source_) {
-      QUIC_BUG << "quic_no_ephemeral_key_source flag is on, but "
-                  "ephemeral_key_source is present";
-    } else {
-      QUIC_RESTART_FLAG_COUNT(quic_no_ephemeral_key_source);
-    }
-  }
-  if (ephemeral_key_source_) {
-    params->forward_secure_premaster_secret =
-        ephemeral_key_source_->CalculateForwardSecureKey(
-            key_exchange_factory, rand, clock->ApproximateNow(), public_value,
-            &forward_secure_public_value);
-  } else {
-    std::unique_ptr<KeyExchange> forward_secure_key_exchange =
-        key_exchange_factory.Create(rand);
-    forward_secure_public_value =
-        QuicString(forward_secure_key_exchange->public_value());
-    if (!forward_secure_key_exchange->CalculateSharedKey(
-            public_value, &params->forward_secure_premaster_secret)) {
-      helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER,
-                  "Invalid public value");
-      return;
-    }
+  std::unique_ptr<KeyExchange> forward_secure_key_exchange =
+      key_exchange_factory.Create(rand);
+  forward_secure_public_value =
+      QuicString(forward_secure_key_exchange->public_value());
+  if (!forward_secure_key_exchange->CalculateSharedKey(
+          public_value, &params->forward_secure_premaster_secret)) {
+    helper.Fail(QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER, "Invalid public value");
+    return;
   }
 
   QuicString forward_secure_hkdf_input;
@@ -1888,11 +1868,6 @@
   return config;
 }
 
-void QuicCryptoServerConfig::SetEphemeralKeySource(
-    std::unique_ptr<EphemeralKeySource> ephemeral_key_source) {
-  ephemeral_key_source_ = std::move(ephemeral_key_source);
-}
-
 void QuicCryptoServerConfig::set_replay_protection(bool on) {
   replay_protection_ = on;
 }
@@ -2101,6 +2076,15 @@
   return false;
 }
 
+bool QuicCryptoServerConfig::IsNextConfigReady(QuicWallTime now) const {
+  if (GetQuicReloadableFlag(quic_fix_config_rotation)) {
+    return !next_config_promotion_time_.IsZero() &&
+           !next_config_promotion_time_.IsAfter(now);
+  }
+  return !next_config_promotion_time_.IsZero() &&
+         next_config_promotion_time_.IsAfter(now);
+}
+
 QuicCryptoServerConfig::Config::Config()
     : channel_id_enabled(false),
       is_primary(false),
diff --git a/net/third_party/quic/core/crypto/quic_crypto_server_config.h b/net/third_party/quic/core/crypto/quic_crypto_server_config.h
index e8f1d4e..ca56990 100644
--- a/net/third_party/quic/core/crypto/quic_crypto_server_config.h
+++ b/net/third_party/quic/core/crypto/quic_crypto_server_config.h
@@ -34,7 +34,6 @@
 namespace quic {
 
 class CryptoHandshakeMessage;
-class EphemeralKeySource;
 class ProofSource;
 class QuicClock;
 class QuicRandom;
@@ -370,12 +369,6 @@
       const CachedNetworkParameters* cached_network_params,
       std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const;
 
-  // SetEphemeralKeySource installs an object that can cache ephemeral keys for
-  // a short period of time. If not set, ephemeral keys will be generated
-  // per-connection.
-  void SetEphemeralKeySource(
-      std::unique_ptr<EphemeralKeySource> ephemeral_key_source);
-
   // set_replay_protection controls whether replay protection is enabled. If
   // replay protection is disabled then no strike registers are needed and
   // frontends can share an orbit value without a shared strike-register.
@@ -772,6 +765,10 @@
       CryptoHandshakeMessage message,
       std::unique_ptr<BuildServerConfigUpdateMessageResultCallback> cb) const;
 
+  // Returns true if the next config promotion should happen now.
+  bool IsNextConfigReady(QuicWallTime now) const
+      SHARED_LOCKS_REQUIRED(configs_lock_);
+
   // replay_protection_ controls whether the server enforces that handshakes
   // aren't replays.
   bool replay_protection_;
@@ -823,10 +820,6 @@
   // ssl_ctx_ contains the server configuration for doing TLS handshakes.
   bssl::UniquePtr<SSL_CTX> ssl_ctx_;
 
-  // ephemeral_key_source_ contains an object that caches ephemeral keys for a
-  // short period of time.
-  std::unique_ptr<EphemeralKeySource> ephemeral_key_source_;
-
   // These fields store configuration values. See the comments for their
   // respective setter functions.
   uint32_t source_address_token_future_secs_;
diff --git a/net/third_party/quic/core/crypto/quic_crypto_server_config_test.cc b/net/third_party/quic/core/crypto/quic_crypto_server_config_test.cc
index 082e7b5..9e1770a 100644
--- a/net/third_party/quic/core/crypto/quic_crypto_server_config_test.cc
+++ b/net/third_party/quic/core/crypto/quic_crypto_server_config_test.cc
@@ -457,6 +457,33 @@
   test_peer_.CheckConfigs({{"a", false}, {"b", true}});
 }
 
+class ValidateCallback : public ValidateClientHelloResultCallback {
+ public:
+  void Run(QuicReferenceCountedPointer<Result> result,
+           std::unique_ptr<ProofSource::Details> /* details */) override {}
+};
+
+TEST_F(CryptoServerConfigsTest, AdvancePrimaryViaValidate) {
+  SetQuicReloadableFlag(quic_fix_config_rotation, true);
+  // Check that a new primary config is enabled at the right time.
+  SetConfigs({{"a", 900, 1}, {"b", 1100, 1}});
+  test_peer_.SelectNewPrimaryConfig(1000);
+  test_peer_.CheckConfigs({{"a", true}, {"b", false}});
+  CryptoHandshakeMessage client_hello;
+  QuicIpAddress client_ip;
+  QuicSocketAddress server_address;
+  QuicTransportVersion version = QUIC_VERSION_99;
+  MockClock clock;
+  QuicReferenceCountedPointer<QuicSignedServerConfig> signed_config(
+      new QuicSignedServerConfig);
+  std::unique_ptr<ValidateClientHelloResultCallback> done_cb(
+      new ValidateCallback);
+  clock.AdvanceTime(QuicTime::Delta::FromSeconds(1100));
+  config_.ValidateClientHello(client_hello, client_ip, server_address, version,
+                              &clock, signed_config, std::move(done_cb));
+  test_peer_.CheckConfigs({{"a", false}, {"b", true}});
+}
+
 TEST_F(CryptoServerConfigsTest, InvalidConfigs) {
   // Ensure that invalid configs don't change anything.
   SetConfigs({{"a", 800, 1}, {"b", 900, 1}, {"c", 1100, 1}});
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 e650364..6fd0fc0 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
@@ -20,6 +20,7 @@
 #include "net/third_party/quic/core/quic_packets.h"
 #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_expect_bug.h"
 #include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_logging.h"
@@ -61,8 +62,6 @@
 #include "net/third_party/quic/tools/quic_simple_server_stream.h"
 #include "net/tools/epoll_server/epoll_server.h"
 
-using net::EpollEvent;
-using net::EpollServer;
 using spdy::kV3LowestPriority;
 using spdy::SETTINGS_MAX_HEADER_LIST_SIZE;
 using spdy::SpdyFramer;
@@ -259,7 +258,7 @@
   explicit ClientDelegate(QuicClient* client) : client_(client) {}
   ~ClientDelegate() override = default;
   void OnCanWrite() override {
-    EpollEvent event(EPOLLOUT);
+    QuicEpollEvent event(EPOLLOUT);
     client_->epoll_network_helper()->OnEvent(client_->GetLatestFD(), &event);
   }
 
@@ -418,7 +417,7 @@
     }
 
     CreateClientWithWriter();
-    static EpollEvent event(EPOLLOUT);
+    static QuicEpollEvent event(EPOLLOUT);
     if (client_writer_ != nullptr) {
       client_writer_->Initialize(
           QuicConnectionPeer::GetHelper(
@@ -1880,7 +1879,7 @@
 
   // Register the new FD for epoll events.
   int new_fd = client_->client()->GetLatestFD();
-  EpollServer* eps = client_->epoll_server();
+  QuicEpollServer* eps = client_->epoll_server();
   eps->RegisterFD(new_fd, client_->client()->epoll_network_helper(),
                   EPOLLIN | EPOLLOUT | EPOLLET);
 
@@ -3129,7 +3128,7 @@
   client->UseWriter(client_writer_);
   client->Connect();
   client_.reset(client);
-  static EpollEvent event(EPOLLOUT);
+  static QuicEpollEvent event(EPOLLOUT);
   client_writer_->Initialize(
       QuicConnectionPeer::GetHelper(
           client_->client()->client_session()->connection()),
diff --git a/net/third_party/quic/core/quic_connection.cc b/net/third_party/quic/core/quic_connection.cc
index 7b0fc5a8..7db206ef 100644
--- a/net/third_party/quic/core/quic_connection.cc
+++ b/net/third_party/quic/core/quic_connection.cc
@@ -350,10 +350,11 @@
                   << "Created connection with connection_id: " << connection_id
                   << " and version: "
                   << QuicVersionToString(transport_version());
-  QUIC_BUG_IF(connection_id.length() != sizeof(uint64_t) &&
+  QUIC_BUG_IF(connection_id.length() != kQuicDefaultConnectionIdLength &&
               transport_version() < QUIC_VERSION_99)
-      << "Cannot use connection ID of length " << connection_id.length()
-      << " with version " << QuicVersionToString(transport_version());
+      << "Cannot use connection ID of length "
+      << static_cast<uint32_t>(connection_id.length()) << " with version "
+      << QuicVersionToString(transport_version());
 
   framer_.set_visitor(this);
   stats_.connection_creation_time = clock_->ApproximateNow();
diff --git a/net/third_party/quic/core/quic_dispatcher.cc b/net/third_party/quic/core/quic_dispatcher.cc
index 96b5aaf..4707173 100644
--- a/net/third_party/quic/core/quic_dispatcher.cc
+++ b/net/third_party/quic/core/quic_dispatcher.cc
@@ -561,9 +561,7 @@
     // expediency. Stop doing this when removing flag
     // quic_always_reset_ietf_connections.
     if (!GetQuicReloadableFlag(quic_always_reset_ietf_connections) &&
-        (!GetQuicReloadableFlag(
-             quic_send_reset_for_post_handshake_connections_without_termination_packets) ||  // NOLINT
-         (source == ConnectionCloseSource::FROM_PEER))) {
+        source == ConnectionCloseSource::FROM_PEER) {
       action = QuicTimeWaitListManager::DO_NOTHING;
     } else if (!connection->IsHandshakeConfirmed()) {
       QUIC_CODE_COUNT(quic_v44_add_to_time_wait_list_with_handshake_failed);
diff --git a/net/third_party/quic/core/quic_dispatcher.h b/net/third_party/quic/core/quic_dispatcher.h
index 56bbd5f..885a772 100644
--- a/net/third_party/quic/core/quic_dispatcher.h
+++ b/net/third_party/quic/core/quic_dispatcher.h
@@ -348,7 +348,7 @@
 
   // Please do not use this method.
   // TODO(fayang): Remove this method when deprecating
-  // quic_proxy_use_real_packet_format_when_reject flag.
+  // quic_fix_last_packet_is_ietf_quic flag.
   PacketHeaderFormat GetLastPacketFormat() const;
 
  private:
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 12079bd..e04e8dcd 100644
--- a/net/third_party/quic/core/quic_epoll_alarm_factory.cc
+++ b/net/third_party/quic/core/quic_epoll_alarm_factory.cc
@@ -12,8 +12,8 @@
 
 class QuicEpollAlarm : public QuicAlarm {
  public:
-  QuicEpollAlarm(net::EpollServer* epoll_server,
-                 QuicArenaScopedPtr<Delegate> delegate)
+  QuicEpollAlarm(QuicEpollServer* epoll_server,
+                 QuicArenaScopedPtr<QuicAlarm::Delegate> delegate)
       : QuicAlarm(std::move(delegate)),
         epoll_server_(epoll_server),
         epoll_alarm_impl_(this) {}
@@ -31,12 +31,13 @@
   }
 
  private:
-  class EpollAlarmImpl : public net::EpollAlarm {
+  class EpollAlarmImpl : public QuicEpollAlarmBase {
    public:
     explicit EpollAlarmImpl(QuicEpollAlarm* alarm) : alarm_(alarm) {}
 
-    int64_t OnAlarm() override {
-      net::EpollAlarm::OnAlarm();
+    // Use the same integer type as the base class.
+    int64_t /* allow-non-std-int */ OnAlarm() override {
+      QuicEpollAlarmBase::OnAlarm();
       alarm_->Fire();
       // Fire will take care of registering the alarm, if needed.
       return 0;
@@ -46,13 +47,13 @@
     QuicEpollAlarm* alarm_;
   };
 
-  net::EpollServer* epoll_server_;
+  QuicEpollServer* epoll_server_;
   EpollAlarmImpl epoll_alarm_impl_;
 };
 
 }  // namespace
 
-QuicEpollAlarmFactory::QuicEpollAlarmFactory(net::EpollServer* epoll_server)
+QuicEpollAlarmFactory::QuicEpollAlarmFactory(QuicEpollServer* epoll_server)
     : epoll_server_(epoll_server) {}
 
 QuicEpollAlarmFactory::~QuicEpollAlarmFactory() = default;
diff --git a/net/third_party/quic/core/quic_epoll_alarm_factory.h b/net/third_party/quic/core/quic_epoll_alarm_factory.h
index 9fe432d..6c95bbf 100644
--- a/net/third_party/quic/core/quic_epoll_alarm_factory.h
+++ b/net/third_party/quic/core/quic_epoll_alarm_factory.h
@@ -7,6 +7,8 @@
 
 #include "net/third_party/quic/core/quic_alarm.h"
 #include "net/third_party/quic/core/quic_alarm_factory.h"
+#include "net/third_party/quic/core/quic_one_block_arena.h"
+#include "net/third_party/quic/platform/api/quic_epoll.h"
 
 namespace quic {}  // namespace quic
 namespace net {
@@ -17,7 +19,7 @@
 // Creates alarms that use the supplied net::EpollServer for timing and firing.
 class QuicEpollAlarmFactory : public QuicAlarmFactory {
  public:
-  explicit QuicEpollAlarmFactory(net::EpollServer* epoll_server);
+  explicit QuicEpollAlarmFactory(QuicEpollServer* eps);
   QuicEpollAlarmFactory(const QuicEpollAlarmFactory&) = delete;
   QuicEpollAlarmFactory& operator=(const QuicEpollAlarmFactory&) = delete;
   ~QuicEpollAlarmFactory() override;
@@ -29,7 +31,7 @@
       QuicConnectionArena* arena) override;
 
  private:
-  net::EpollServer* epoll_server_;  // Not owned.
+  QuicEpollServer* epoll_server_;  // Not owned.
 };
 
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_epoll_connection_helper.cc b/net/third_party/quic/core/quic_epoll_connection_helper.cc
index 10ec875..248dba60 100644
--- a/net/third_party/quic/core/quic_epoll_connection_helper.cc
+++ b/net/third_party/quic/core/quic_epoll_connection_helper.cc
@@ -14,7 +14,7 @@
 namespace quic {
 
 QuicEpollConnectionHelper::QuicEpollConnectionHelper(
-    net::EpollServer* epoll_server,
+    QuicEpollServer* epoll_server,
     QuicAllocator type)
     : clock_(epoll_server),
       random_generator_(QuicRandom::GetInstance()),
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 9bf7335f..91c506e 100644
--- a/net/third_party/quic/core/quic_epoll_connection_helper.h
+++ b/net/third_party/quic/core/quic_epoll_connection_helper.h
@@ -18,12 +18,10 @@
 #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_time.h"
+#include "net/third_party/quic/platform/api/quic_epoll.h"
 #include "net/third_party/quic/platform/impl/quic_epoll_clock.h"
 
 namespace quic {}  // namespace quic
-namespace net {
-class EpollServer;
-}  // namespace net
 namespace quic {
 class QuicRandom;
 
@@ -33,7 +31,7 @@
 
 class QuicEpollConnectionHelper : public QuicConnectionHelperInterface {
  public:
-  QuicEpollConnectionHelper(net::EpollServer* eps, QuicAllocator allocator);
+  QuicEpollConnectionHelper(QuicEpollServer* eps, QuicAllocator allocator);
   QuicEpollConnectionHelper(const QuicEpollConnectionHelper&) = delete;
   QuicEpollConnectionHelper& operator=(const QuicEpollConnectionHelper&) =
       delete;
diff --git a/net/third_party/quic/core/quic_error_codes.cc b/net/third_party/quic/core/quic_error_codes.cc
index 3f8b588..e55b60d 100644
--- a/net/third_party/quic/core/quic_error_codes.cc
+++ b/net/third_party/quic/core/quic_error_codes.cc
@@ -155,6 +155,7 @@
     RETURN_STRING_LITERAL(QUIC_STREAM_ID_BLOCKED_ERROR);
     RETURN_STRING_LITERAL(QUIC_MAX_STREAM_ID_ERROR);
     RETURN_STRING_LITERAL(QUIC_HTTP_DECODER_ERROR);
+    RETURN_STRING_LITERAL(QUIC_STALE_CONNECTION_CANCELLED);
 
     RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
     // Intentionally have no default case, so we'll break the build
diff --git a/net/third_party/quic/core/quic_error_codes.h b/net/third_party/quic/core/quic_error_codes.h
index 114cc62..6c7761b 100644
--- a/net/third_party/quic/core/quic_error_codes.h
+++ b/net/third_party/quic/core/quic_error_codes.h
@@ -325,9 +325,11 @@
   QUIC_MAX_STREAM_ID_ERROR = 119,
   // Error in Http decoder
   QUIC_HTTP_DECODER_ERROR = 120,
+  // Connection from stale host needs to be cancelled.
+  QUIC_STALE_CONNECTION_CANCELLED = 121,
 
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 121,
+  QUIC_LAST_ERROR = 122,
 };
 // QuicErrorCodes is encoded as a single octet on-the-wire.
 static_assert(static_cast<int>(QUIC_LAST_ERROR) <=
diff --git a/net/third_party/quic/core/quic_framer.cc b/net/third_party/quic/core/quic_framer.cc
index d24ce42..49fde715 100644
--- a/net/third_party/quic/core/quic_framer.cc
+++ b/net/third_party/quic/core/quic_framer.cc
@@ -1726,8 +1726,8 @@
                       kQuicDefaultConnectionIdLength &&
                   transport_version() < QUIC_VERSION_99)
           << "Cannot use connection ID of length "
-          << header.destination_connection_id.length() << " with version "
-          << QuicVersionToString(transport_version());
+          << static_cast<uint32_t>(header.destination_connection_id.length())
+          << " with version " << QuicVersionToString(transport_version());
       public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID;
       if (perspective_ == Perspective::IS_CLIENT) {
         public_flags |= PACKET_PUBLIC_FLAGS_8BYTE_CONNECTION_ID_OLD;
@@ -1808,8 +1808,8 @@
                   kQuicDefaultConnectionIdLength &&
               transport_version() < QUIC_VERSION_99)
       << "Cannot use connection ID of length "
-      << header.destination_connection_id.length() << " with version "
-      << QuicVersionToString(transport_version());
+      << static_cast<uint32_t>(header.destination_connection_id.length())
+      << " with version " << QuicVersionToString(transport_version());
   if (!AppendIetfHeaderTypeByte(header, writer)) {
     return false;
   }
diff --git a/net/third_party/quic/core/quic_framer.h b/net/third_party/quic/core/quic_framer.h
index 79b35a3..cc1da0f 100644
--- a/net/third_party/quic/core/quic_framer.h
+++ b/net/third_party/quic/core/quic_framer.h
@@ -516,7 +516,7 @@
   // Returns header wire format of last received packet.
   // Please do not use this method.
   // TODO(fayang): Remove last_header_form_ when deprecating
-  // quic_proxy_use_real_packet_format_when_reject flag.
+  // quic_fix_last_packet_is_ietf_quic flag.
   PacketHeaderFormat GetLastPacketFormat() const;
 
   void set_validate_flags(bool value) { validate_flags_ = value; }
diff --git a/net/third_party/quic/core/quic_lru_cache.h b/net/third_party/quic/core/quic_lru_cache.h
index 7c6e540..4422cda6 100644
--- a/net/third_party/quic/core/quic_lru_cache.h
+++ b/net/third_party/quic/core/quic_lru_cache.h
@@ -10,7 +10,6 @@
 #include "net/third_party/quic/platform/api/quic_containers.h"
 #include "net/third_party/quic/platform/api/quic_flag_utils.h"
 #include "net/third_party/quic/platform/api/quic_flags.h"
-#include "net/third_party/quic/platform/api/quic_lru_cache.h"
 
 namespace quic {
 
@@ -18,13 +17,12 @@
 // This cache CANNOT be shared by multiple threads (even with locks) because
 // Value* returned by Lookup() can be invalid if the entry is evicted by other
 // threads.
-// TODO(vasilvv): rename this class when quic_new_lru_cache flag is deprecated.
 template <class K, class V>
-class QuicLRUCacheNew {
+class QuicLRUCache {
  public:
-  explicit QuicLRUCacheNew(size_t capacity) : capacity_(capacity) {}
-  QuicLRUCacheNew(const QuicLRUCacheNew&) = delete;
-  QuicLRUCacheNew& operator=(const QuicLRUCacheNew&) = delete;
+  explicit QuicLRUCache(size_t capacity) : capacity_(capacity) {}
+  QuicLRUCache(const QuicLRUCache&) = delete;
+  QuicLRUCache& operator=(const QuicLRUCache&) = delete;
 
   // Inserts one unit of |key|, |value| pair to the cache. Cache takes ownership
   // of inserted |value|.
@@ -45,7 +43,6 @@
   // value is guaranteed to be valid until Insert or Clear.
   // Else return nullptr.
   V* Lookup(const K& key) {
-    QUIC_RELOADABLE_FLAG_COUNT(quic_new_lru_cache);
     auto it = cache_.find(key);
     if (it == cache_.end()) {
       return nullptr;
@@ -72,63 +69,6 @@
   const size_t capacity_;
 };
 
-// TODO(vasilvv): remove this class when quic_new_lru_cache flag is deprecated.
-template <class K, class V>
-class QuicLRUCache {
- public:
-  explicit QuicLRUCache(size_t capacity)
-      : QuicLRUCache(capacity, GetQuicReloadableFlag(quic_new_lru_cache)) {}
-  QuicLRUCache(size_t capacity, bool use_new)
-      : new_(capacity), old_(capacity), use_new_(use_new) {}
-  QuicLRUCache(const QuicLRUCache&) = delete;
-  QuicLRUCache& operator=(const QuicLRUCache&) = delete;
-
-  void Insert(const K& key, std::unique_ptr<V> value) {
-    if (use_new_) {
-      new_.Insert(key, std::move(value));
-    } else {
-      old_.Insert(key, std::move(value));
-    }
-  }
-
-  V* Lookup(const K& key) {
-    if (use_new_) {
-      return new_.Lookup(key);
-    } else {
-      return old_.Lookup(key);
-    }
-  }
-
-  void Clear() {
-    if (use_new_) {
-      new_.Clear();
-    } else {
-      old_.Clear();
-    }
-  }
-
-  size_t MaxSize() const {
-    if (use_new_) {
-      return new_.MaxSize();
-    } else {
-      return old_.MaxSize();
-    }
-  }
-
-  size_t Size() const {
-    if (use_new_) {
-      return new_.Size();
-    } else {
-      return old_.Size();
-    }
-  }
-
- private:
-  QuicLRUCacheNew<K, V> new_;
-  QuicLRUCacheOld<K, V> old_;
-  bool use_new_;
-};
-
 }  // namespace quic
 
 #endif  // NET_THIRD_PARTY_QUIC_CORE_QUIC_LRU_CACHE_H_
diff --git a/net/third_party/quic/core/quic_lru_cache_test.cc b/net/third_party/quic/core/quic_lru_cache_test.cc
index 2670920..a6c94bb 100644
--- a/net/third_party/quic/core/quic_lru_cache_test.cc
+++ b/net/third_party/quic/core/quic_lru_cache_test.cc
@@ -16,12 +16,8 @@
   uint32_t value;
 };
 
-class QuicLRUCacheTest : public QuicTestWithParam<bool> {};
-
-INSTANTIATE_TEST_SUITE_P(QuicLRUCacheTests, QuicLRUCacheTest, testing::Bool());
-
-TEST_P(QuicLRUCacheTest, InsertAndLookup) {
-  QuicLRUCache<int, CachedItem> cache(5, GetParam());
+TEST(QuicLRUCacheTest, InsertAndLookup) {
+  QuicLRUCache<int, CachedItem> cache(5);
   EXPECT_EQ(nullptr, cache.Lookup(1));
   EXPECT_EQ(0u, cache.Size());
   EXPECT_EQ(5u, cache.MaxSize());
@@ -48,8 +44,8 @@
   EXPECT_EQ(0u, cache.Size());
 }
 
-TEST_P(QuicLRUCacheTest, Eviction) {
-  QuicLRUCache<int, CachedItem> cache(3, GetParam());
+TEST(QuicLRUCacheTest, Eviction) {
+  QuicLRUCache<int, CachedItem> cache(3);
 
   for (size_t i = 1; i <= 4; ++i) {
     std::unique_ptr<CachedItem> item(new CachedItem(10 + i));
diff --git a/net/third_party/quic/core/quic_packet_number.cc b/net/third_party/quic/core/quic_packet_number.cc
index ea6456b6..5da17ee 100644
--- a/net/third_party/quic/core/quic_packet_number.cc
+++ b/net/third_party/quic/core/quic_packet_number.cc
@@ -5,21 +5,18 @@
 #include "net/third_party/quic/core/quic_packet_number.h"
 
 namespace quic {
-namespace {
-const uint64_t kUninitializedPacketNumber = 0;
-}  // namespace
 
 QuicPacketNumber::QuicPacketNumber()
-    : packet_number_(kUninitializedPacketNumber) {}
+    : packet_number_(UninitializedPacketNumber()) {}
 
 QuicPacketNumber::QuicPacketNumber(uint64_t packet_number)
     : packet_number_(packet_number) {
-  DCHECK_NE(kUninitializedPacketNumber, packet_number)
+  DCHECK_NE(UninitializedPacketNumber(), packet_number)
       << "Use default constructor for uninitialized packet number";
 }
 
 void QuicPacketNumber::Clear() {
-  packet_number_ = kUninitializedPacketNumber;
+  packet_number_ = UninitializedPacketNumber();
 }
 
 uint64_t QuicPacketNumber::Hash() const {
@@ -33,44 +30,85 @@
 }
 
 bool QuicPacketNumber::IsInitialized() const {
-  return packet_number_ != kUninitializedPacketNumber;
+  return packet_number_ != UninitializedPacketNumber();
 }
 
 QuicPacketNumber& QuicPacketNumber::operator++() {
-  DCHECK(IsInitialized() && ToUint64() < std::numeric_limits<uint64_t>::max());
+#ifndef NDEBUG
+  DCHECK(IsInitialized());
+  if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max() - 1);
+  } else {
+    DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max());
+  }
+#endif
   packet_number_++;
   return *this;
 }
 
 QuicPacketNumber QuicPacketNumber::operator++(int) {
-  DCHECK(IsInitialized() && ToUint64() < std::numeric_limits<uint64_t>::max());
+#ifndef NDEBUG
+  DCHECK(IsInitialized());
+  if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max() - 1);
+  } else {
+    DCHECK_LT(ToUint64(), std::numeric_limits<uint64_t>::max());
+  }
+#endif
   QuicPacketNumber previous(*this);
   packet_number_++;
   return previous;
 }
 
 QuicPacketNumber& QuicPacketNumber::operator--() {
-  DCHECK(IsInitialized() && ToUint64() > 1);
+#ifndef NDEBUG
+  DCHECK(IsInitialized());
+  if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    DCHECK_GE(ToUint64(), 1UL);
+  } else {
+    DCHECK_GT(ToUint64(), 1UL);
+  }
+#endif
   packet_number_--;
   return *this;
 }
 
 QuicPacketNumber QuicPacketNumber::operator--(int) {
-  DCHECK(IsInitialized() && ToUint64() > 1);
+#ifndef NDEBUG
+  DCHECK(IsInitialized());
+  if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    DCHECK_GE(ToUint64(), 1UL);
+  } else {
+    DCHECK_GT(ToUint64(), 1UL);
+  }
+#endif
   QuicPacketNumber previous(*this);
   packet_number_--;
   return previous;
 }
 
 QuicPacketNumber& QuicPacketNumber::operator+=(uint64_t delta) {
-  DCHECK(IsInitialized() &&
-         std::numeric_limits<uint64_t>::max() - ToUint64() >= delta);
+#ifndef NDEBUG
+  DCHECK(IsInitialized());
+  if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    DCHECK_GT(std::numeric_limits<uint64_t>::max() - ToUint64(), delta);
+  } else {
+    DCHECK_GE(std::numeric_limits<uint64_t>::max() - ToUint64(), delta);
+  }
+#endif
   packet_number_ += delta;
   return *this;
 }
 
 QuicPacketNumber& QuicPacketNumber::operator-=(uint64_t delta) {
-  DCHECK(IsInitialized() && ToUint64() > delta);
+#ifndef NDEBUG
+  DCHECK(IsInitialized());
+  if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    DCHECK_GE(ToUint64(), delta);
+  } else {
+    DCHECK_GT(ToUint64(), delta);
+  }
+#endif
   packet_number_ -= delta;
   return *this;
 }
@@ -84,4 +122,11 @@
   return os;
 }
 
+// static
+uint64_t QuicPacketNumber::UninitializedPacketNumber() {
+  return GetQuicRestartFlag(quic_uint64max_uninitialized_pn)
+             ? std::numeric_limits<uint64_t>::max()
+             : 0;
+}
+
 }  // namespace quic
diff --git a/net/third_party/quic/core/quic_packet_number.h b/net/third_party/quic/core/quic_packet_number.h
index c78ea37..d73795f 100644
--- a/net/third_party/quic/core/quic_packet_number.h
+++ b/net/third_party/quic/core/quic_packet_number.h
@@ -9,6 +9,7 @@
 #include <ostream>
 
 #include "net/third_party/quic/platform/api/quic_export.h"
+#include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_logging.h"
 #include "net/third_party/quic/platform/api/quic_string.h"
 #include "net/third_party/quic/platform/api/quic_uint128.h"
@@ -40,17 +41,17 @@
   bool IsInitialized() const;
 
   // REQUIRES: IsInitialized() == true && ToUint64() <
-  // numeric_limits<uint64_t>::max().
+  // numeric_limits<uint64_t>::max() - 1.
   QuicPacketNumber& operator++();
   QuicPacketNumber operator++(int);
-  // REQUIRES: IsInitialized() == true && ToUint64() > 1.
+  // REQUIRES: IsInitialized() == true && ToUint64() >= 1.
   QuicPacketNumber& operator--();
   QuicPacketNumber operator--(int);
 
   // REQUIRES: IsInitialized() == true && numeric_limits<uint64_t>::max() -
-  // ToUint64() >= |delta|.
+  // ToUint64() > |delta|.
   QuicPacketNumber& operator+=(uint64_t delta);
-  // REQUIRES: IsInitialized() == true && ToUint64() > |delta|.
+  // REQUIRES: IsInitialized() == true && ToUint64() >= |delta|.
   QuicPacketNumber& operator-=(uint64_t delta);
 
   QUIC_EXPORT_PRIVATE friend std::ostream& operator<<(
@@ -66,15 +67,18 @@
   friend inline bool operator>(QuicPacketNumber lhs, QuicPacketNumber rhs);
   friend inline bool operator>=(QuicPacketNumber lhs, QuicPacketNumber rhs);
 
-  // REQUIRES: numeric_limits<uint64_t>::max() - lhs.ToUint64() >= |delta|.
+  // REQUIRES: numeric_limits<uint64_t>::max() - lhs.ToUint64() > |delta|.
   friend inline QuicPacketNumber operator+(QuicPacketNumber lhs,
                                            uint64_t delta);
-  // REQUIRES: lhs.ToUint64() > |delta|.
+  // REQUIRES: lhs.ToUint64() >= |delta|.
   friend inline QuicPacketNumber operator-(QuicPacketNumber lhs,
                                            uint64_t delta);
   // REQUIRES: lhs >= rhs.
   friend inline uint64_t operator-(QuicPacketNumber lhs, QuicPacketNumber rhs);
 
+  // The sentinel value representing an uninitialized packet number.
+  static uint64_t UninitializedPacketNumber();
+
   uint64_t packet_number_;
 };
 
@@ -116,13 +120,26 @@
 }
 
 inline QuicPacketNumber operator+(QuicPacketNumber lhs, uint64_t delta) {
-  DCHECK(lhs.IsInitialized() &&
-         std::numeric_limits<uint64_t>::max() - lhs.ToUint64() >= delta);
+#ifndef NDEBUG
+  DCHECK(lhs.IsInitialized());
+  if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    DCHECK_GT(std::numeric_limits<uint64_t>::max() - lhs.ToUint64(), delta);
+  } else {
+    DCHECK_GE(std::numeric_limits<uint64_t>::max() - lhs.ToUint64(), delta);
+  }
+#endif
   return QuicPacketNumber(lhs.packet_number_ + delta);
 }
 
 inline QuicPacketNumber operator-(QuicPacketNumber lhs, uint64_t delta) {
-  DCHECK(lhs.IsInitialized() && lhs.ToUint64() > delta);
+#ifndef NDEBUG
+  DCHECK(lhs.IsInitialized());
+  if (GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    DCHECK_GE(lhs.ToUint64(), delta);
+  } else {
+    DCHECK_GT(lhs.ToUint64(), delta);
+  }
+#endif
   return QuicPacketNumber(lhs.packet_number_ - delta);
 }
 
diff --git a/net/third_party/quic/core/quic_packet_number_test.cc b/net/third_party/quic/core/quic_packet_number_test.cc
index ded7ba6..592f977 100644
--- a/net/third_party/quic/core/quic_packet_number_test.cc
+++ b/net/third_party/quic/core/quic_packet_number_test.cc
@@ -4,6 +4,7 @@
 
 #include "net/third_party/quic/core/quic_packet_number.h"
 
+#include "net/third_party/quic/platform/api/quic_flags.h"
 #include "net/third_party/quic/platform/api/quic_test.h"
 
 namespace quic {
@@ -23,12 +24,22 @@
   num2.Clear();
   EXPECT_FALSE(num2.IsInitialized());
 
-  QuicPacketNumber num3(std::numeric_limits<uint64_t>::max());
-  EXPECT_TRUE(num3.IsInitialized());
-  EXPECT_EQ(std::numeric_limits<uint64_t>::max(), num3.ToUint64());
-  EXPECT_EQ(std::numeric_limits<uint64_t>::max(), num3.Hash());
-  num3.Clear();
-  EXPECT_FALSE(num3.IsInitialized());
+  if (!GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    QuicPacketNumber num3(std::numeric_limits<uint64_t>::max());
+    EXPECT_TRUE(num3.IsInitialized());
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(), num3.ToUint64());
+    EXPECT_EQ(std::numeric_limits<uint64_t>::max(), num3.Hash());
+    num3.Clear();
+    EXPECT_FALSE(num3.IsInitialized());
+    return;
+  }
+
+  QuicPacketNumber num4(0);
+  EXPECT_TRUE(num4.IsInitialized());
+  EXPECT_EQ(0u, num4.ToUint64());
+  EXPECT_EQ(0u, num4.Hash());
+  num4.Clear();
+  EXPECT_FALSE(num4.IsInitialized());
 }
 
 TEST(QuicPacketNumberTest, Operators) {
@@ -40,6 +51,29 @@
 
   EXPECT_EQ(QuicPacketNumber(101), ++num);
   EXPECT_EQ(QuicPacketNumber(100), --num);
+
+  if (!GetQuicRestartFlag(quic_uint64max_uninitialized_pn)) {
+    QuicPacketNumber num2(std::numeric_limits<uint64_t>::max());
+    EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max()), num2--);
+    EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max() - 1), num2);
+    EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max() - 2),
+              --num2);
+
+    EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max() - 2),
+              num2++);
+    EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max() - 1), num2);
+    EXPECT_EQ(QuicPacketNumber(std::numeric_limits<uint64_t>::max()), ++num2);
+    return;
+  }
+
+  QuicPacketNumber num3(0);
+  EXPECT_EQ(QuicPacketNumber(0), num3++);
+  EXPECT_EQ(QuicPacketNumber(1), num3);
+  EXPECT_EQ(QuicPacketNumber(2), ++num3);
+
+  EXPECT_EQ(QuicPacketNumber(2), num3--);
+  EXPECT_EQ(QuicPacketNumber(1), num3);
+  EXPECT_EQ(QuicPacketNumber(0), --num3);
 }
 
 }  // namespace
diff --git a/net/third_party/quic/core/quic_session.cc b/net/third_party/quic/core/quic_session.cc
index e9fd007d..a18da979 100644
--- a/net/third_party/quic/core/quic_session.cc
+++ b/net/third_party/quic/core/quic_session.cc
@@ -949,10 +949,7 @@
       // Discard originally encrypted packets, since they can't be decrypted by
       // the peer.
       NeuterUnencryptedData();
-      if (GetQuicReloadableFlag(quic_optimize_encryption_established)) {
-        QUIC_RELOADABLE_FLAG_COUNT(quic_optimize_encryption_established);
-        is_handshake_confirmed_ = true;
-      }
+      is_handshake_confirmed_ = true;
       break;
 
     default:
diff --git a/net/third_party/quic/platform/api/quic_epoll.h b/net/third_party/quic/platform/api/quic_epoll.h
new file mode 100644
index 0000000..f64a95be
--- /dev/null
+++ b/net/third_party/quic/platform/api/quic_epoll.h
@@ -0,0 +1,24 @@
+// 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.
+
+// 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_
+
+#include "net/third_party/quic/platform/impl/quic_epoll_impl.h"
+
+namespace quic {
+
+using QuicEpollServer = QuicEpollServerImpl;
+using QuicEpollEvent = QuicEpollEventImpl;
+using QuicEpollAlarmBase = QuicEpollAlarmBaseImpl;
+using QuicEpollCallbackInterface = QuicEpollCallbackInterfaceImpl;
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_EPOLL_H_
diff --git a/net/third_party/quic/platform/api/quic_goog_cc_sender.h b/net/third_party/quic/platform/api/quic_goog_cc_sender.h
deleted file mode 100644
index e411a8b..0000000
--- a/net/third_party/quic/platform/api/quic_goog_cc_sender.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright (c) 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 NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_GOOG_CC_SENDER_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_GOOG_CC_SENDER_H_
-
-#include "net/third_party/quic/core/congestion_control/rtt_stats.h"
-#include "net/third_party/quic/core/congestion_control/send_algorithm_interface.h"
-#include "net/third_party/quic/core/crypto/quic_random.h"
-#include "net/third_party/quic/core/quic_connection_stats.h"
-#include "net/third_party/quic/core/quic_types.h"
-#include "net/third_party/quic/core/quic_unacked_packet_map.h"
-#include "net/third_party/quic/platform/api/quic_clock.h"
-#include "net/third_party/quic/platform/impl/quic_goog_cc_sender_impl.h"
-
-namespace quic {
-
-// Interface for creating a GoogCC SendAlgorithmInterface.
-// TODO(b/122312335): Remove this file.
-SendAlgorithmInterface* CreateGoogCcSender(
-    const QuicClock* clock,
-    const RttStats* rtt_stats,
-    const QuicUnackedPacketMap* unacked_packets,
-    QuicRandom* random,
-    QuicConnectionStats* stats,
-    QuicPacketCount initial_congestion_window,
-    QuicPacketCount max_congestion_window) {
-  return CreateGoogCcSenderImpl(clock, rtt_stats, unacked_packets, random,
-                                stats, initial_congestion_window,
-                                max_congestion_window);
-}
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_GOOG_CC_SENDER_H_
diff --git a/net/third_party/quic/platform/api/quic_lru_cache.h b/net/third_party/quic/platform/api/quic_lru_cache.h
deleted file mode 100644
index ec3a3e2..0000000
--- a/net/third_party/quic/platform/api/quic_lru_cache.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// Copyright (c) 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 NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_LRU_CACHE_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_LRU_CACHE_H_
-
-#include <memory>
-
-#include "net/third_party/quic/platform/impl/quic_lru_cache_impl.h"
-
-namespace quic {
-
-// A LRU cache that maps from type Key to Value* in QUIC.
-// This cache CANNOT be shared by multiple threads (even with locks) because
-// Value* returned by Lookup() can be invalid if the entry is evicted by other
-// threads.
-template <class K, class V>
-class QuicLRUCacheOld {
- public:
-  explicit QuicLRUCacheOld(int64_t total_units) : impl_(total_units) {}
-  QuicLRUCacheOld(const QuicLRUCacheOld&) = delete;
-  QuicLRUCacheOld& operator=(const QuicLRUCacheOld&) = delete;
-
-  // Inserts one unit of |key|, |value| pair to the cache. Cache takes ownership
-  // of inserted |value|.
-  void Insert(const K& key, std::unique_ptr<V> value) {
-    impl_.Insert(key, std::move(value));
-  }
-
-  // If cache contains an entry for |key|, return a pointer to it. This returned
-  // value is guaranteed to be valid until Insert or Clear.
-  // Else return nullptr.
-  V* Lookup(const K& key) { return impl_.Lookup(key); }
-
-  // Removes all entries from the cache. This method MUST be called before
-  // destruction.
-  void Clear() { impl_.Clear(); }
-
-  // Returns maximum size of the cache.
-  int64_t MaxSize() const { return impl_.MaxSize(); }
-
-  // Returns current size of the cache.
-  int64_t Size() const { return impl_.Size(); }
-
- private:
-  QuicLRUCacheImpl<K, V> impl_;
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_API_QUIC_LRU_CACHE_H_
diff --git a/net/third_party/quic/platform/impl/quic_epoll_impl.h b/net/third_party/quic/platform/impl/quic_epoll_impl.h
new file mode 100644
index 0000000..6a909cd
--- /dev/null
+++ b/net/third_party/quic/platform/impl/quic_epoll_impl.h
@@ -0,0 +1,24 @@
+// 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.
+
+// 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_IMPL_QUIC_EPOLL_IMPL_H_
+#define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_EPOLL_IMPL_H_
+
+#include "net/tools/epoll_server/epoll_server.h"
+
+namespace quic {
+
+using QuicEpollServerImpl = ::net::EpollServer;
+using QuicEpollEventImpl = ::net::EpollEvent;
+using QuicEpollAlarmBaseImpl = ::net::EpollAlarm;
+using QuicEpollCallbackInterfaceImpl = ::net::EpollCallbackInterface;
+
+}  // namespace quic
+
+#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_EPOLL_IMPL_H_
diff --git a/net/third_party/quic/platform/impl/quic_goog_cc_sender_impl.h b/net/third_party/quic/platform/impl/quic_goog_cc_sender_impl.h
deleted file mode 100644
index 47f9067..0000000
--- a/net/third_party/quic/platform/impl/quic_goog_cc_sender_impl.h
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 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 NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_GOOG_CC_SENDER_IMPL_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_GOOG_CC_SENDER_IMPL_H_
-
-#include "net/third_party/quic/core/congestion_control/bbr_sender.h"
-#include "net/third_party/quic/core/congestion_control/rtt_stats.h"
-#include "net/third_party/quic/core/congestion_control/send_algorithm_interface.h"
-#include "net/third_party/quic/core/crypto/quic_random.h"
-#include "net/third_party/quic/core/quic_connection_stats.h"
-#include "net/third_party/quic/core/quic_types.h"
-#include "net/third_party/quic/core/quic_unacked_packet_map.h"
-#include "net/third_party/quic/platform/api/quic_clock.h"
-
-namespace quic {
-
-// Implementation for creating a GoogCC SendAlgorithmInterface.
-SendAlgorithmInterface* CreateGoogCcSenderImpl(
-    const QuicClock* clock,
-    const RttStats* rtt_stats,
-    const QuicUnackedPacketMap* unacked_packets,
-    QuicRandom* random,
-    QuicConnectionStats* stats,
-    QuicPacketCount initial_congestion_window,
-    QuicPacketCount max_congestion_window) {
-  // TODO(mellem):  Switch to GoogCCSender once it exists.
-  return new BbrSender(rtt_stats, unacked_packets, initial_congestion_window,
-                       max_congestion_window, random);
-}
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_GOOG_CC_SENDER_IMPL_H_
diff --git a/net/third_party/quic/platform/impl/quic_lru_cache_impl.h b/net/third_party/quic/platform/impl/quic_lru_cache_impl.h
deleted file mode 100644
index 527173ff..0000000
--- a/net/third_party/quic/platform/impl/quic_lru_cache_impl.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 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 NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_LRU_CACHE_IMPL_H_
-#define NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_LRU_CACHE_IMPL_H_
-
-#include "base/containers/mru_cache.h"
-
-namespace quic {
-
-template <class K, class V>
-class QuicLRUCacheImpl {
- public:
-  explicit QuicLRUCacheImpl(int64_t total_units) : mru_cache_(total_units) {}
-
-  // Inserts one unit of |key|, |value| pair to the cache.
-  void Insert(const K& key, std::unique_ptr<V> value) {
-    mru_cache_.Put(key, std::move(value));
-  }
-
-  // If cache contains an entry for |key|, return a pointer to it. This returned
-  // value is guaranteed to be valid until Insert or Clear.
-  // Else return nullptr.
-  V* Lookup(const K& key) {
-    auto cached_it = mru_cache_.Get(key);
-    if (cached_it != mru_cache_.end()) {
-      return cached_it->second.get();
-    }
-    return nullptr;
-  }
-
-  // Removes all entries from the cache.
-  void Clear() { mru_cache_.Clear(); }
-
-  // Returns maximum size of the cache.
-  int64_t MaxSize() const { return mru_cache_.max_size(); }
-
-  // Returns current size of the cache.
-  int64_t Size() const { return mru_cache_.size(); }
-
- private:
-  base::MRUCache<K, std::unique_ptr<V>> mru_cache_;
-
-  DISALLOW_COPY_AND_ASSIGN(QuicLRUCacheImpl);
-};
-
-}  // namespace quic
-
-#endif  // NET_THIRD_PARTY_QUIC_PLATFORM_IMPL_QUIC_LRU_CACHE_IMPL_H_
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 80e5bba3..873d875 100644
--- a/net/third_party/quic/test_tools/quic_test_client.cc
+++ b/net/third_party/quic/test_tools/quic_test_client.cc
@@ -167,7 +167,7 @@
     QuicSocketAddress server_address,
     const QuicServerId& server_id,
     const ParsedQuicVersionVector& supported_versions,
-    net::EpollServer* epoll_server)
+    QuicEpollServer* epoll_server)
     : MockableQuicClient(server_address,
                          server_id,
                          QuicConfig(),
@@ -179,7 +179,7 @@
     const QuicServerId& server_id,
     const QuicConfig& config,
     const ParsedQuicVersionVector& supported_versions,
-    net::EpollServer* epoll_server)
+    QuicEpollServer* epoll_server)
     : MockableQuicClient(server_address,
                          server_id,
                          config,
@@ -192,7 +192,7 @@
     const QuicServerId& server_id,
     const QuicConfig& config,
     const ParsedQuicVersionVector& supported_versions,
-    net::EpollServer* epoll_server,
+    QuicEpollServer* epoll_server,
     std::unique_ptr<ProofVerifier> proof_verifier)
     : QuicClient(
           server_address,
diff --git a/net/third_party/quic/test_tools/quic_test_client.h b/net/third_party/quic/test_tools/quic_test_client.h
index 01ec398..282d3a80 100644
--- a/net/third_party/quic/test_tools/quic_test_client.h
+++ b/net/third_party/quic/test_tools/quic_test_client.h
@@ -15,6 +15,7 @@
 #include "net/third_party/quic/core/quic_packet_creator.h"
 #include "net/third_party/quic/core/quic_packets.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/platform/api/quic_map_util.h"
 #include "net/third_party/quic/platform/api/quic_string_piece.h"
 #include "net/third_party/quic/tools/quic_client.h"
@@ -35,19 +36,19 @@
   MockableQuicClient(QuicSocketAddress server_address,
                      const QuicServerId& server_id,
                      const ParsedQuicVersionVector& supported_versions,
-                     net::EpollServer* epoll_server);
+                     QuicEpollServer* epoll_server);
 
   MockableQuicClient(QuicSocketAddress server_address,
                      const QuicServerId& server_id,
                      const QuicConfig& config,
                      const ParsedQuicVersionVector& supported_versions,
-                     net::EpollServer* epoll_server);
+                     QuicEpollServer* epoll_server);
 
   MockableQuicClient(QuicSocketAddress server_address,
                      const QuicServerId& server_id,
                      const QuicConfig& config,
                      const ParsedQuicVersionVector& supported_versions,
-                     net::EpollServer* epoll_server,
+                     QuicEpollServer* epoll_server,
                      std::unique_ptr<ProofVerifier> proof_verifier);
   MockableQuicClient(const MockableQuicClient&) = delete;
   MockableQuicClient& operator=(const MockableQuicClient&) = delete;
@@ -254,7 +255,7 @@
 
   void WaitForWriteToFlush();
 
-  net::EpollServer* epoll_server() { return &epoll_server_; }
+  QuicEpollServer* epoll_server() { return &epoll_server_; }
 
   size_t num_requests() const { return num_requests_; }
 
@@ -357,7 +358,7 @@
   // tracking its state.
   void SetLatestCreatedStream(QuicSpdyClientStream* stream);
 
-  net::EpollServer epoll_server_;
+  QuicEpollServer epoll_server_;
   std::unique_ptr<MockableQuicClient> client_;  // The actual client
   QuicSpdyClientStream* latest_created_stream_;
   std::map<QuicStreamId, QuicSpdyClientStream*> open_streams_;
diff --git a/net/third_party/quic/tools/quic_client.cc b/net/third_party/quic/tools/quic_client.cc
index 0b3366b..ab6b449 100644
--- a/net/third_party/quic/tools/quic_client.cc
+++ b/net/third_party/quic/tools/quic_client.cc
@@ -39,7 +39,7 @@
 QuicClient::QuicClient(QuicSocketAddress server_address,
                        const QuicServerId& server_id,
                        const ParsedQuicVersionVector& supported_versions,
-                       net::EpollServer* epoll_server,
+                       QuicEpollServer* epoll_server,
                        std::unique_ptr<ProofVerifier> proof_verifier)
     : QuicClient(
           server_address,
@@ -54,7 +54,7 @@
     QuicSocketAddress server_address,
     const QuicServerId& server_id,
     const ParsedQuicVersionVector& supported_versions,
-    net::EpollServer* epoll_server,
+    QuicEpollServer* epoll_server,
     std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
     std::unique_ptr<ProofVerifier> proof_verifier)
     : QuicClient(server_address,
@@ -70,7 +70,7 @@
     const QuicServerId& server_id,
     const ParsedQuicVersionVector& supported_versions,
     const QuicConfig& config,
-    net::EpollServer* epoll_server,
+    QuicEpollServer* epoll_server,
     std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
     std::unique_ptr<ProofVerifier> proof_verifier)
     : QuicSpdyClientBase(
diff --git a/net/third_party/quic/tools/quic_client.h b/net/third_party/quic/tools/quic_client.h
index 049aca2..15cc622 100644
--- a/net/third_party/quic/tools/quic_client.h
+++ b/net/third_party/quic/tools/quic_client.h
@@ -21,6 +21,7 @@
 #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"
@@ -40,20 +41,20 @@
   QuicClient(QuicSocketAddress server_address,
              const QuicServerId& server_id,
              const ParsedQuicVersionVector& supported_versions,
-             net::EpollServer* epoll_server,
+             QuicEpollServer* epoll_server,
              std::unique_ptr<ProofVerifier> proof_verifier);
   // This will take ownership of a passed in network primitive.
   QuicClient(QuicSocketAddress server_address,
              const QuicServerId& server_id,
              const ParsedQuicVersionVector& supported_versions,
-             net::EpollServer* epoll_server,
+             QuicEpollServer* epoll_server,
              std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
              std::unique_ptr<ProofVerifier> proof_verifier);
   QuicClient(QuicSocketAddress server_address,
              const QuicServerId& server_id,
              const ParsedQuicVersionVector& supported_versions,
              const QuicConfig& config,
-             net::EpollServer* epoll_server,
+             QuicEpollServer* epoll_server,
              std::unique_ptr<QuicClientEpollNetworkHelper> network_helper,
              std::unique_ptr<ProofVerifier> proof_verifier);
   QuicClient(const QuicClient&) = delete;
diff --git a/net/third_party/quic/tools/quic_client_bin.cc b/net/third_party/quic/tools/quic_client_bin.cc
index 2da7b9d..827885d 100644
--- a/net/third_party/quic/tools/quic_client_bin.cc
+++ b/net/third_party/quic/tools/quic_client_bin.cc
@@ -260,8 +260,8 @@
   VLOG(1) << "Resolved " << host << " to " << host_port << endl;
 
   // Build the client, and try to connect.
-  net::EpollServer epoll_server;
-  quic::QuicServerId server_id(url.host(), url.port(), false);
+  quic::QuicEpollServer epoll_server;
+  quic::QuicServerId server_id(url.host(), port, false);
   quic::ParsedQuicVersionVector versions = quic::CurrentSupportedVersions();
   if (FLAGS_quic_version != -1) {
     versions.clear();
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 dfdf2fc7..f05d3ca 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
@@ -39,7 +39,7 @@
 }  // namespace
 
 QuicClientEpollNetworkHelper::QuicClientEpollNetworkHelper(
-    net::EpollServer* epoll_server,
+    QuicEpollServer* epoll_server,
     QuicClientBase* client)
     : epoll_server_(epoll_server),
       packets_dropped_(0),
@@ -125,14 +125,14 @@
   epoll_server_->WaitForEventsAndExecuteCallbacks();
 }
 
-void QuicClientEpollNetworkHelper::OnRegistration(net::EpollServer* eps,
+void QuicClientEpollNetworkHelper::OnRegistration(QuicEpollServer* eps,
                                                   int fd,
                                                   int event_mask) {}
 void QuicClientEpollNetworkHelper::OnModification(int fd, int event_mask) {}
 void QuicClientEpollNetworkHelper::OnUnregistration(int fd, bool replaced) {}
-void QuicClientEpollNetworkHelper::OnShutdown(net::EpollServer* eps, int fd) {}
+void QuicClientEpollNetworkHelper::OnShutdown(QuicEpollServer* eps, int fd) {}
 
-void QuicClientEpollNetworkHelper::OnEvent(int fd, net::EpollEvent* event) {
+void QuicClientEpollNetworkHelper::OnEvent(int fd, QuicEpollEvent* event) {
   DCHECK_EQ(fd, GetLatestFD());
 
   if (event->in_events & EPOLLIN) {
diff --git a/net/third_party/quic/tools/quic_client_epoll_network_helper.h b/net/third_party/quic/tools/quic_client_epoll_network_helper.h
index 096ce12..b96f5b3 100644
--- a/net/third_party/quic/tools/quic_client_epoll_network_helper.h
+++ b/net/third_party/quic/tools/quic_client_epoll_network_helper.h
@@ -21,6 +21,7 @@
 #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_spdy_client_base.h"
 #include "net/tools/epoll_server/epoll_server.h"
@@ -34,12 +35,12 @@
 // An implementation of the QuicClientBase::NetworkHelper based off
 // the epoll server.
 class QuicClientEpollNetworkHelper : public QuicClientBase::NetworkHelper,
-                                     public net::EpollCallbackInterface,
+                                     public QuicEpollCallbackInterface,
                                      public ProcessPacketInterface {
  public:
   // Create a quic client, which will have events managed by an externally owned
-  // net::EpollServer.
-  QuicClientEpollNetworkHelper(net::EpollServer* epoll_server,
+  // EpollServer.
+  QuicClientEpollNetworkHelper(QuicEpollServer* epoll_server,
                                QuicClientBase* client);
   QuicClientEpollNetworkHelper(const QuicClientEpollNetworkHelper&) = delete;
   QuicClientEpollNetworkHelper& operator=(const QuicClientEpollNetworkHelper&) =
@@ -50,15 +51,15 @@
   // Return a name describing the class for use in debug/error reporting.
   QuicString Name() const override;
 
-  // From net::EpollCallbackInterface
-  void OnRegistration(net::EpollServer* eps, int fd, int event_mask) override;
+  // From EpollCallbackInterface
+  void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override;
   void OnModification(int fd, int event_mask) override;
-  void OnEvent(int fd, net::EpollEvent* event) override;
+  void OnEvent(int fd, QuicEpollEvent* event) override;
   // |fd_| can be unregistered without the client being disconnected. This
   // happens in b3m QuicProber where we unregister |fd_| to feed in events to
   // the client from the SelectServer.
   void OnUnregistration(int fd, bool replaced) override;
-  void OnShutdown(net::EpollServer* eps, int fd) override;
+  void OnShutdown(QuicEpollServer* eps, int fd) override;
 
   // From ProcessPacketInterface. This will be called for each received
   // packet.
@@ -77,7 +78,7 @@
 
   // Accessors provided for convenience, not part of any interface.
 
-  net::EpollServer* epoll_server() { return epoll_server_; }
+  QuicEpollServer* epoll_server() { return epoll_server_; }
 
   const QuicLinkedHashMap<int, QuicSocketAddress>& fd_address_map() const {
     return fd_address_map_;
@@ -112,7 +113,7 @@
   void CleanUpUDPSocketImpl(int fd);
 
   // Listens for events on the client socket.
-  net::EpollServer* epoll_server_;
+  QuicEpollServer* epoll_server_;
 
   // Map mapping created UDP sockets to their addresses. By using linked hash
   // map, the order of socket creation can be recorded.
diff --git a/net/third_party/quic/tools/quic_client_test.cc b/net/third_party/quic/tools/quic_client_test.cc
index 4c27c52..8717fd7f 100644
--- a/net/third_party/quic/tools/quic_client_test.cc
+++ b/net/third_party/quic/tools/quic_client_test.cc
@@ -49,8 +49,7 @@
 
 // Creates a new QuicClient and Initializes it. Caller is responsible for
 // deletion.
-QuicClient* CreateAndInitializeQuicClient(net::EpollServer* eps,
-                                          uint16_t port) {
+QuicClient* CreateAndInitializeQuicClient(QuicEpollServer* eps, uint16_t port) {
   QuicSocketAddress server_address(QuicSocketAddress(TestLoopback(), port));
   QuicServerId server_id("hostname", server_address.port(), false);
   ParsedQuicVersionVector versions = AllSupportedVersions();
@@ -71,8 +70,8 @@
   // around some memory corruption detector weirdness.
   crypto_test_utils::ProofVerifierForTesting().reset();
 
-  // Record initial number of FDs, after creation of net::EpollServer.
-  net::EpollServer eps;
+  // Record initial number of FDs, after creation of EpollServer.
+  QuicEpollServer eps;
   size_t number_of_open_fds = NumOpenSocketFDs();
 
   // Create a number of clients, initialize them, and verify this has resulted
@@ -95,7 +94,7 @@
   // around some memory corruption detector weirdness.
   crypto_test_utils::ProofVerifierForTesting().reset();
 
-  net::EpollServer eps;
+  QuicEpollServer eps;
   size_t number_of_open_fds = NumOpenSocketFDs();
 
   std::unique_ptr<QuicClient> client(
diff --git a/net/third_party/quic/tools/quic_server.cc b/net/third_party/quic/tools/quic_server.cc
index 60a4ba8..cb02b2d9 100644
--- a/net/third_party/quic/tools/quic_server.cc
+++ b/net/third_party/quic/tools/quic_server.cc
@@ -187,7 +187,7 @@
   fd_ = -1;
 }
 
-void QuicServer::OnEvent(int fd, net::EpollEvent* event) {
+void QuicServer::OnEvent(int fd, QuicEpollEvent* event) {
   DCHECK_EQ(fd, fd_);
   event->out_ready_mask = 0;
 
diff --git a/net/third_party/quic/tools/quic_server.h b/net/third_party/quic/tools/quic_server.h
index 9a76148..fb62348 100644
--- a/net/third_party/quic/tools/quic_server.h
+++ b/net/third_party/quic/tools/quic_server.h
@@ -20,6 +20,7 @@
 #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"
+#include "net/third_party/quic/platform/api/quic_epoll.h"
 #include "net/third_party/quic/platform/api/quic_socket_address.h"
 #include "net/third_party/quic/tools/quic_simple_server_backend.h"
 #include "net/tools/epoll_server/epoll_server.h"
@@ -33,7 +34,7 @@
 class QuicDispatcher;
 class QuicPacketReader;
 
-class QuicServer : public net::EpollCallbackInterface {
+class QuicServer : public QuicEpollCallbackInterface {
  public:
   QuicServer(std::unique_ptr<ProofSource> proof_source,
              QuicSimpleServerBackend* quic_simple_server_backend);
@@ -61,13 +62,13 @@
   // Server deletion is imminent.  Start cleaning up the epoll server.
   virtual void Shutdown();
 
-  // From net::EpollCallbackInterface
-  void OnRegistration(net::EpollServer* eps, int fd, int event_mask) override {}
+  // From EpollCallbackInterface
+  void OnRegistration(QuicEpollServer* eps, int fd, int event_mask) override {}
   void OnModification(int fd, int event_mask) override {}
-  void OnEvent(int fd, net::EpollEvent* event) override;
+  void OnEvent(int fd, QuicEpollEvent* event) override;
   void OnUnregistration(int fd, bool replaced) override {}
 
-  void OnShutdown(net::EpollServer* eps, int fd) override {}
+  void OnShutdown(QuicEpollServer* eps, int fd) override {}
 
   void SetChloMultiplier(size_t multiplier) {
     crypto_config_.set_chlo_multiplier(multiplier);
@@ -90,7 +91,7 @@
 
   const QuicConfig& config() const { return config_; }
   const QuicCryptoServerConfig& crypto_config() const { return crypto_config_; }
-  net::EpollServer* epoll_server() { return &epoll_server_; }
+  QuicEpollServer* epoll_server() { return &epoll_server_; }
 
   QuicDispatcher* dispatcher() { return dispatcher_.get(); }
 
@@ -111,7 +112,7 @@
   // Accepts data from the framer and demuxes clients to sessions.
   std::unique_ptr<QuicDispatcher> dispatcher_;
   // Frames incoming packets and hands them to the dispatcher.
-  net::EpollServer epoll_server_;
+  QuicEpollServer epoll_server_;
 
   // The port the server is listening on.
   int port_;
diff --git a/net/third_party/quic/tools/quic_server_test.cc b/net/third_party/quic/tools/quic_server_test.cc
index e342963..5d8908b 100644
--- a/net/third_party/quic/tools/quic_server_test.cc
+++ b/net/third_party/quic/tools/quic_server_test.cc
@@ -21,11 +21,11 @@
 #include "net/third_party/quic/tools/quic_memory_cache_backend.h"
 #include "net/third_party/quic/tools/quic_simple_crypto_server_stream_helper.h"
 
-using ::testing::_;
-
 namespace quic {
 namespace test {
 
+using ::testing::_;
+
 namespace {
 
 class MockQuicSimpleDispatcher : public QuicSimpleDispatcher {
@@ -179,7 +179,7 @@
   QuicConfig config_;
   QuicCryptoServerConfig crypto_config_;
   QuicVersionManager version_manager_;
-  net::EpollServer eps_;
+  QuicEpollServer eps_;
   QuicMemoryCacheBackend quic_simple_server_backend_;
   MockQuicDispatcher dispatcher_;
 };
diff --git a/net/tools/update_ios_bundle_data.py b/net/tools/update_ios_bundle_data.py
index e6b8ad57..144655c3 100755
--- a/net/tools/update_ios_bundle_data.py
+++ b/net/tools/update_ios_bundle_data.py
@@ -51,6 +51,7 @@
     "data/parse_certificate_unittest/*.pem",
     "data/parse_certificate_unittest/*.pk8",
     "data/test.html",
+    "data/trial_comparison_cert_verifier_unittest/**/*.pem",
     "data/url_request_unittest/*",
     "data/verify_certificate_chain_unittest/**/*.pem",
     "data/verify_certificate_chain_unittest/**/*.test",
diff --git a/net/websockets/websocket_basic_stream_adapters_test.cc b/net/websockets/websocket_basic_stream_adapters_test.cc
index 245cdaf7..111dee4f9 100644
--- a/net/websockets/websocket_basic_stream_adapters_test.cc
+++ b/net/websockets/websocket_basic_stream_adapters_test.cc
@@ -95,7 +95,7 @@
         TransportClientSocketPool::SocketParams::CreateFromSSLSocketParams(
             ssl_params_),
         MEDIUM, SocketTag(), ClientSocketPool::RespectLimits::ENABLED,
-        callback.callback(), socket_pool_manager_->GetSSLSocketPool(),
+        callback.callback(), socket_pool_manager_->GetTransportSocketPool(),
         net_log_);
     rv = callback.GetResult(rv);
     return rv == OK;
diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc
index 59a5390..352d366 100644
--- a/remoting/client/plugin/chromoting_instance.cc
+++ b/remoting/client/plugin/chromoting_instance.cc
@@ -228,17 +228,14 @@
   // Initialize TaskScheduler. TaskScheduler::StartWithDefaultParams() doesn't
   // work on NACL.
   base::TaskScheduler::Create("RemotingChromeApp");
-  constexpr int kBackgroundMaxThreads = 1;
-  constexpr int kBackgroundBlockingMaxThreads = 2;
-  constexpr int kForegroundMaxThreads = 1;
-  constexpr int kForegroundBlockingMaxThreads = 2;
+  // TODO(etiennep): Change this to 2 in future CL.
+  constexpr int kBackgroundMaxThreads = 3;
+  constexpr int kForegroundMaxThreads = 3;
   constexpr base::TimeDelta kSuggestedReclaimTime =
       base::TimeDelta::FromSeconds(30);
   base::TaskScheduler::GetInstance()->Start(
       {{kBackgroundMaxThreads, kSuggestedReclaimTime},
-       {kBackgroundBlockingMaxThreads, kSuggestedReclaimTime},
-       {kForegroundMaxThreads, kSuggestedReclaimTime},
-       {kForegroundBlockingMaxThreads, kSuggestedReclaimTime}});
+       {kForegroundMaxThreads, kSuggestedReclaimTime}});
 
   return true;
 }
diff --git a/services/identity/public/cpp/DEPS b/services/identity/public/cpp/DEPS
index 0656b1e..01ac2ce 100644
--- a/services/identity/public/cpp/DEPS
+++ b/services/identity/public/cpp/DEPS
@@ -3,7 +3,6 @@
   "+components/signin/core/browser/account_fetcher_service.h",
   "+components/signin/core/browser/account_info.h",
   "+components/signin/core/browser/fake_account_fetcher_service.h",
-  "+components/signin/core/browser/fake_gaia_cookie_manager_service.h",
   "+components/signin/core/browser/gaia_cookie_manager_service.h",
   "+components/signin/core/browser/list_accounts_test_utils.h",
   "+components/signin/core/browser/account_consistency_method.h",
diff --git a/services/identity/public/cpp/identity_test_environment.h b/services/identity/public/cpp/identity_test_environment.h
index 0a798cfe9..9df6f55 100644
--- a/services/identity/public/cpp/identity_test_environment.h
+++ b/services/identity/public/cpp/identity_test_environment.h
@@ -9,7 +9,6 @@
 #include "components/signin/core/browser/account_consistency_method.h"
 #include "components/signin/core/browser/account_tracker_service.h"
 #include "components/signin/core/browser/fake_account_fetcher_service.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h"
 #include "components/signin/core/browser/fake_signin_manager.h"
 #include "services/identity/public/cpp/identity_manager.h"
diff --git a/services/identity/public/cpp/identity_test_utils.cc b/services/identity/public/cpp/identity_test_utils.cc
index 3a63914a..959709b 100644
--- a/services/identity/public/cpp/identity_test_utils.cc
+++ b/services/identity/public/cpp/identity_test_utils.cc
@@ -10,7 +10,7 @@
 #include "base/run_loop.h"
 #include "base/strings/string_split.h"
 #include "components/signin/core/browser/account_tracker_service.h"
-#include "components/signin/core/browser/fake_gaia_cookie_manager_service.h"
+#include "components/signin/core/browser/list_accounts_test_utils.h"
 #include "components/signin/core/browser/profile_oauth2_token_service.h"
 #include "google_apis/gaia/gaia_auth_util.h"
 #include "services/identity/public/cpp/identity_manager.h"
@@ -333,30 +333,6 @@
   run_loop.Run();
 }
 
-void SetCookieAccounts(FakeGaiaCookieManagerService* cookie_manager,
-                       IdentityManager* identity_manager,
-                       const std::vector<CookieParams>& cookie_accounts) {
-  // Convert |cookie_accounts| to the format FakeGaiaCookieManagerService wants.
-  std::vector<signin::CookieParams> gaia_cookie_accounts;
-  for (const CookieParams& params : cookie_accounts) {
-    gaia_cookie_accounts.push_back({params.email, params.gaia_id,
-                                    /*valid=*/true, /*signed_out=*/false,
-                                    /*verified=*/true});
-  }
-
-  base::RunLoop run_loop;
-  OneShotIdentityManagerObserver cookie_observer(
-      identity_manager, run_loop.QuitClosure(),
-      IdentityManagerEvent::ACCOUNTS_IN_COOKIE_UPDATED);
-
-  cookie_manager->SetListAccountsResponseWithParams(gaia_cookie_accounts);
-
-  cookie_manager->set_list_accounts_stale_for_testing(true);
-  cookie_manager->ListAccounts(nullptr, nullptr);
-
-  run_loop.Run();
-}
-
 void UpdateAccountInfoForAccount(IdentityManager* identity_manager,
                                  AccountInfo account_info) {
   // Make sure the account being updated is a known account.
diff --git a/services/identity/public/cpp/identity_test_utils.h b/services/identity/public/cpp/identity_test_utils.h
index e238ff6..a85962d9 100644
--- a/services/identity/public/cpp/identity_test_utils.h
+++ b/services/identity/public/cpp/identity_test_utils.h
@@ -14,7 +14,6 @@
 class TestURLLoaderFactory;
 }
 
-class FakeGaiaCookieManagerService;
 class GoogleServiceAuthError;
 
 // Test-related utilities that don't fit in either IdentityTestEnvironment or
@@ -130,13 +129,6 @@
                        network::TestURLLoaderFactory* test_url_loader_factory,
                        const std::vector<CookieParams>& cookie_accounts);
 
-// Same as above, but takes a FakeGaiaCookieManagerService.
-// TODO(https://crbug.com/1379770): Delete this overload once FakeGCMS has been
-// eliminated.
-void SetCookieAccounts(FakeGaiaCookieManagerService* cookie_manager,
-                       IdentityManager* identity_manager,
-                       const std::vector<CookieParams>& cookie_accounts);
-
 // Updates the info for |account_info.account_id|, which must be a known
 // account.
 void UpdateAccountInfoForAccount(IdentityManager* identity_manager,
diff --git a/services/media_session/media_controller_unittest.cc b/services/media_session/media_controller_unittest.cc
index c0090fe..3736546 100644
--- a/services/media_session/media_controller_unittest.cc
+++ b/services/media_session/media_controller_unittest.cc
@@ -568,8 +568,7 @@
   {
     test::TestMediaControllerObserver observer(controller());
     media_session.SimulateMetadataChanged(test_metadata);
-    observer.WaitForNonEmptyMetadata();
-    EXPECT_EQ(metadata, observer.session_metadata());
+    observer.WaitForExpectedMetadata(metadata);
   }
 }
 
@@ -614,8 +613,7 @@
 
   {
     test::TestMediaControllerObserver observer(controller());
-    observer.WaitForNonEmptyMetadata();
-    EXPECT_EQ(metadata, observer.session_metadata());
+    observer.WaitForExpectedMetadata(metadata);
   }
 }
 
@@ -744,8 +742,7 @@
 
   {
     test::TestMediaControllerObserver observer(controller());
-    observer.WaitForActions();
-    EXPECT_TRUE(observer.actions().empty());
+    observer.WaitForEmptyActions();
   }
 }
 
@@ -763,15 +760,14 @@
 
   {
     test::TestMediaControllerObserver observer(controller());
-    observer.WaitForActions();
 
-    EXPECT_EQ(1u, observer.actions().size());
-    EXPECT_EQ(mojom::MediaSessionAction::kPlay, observer.actions()[0]);
+    std::set<mojom::MediaSessionAction> expected_actions;
+    expected_actions.insert(mojom::MediaSessionAction::kPlay);
+    observer.WaitForExpectedActions(expected_actions);
   }
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
-TEST_F(MediaControllerTest, DISABLED_ActiveController_Actions_Observer_Empty) {
+TEST_F(MediaControllerTest, ActiveController_Actions_Observer_Empty) {
   test::MockMediaSession media_session;
   media_session.EnableAction(mojom::MediaSessionAction::kPlay);
   media_session.SetIsControllable(true);
@@ -785,15 +781,11 @@
   {
     test::TestMediaControllerObserver observer(controller());
     media_session.DisableAction(mojom::MediaSessionAction::kPlay);
-    observer.WaitForActions();
-
-    EXPECT_TRUE(observer.actions().empty());
+    observer.WaitForEmptyActions();
   }
 }
 
-// TODO(https://crbug.com/925868): Fix and re-enable this.
-TEST_F(MediaControllerTest,
-       DISABLED_ActiveController_Actions_Observer_WithInfo) {
+TEST_F(MediaControllerTest, ActiveController_Actions_Observer_WithInfo) {
   test::MockMediaSession media_session;
   media_session.SetIsControllable(true);
 
@@ -806,10 +798,10 @@
   {
     test::TestMediaControllerObserver observer(controller());
     media_session.EnableAction(mojom::MediaSessionAction::kPlay);
-    observer.WaitForActions();
 
-    EXPECT_EQ(1u, observer.actions().size());
-    EXPECT_EQ(mojom::MediaSessionAction::kPlay, observer.actions()[0]);
+    std::set<mojom::MediaSessionAction> expected_actions;
+    expected_actions.insert(mojom::MediaSessionAction::kPlay);
+    observer.WaitForExpectedActions(expected_actions);
   }
 }
 
@@ -828,8 +820,7 @@
 
   {
     test::TestMediaControllerObserver observer(controller());
-    observer.WaitForActions();
-    EXPECT_TRUE(observer.actions().empty());
+    observer.WaitForEmptyActions();
   }
 }
 
@@ -851,8 +842,7 @@
     // controller is no longer bound to a media session.
     observer.WaitForEmptyInfo();
     observer.WaitForEmptyMetadata();
-    observer.WaitForActions();
-    EXPECT_TRUE(observer.actions().empty());
+    observer.WaitForEmptyActions();
   }
 }
 
@@ -875,8 +865,7 @@
     // controller is no longer bound to a media session.
     observer.WaitForEmptyInfo();
     observer.WaitForEmptyMetadata();
-    observer.WaitForActions();
-    EXPECT_TRUE(observer.actions().empty());
+    observer.WaitForEmptyActions();
   }
 }
 
diff --git a/services/media_session/public/cpp/features.cc b/services/media_session/public/cpp/features.cc
index de8d339..7fa1bc2 100644
--- a/services/media_session/public/cpp/features.cc
+++ b/services/media_session/public/cpp/features.cc
@@ -11,10 +11,10 @@
 
 // Enables the Media Session service including audio focus tracking. This allows
 // clients to consume the Media Session Mojo APIs but should not have any
-// changes to behavior. It is enabled by default on Chrome OS.
+// changes to behavior. It is enabled by default on all platforms except Android.
 const base::Feature kMediaSessionService {
   "MediaSessionService",
-#if defined(OS_CHROMEOS)
+#if !defined(OS_ANDROID)
       base::FEATURE_ENABLED_BY_DEFAULT
 #else
       base::FEATURE_DISABLED_BY_DEFAULT
diff --git a/services/media_session/public/cpp/test/mock_media_session.cc b/services/media_session/public/cpp/test/mock_media_session.cc
index b3ccd79..2e19ae44 100644
--- a/services/media_session/public/cpp/test/mock_media_session.cc
+++ b/services/media_session/public/cpp/test/mock_media_session.cc
@@ -26,8 +26,12 @@
     mojom::MediaSessionInfoPtr session) {
   session_info_ = std::move(session);
 
-  if (wanted_state_ == session_info_->state ||
-      session_info_->playback_state == wanted_playback_state_) {
+  if (expected_controllable_.has_value() &&
+      expected_controllable_ == session_info_->is_controllable) {
+    run_loop_->Quit();
+    expected_controllable_.reset();
+  } else if (wanted_state_ == session_info_->state ||
+             session_info_->playback_state == wanted_playback_state_) {
     run_loop_->Quit();
   }
 }
@@ -36,25 +40,24 @@
     const base::Optional<MediaMetadata>& metadata) {
   session_metadata_ = metadata;
 
-  if (waiting_for_metadata_) {
+  if (expected_metadata_.has_value() && expected_metadata_ == metadata) {
     run_loop_->Quit();
-    waiting_for_metadata_ = false;
-  } else if (waiting_for_non_empty_metadata_ && metadata.has_value() &&
-             !metadata->IsEmpty()) {
+    expected_metadata_.reset();
+  } else if (waiting_for_empty_metadata_ &&
+             (!metadata.has_value() || metadata->IsEmpty())) {
     run_loop_->Quit();
-    waiting_for_non_empty_metadata_ = false;
+    waiting_for_empty_metadata_ = false;
   }
 }
 
 void MockMediaSessionMojoObserver::MediaSessionActionsChanged(
     const std::vector<mojom::MediaSessionAction>& actions) {
-  session_actions_ = actions;
-  session_actions_set_ =
+  session_actions_ =
       std::set<mojom::MediaSessionAction>(actions.begin(), actions.end());
 
-  if (waiting_for_actions_) {
+  if (expected_actions_.has_value() && expected_actions_ == session_actions_) {
     run_loop_->Quit();
-    waiting_for_actions_ = false;
+    expected_actions_.reset();
   }
 }
 
@@ -76,27 +79,41 @@
   StartWaiting();
 }
 
-const base::Optional<MediaMetadata>&
-MockMediaSessionMojoObserver::WaitForMetadata() {
-  if (!session_metadata_.has_value()) {
-    waiting_for_metadata_ = true;
-    StartWaiting();
-  }
+void MockMediaSessionMojoObserver::WaitForControllable(bool is_controllable) {
+  if (session_info_ && session_info_->is_controllable == is_controllable)
+    return;
 
-  return session_metadata_.value();
+  expected_controllable_ = is_controllable;
+  StartWaiting();
 }
 
-const MediaMetadata& MockMediaSessionMojoObserver::WaitForNonEmptyMetadata() {
-  if (!session_metadata_.has_value() || !session_metadata_->has_value()) {
-    waiting_for_non_empty_metadata_ = true;
-    StartWaiting();
-  }
+void MockMediaSessionMojoObserver::WaitForEmptyMetadata() {
+  if (session_metadata_.has_value() || !session_metadata_->has_value())
+    return;
 
-  return session_metadata_->value();
+  waiting_for_empty_metadata_ = true;
+  StartWaiting();
 }
 
-void MockMediaSessionMojoObserver::WaitForActions() {
-  waiting_for_actions_ = true;
+void MockMediaSessionMojoObserver::WaitForExpectedMetadata(
+    const MediaMetadata& metadata) {
+  if (session_metadata_.has_value() && session_metadata_ == metadata)
+    return;
+
+  expected_metadata_ = metadata;
+  StartWaiting();
+}
+
+void MockMediaSessionMojoObserver::WaitForEmptyActions() {
+  WaitForExpectedActions(std::set<mojom::MediaSessionAction>());
+}
+
+void MockMediaSessionMojoObserver::WaitForExpectedActions(
+    const std::set<mojom::MediaSessionAction>& actions) {
+  if (session_actions_.has_value() && session_actions_ == actions)
+    return;
+
+  expected_actions_ = actions;
   StartWaiting();
 }
 
diff --git a/services/media_session/public/cpp/test/mock_media_session.h b/services/media_session/public/cpp/test/mock_media_session.h
index a226ffa..58931ad 100644
--- a/services/media_session/public/cpp/test/mock_media_session.h
+++ b/services/media_session/public/cpp/test/mock_media_session.h
@@ -40,9 +40,14 @@
 
   void WaitForState(mojom::MediaSessionInfo::SessionState wanted_state);
   void WaitForPlaybackState(mojom::MediaPlaybackState wanted_state);
-  const base::Optional<MediaMetadata>& WaitForMetadata();
-  const MediaMetadata& WaitForNonEmptyMetadata();
-  void WaitForActions();
+  void WaitForControllable(bool is_controllable);
+
+  void WaitForEmptyMetadata();
+  void WaitForExpectedMetadata(const MediaMetadata& metadata);
+
+  void WaitForEmptyActions();
+  void WaitForExpectedActions(
+      const std::set<mojom::MediaSessionAction>& actions);
 
   const mojom::MediaSessionInfoPtr& session_info() const {
     return session_info_;
@@ -53,12 +58,8 @@
     return session_metadata_;
   }
 
-  const std::vector<mojom::MediaSessionAction>& actions() const {
-    return session_actions_;
-  }
-
-  const std::set<mojom::MediaSessionAction>& actions_set() const {
-    return session_actions_set_;
+  const std::set<mojom::MediaSessionAction>& actions() const {
+    return *session_actions_;
   }
 
  private:
@@ -66,12 +67,12 @@
 
   mojom::MediaSessionInfoPtr session_info_;
   base::Optional<base::Optional<MediaMetadata>> session_metadata_;
-  std::vector<mojom::MediaSessionAction> session_actions_;
-  std::set<mojom::MediaSessionAction> session_actions_set_;
+  base::Optional<std::set<mojom::MediaSessionAction>> session_actions_;
 
-  bool waiting_for_metadata_ = false;
-  bool waiting_for_non_empty_metadata_ = false;
-  bool waiting_for_actions_ = false;
+  base::Optional<MediaMetadata> expected_metadata_;
+  base::Optional<std::set<mojom::MediaSessionAction>> expected_actions_;
+  base::Optional<bool> expected_controllable_;
+  bool waiting_for_empty_metadata_ = false;
 
   base::Optional<mojom::MediaSessionInfo::SessionState> wanted_state_;
   base::Optional<mojom::MediaPlaybackState> wanted_playback_state_;
diff --git a/services/media_session/public/cpp/test/test_media_controller.cc b/services/media_session/public/cpp/test/test_media_controller.cc
index 905c153..4925919f 100644
--- a/services/media_session/public/cpp/test/test_media_controller.cc
+++ b/services/media_session/public/cpp/test/test_media_controller.cc
@@ -36,26 +36,24 @@
     const base::Optional<MediaMetadata>& metadata) {
   session_metadata_ = metadata;
 
-  if (waiting_for_empty_metadata_ &&
-      (!metadata.has_value() || metadata->IsEmpty())) {
+  if (expected_metadata_.has_value() && expected_metadata_ == metadata) {
+    run_loop_->Quit();
+    expected_metadata_.reset();
+  } else if (waiting_for_empty_metadata_ &&
+             (!metadata.has_value() || metadata->IsEmpty())) {
     run_loop_->Quit();
     waiting_for_empty_metadata_ = false;
-  } else if (waiting_for_non_empty_metadata_ && metadata.has_value() &&
-             !metadata->IsEmpty()) {
-    run_loop_->Quit();
-    waiting_for_non_empty_metadata_ = false;
   }
 }
 
 void TestMediaControllerObserver::MediaSessionActionsChanged(
     const std::vector<mojom::MediaSessionAction>& actions) {
-  session_actions_ = actions;
-  session_actions_set_ =
+  session_actions_ =
       std::set<mojom::MediaSessionAction>(actions.begin(), actions.end());
 
-  if (waiting_for_actions_) {
+  if (expected_actions_.has_value() && expected_actions_ == session_actions_) {
     run_loop_->Quit();
-    waiting_for_actions_ = false;
+    expected_actions_.reset();
   }
 }
 
@@ -93,19 +91,25 @@
   StartWaiting();
 }
 
-void TestMediaControllerObserver::WaitForNonEmptyMetadata() {
-  if (session_metadata_.has_value() && !session_metadata_.value()->IsEmpty())
+void TestMediaControllerObserver::WaitForExpectedMetadata(
+    const MediaMetadata& metadata) {
+  if (session_metadata_.has_value() && session_metadata_ == metadata)
     return;
 
-  waiting_for_non_empty_metadata_ = true;
+  expected_metadata_ = metadata;
   StartWaiting();
 }
 
-void TestMediaControllerObserver::WaitForActions() {
-  if (session_actions_.has_value())
+void TestMediaControllerObserver::WaitForEmptyActions() {
+  WaitForExpectedActions(std::set<mojom::MediaSessionAction>());
+}
+
+void TestMediaControllerObserver::WaitForExpectedActions(
+    const std::set<mojom::MediaSessionAction>& actions) {
+  if (session_actions_.has_value() && session_actions_ == actions)
     return;
 
-  waiting_for_actions_ = true;
+  expected_actions_ = actions;
   StartWaiting();
 }
 
diff --git a/services/media_session/public/cpp/test/test_media_controller.h b/services/media_session/public/cpp/test/test_media_controller.h
index af8de837..ccd11af3 100644
--- a/services/media_session/public/cpp/test/test_media_controller.h
+++ b/services/media_session/public/cpp/test/test_media_controller.h
@@ -36,9 +36,11 @@
   void WaitForEmptyInfo();
 
   void WaitForEmptyMetadata();
-  void WaitForNonEmptyMetadata();
+  void WaitForExpectedMetadata(const MediaMetadata& metadata);
 
-  void WaitForActions();
+  void WaitForEmptyActions();
+  void WaitForExpectedActions(
+      const std::set<mojom::MediaSessionAction>& actions);
 
   const mojom::MediaSessionInfoPtr& session_info() const {
     return *session_info_;
@@ -49,26 +51,20 @@
     return session_metadata_;
   }
 
-  const std::vector<mojom::MediaSessionAction>& actions() const {
+  const std::set<mojom::MediaSessionAction>& actions() const {
     return *session_actions_;
   }
 
-  const std::set<mojom::MediaSessionAction>& actions_set() const {
-    return session_actions_set_;
-  }
-
  private:
   void StartWaiting();
 
   base::Optional<mojom::MediaSessionInfoPtr> session_info_;
   base::Optional<base::Optional<MediaMetadata>> session_metadata_;
-  base::Optional<std::vector<mojom::MediaSessionAction>> session_actions_;
-  std::set<mojom::MediaSessionAction> session_actions_set_;
+  base::Optional<std::set<mojom::MediaSessionAction>> session_actions_;
 
+  base::Optional<MediaMetadata> expected_metadata_;
+  base::Optional<std::set<mojom::MediaSessionAction>> expected_actions_;
   bool waiting_for_empty_metadata_ = false;
-  bool waiting_for_non_empty_metadata_ = false;
-
-  bool waiting_for_actions_ = false;
 
   bool waiting_for_empty_info_ = false;
   base::Optional<mojom::MediaSessionInfo::SessionState> wanted_state_;
diff --git a/services/network/BUILD.gn b/services/network/BUILD.gn
index 3d9c394..a561e2c 100644
--- a/services/network/BUILD.gn
+++ b/services/network/BUILD.gn
@@ -269,6 +269,13 @@
   if (is_chromecast) {
     defines += [ "IS_CHROMECAST" ]
   }
+
+  if (trial_comparison_cert_verifier_supported) {
+    sources += [
+      "trial_comparison_cert_verifier_mojo.cc",
+      "trial_comparison_cert_verifier_mojo.h",
+    ]
+  }
 }
 
 source_set("tests") {
diff --git a/services/network/network_context.cc b/services/network/network_context.cc
index 08e0bfed..afe74a3 100644
--- a/services/network/network_context.cc
+++ b/services/network/network_context.cc
@@ -142,6 +142,13 @@
 #include "base/android/application_status_listener.h"
 #endif
 
+#if BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
+#include "net/cert/caching_cert_verifier.h"
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/cert_verify_proc_builtin.h"
+#include "services/network/trial_comparison_cert_verifier_mojo.h"
+#endif
+
 namespace network {
 
 namespace {
@@ -2100,9 +2107,21 @@
       cert_verifier_with_trust_anchors_->InitializeOnIOThread(verify_proc);
       cert_verifier = base::WrapUnique(cert_verifier_with_trust_anchors_);
     }
-#else
-    cert_verifier = net::CertVerifier::CreateDefault();
+#elif BUILDFLAG(TRIAL_COMPARISON_CERT_VERIFIER_SUPPORTED)
+    if (params_->trial_comparison_cert_verifier_params) {
+      cert_verifier = std::make_unique<net::CachingCertVerifier>(
+          std::make_unique<TrialComparisonCertVerifierMojo>(
+              params_->trial_comparison_cert_verifier_params->initial_allowed,
+              std::move(params_->trial_comparison_cert_verifier_params
+                            ->config_client_request),
+              std::move(params_->trial_comparison_cert_verifier_params
+                            ->report_client),
+              net::CertVerifyProc::CreateDefault(),
+              net::CreateCertVerifyProcBuiltin()));
+    }
 #endif
+    if (!cert_verifier)
+      cert_verifier = net::CertVerifier::CreateDefault();
   }
 
   builder.SetCertVerifier(IgnoreErrorsCertVerifier::MaybeWrapCertVerifier(
diff --git a/services/network/network_context_unittest.cc b/services/network/network_context_unittest.cc
index 70f7b95..9c7114f 100644
--- a/services/network/network_context_unittest.cc
+++ b/services/network/network_context_unittest.cc
@@ -11,6 +11,7 @@
 #include "base/barrier_closure.h"
 #include "base/bind.h"
 #include "base/command_line.h"
+#include "base/containers/span.h"
 #include "base/files/file.h"
 #include "base/files/file_path.h"
 #include "base/files/file_util.h"
@@ -73,7 +74,6 @@
 #include "net/proxy_resolution/proxy_config.h"
 #include "net/proxy_resolution/proxy_info.h"
 #include "net/proxy_resolution/proxy_resolution_service.h"
-#include "net/socket/ssl_client_socket_pool.h"
 #include "net/socket/transport_client_socket_pool.h"
 #include "net/ssl/channel_id_service.h"
 #include "net/ssl/channel_id_store.h"
@@ -301,30 +301,36 @@
     return value;
   }
 
-  // Looks up a value with the given name from the NetworkContext's
-  // SSLSocketPool info dictionary.
-  int GetSSLSocketPoolInfo(NetworkContext* context, base::StringPiece name) {
-    int value;
-    context->url_request_context()
-        ->http_transaction_factory()
-        ->GetSession()
-        ->GetSSLSocketPool(
-            net::HttpNetworkSession::SocketPoolType::NORMAL_SOCKET_POOL)
-        ->GetInfoAsValue("", "", false)
-        ->GetInteger(name, &value);
-    return value;
-  }
+  int GetSocketCountForGroup(NetworkContext* context,
+                             const std::string& group_name) {
+    std::unique_ptr<base::Value> pool_info =
+        context->url_request_context()
+            ->http_transaction_factory()
+            ->GetSession()
+            ->GetTransportSocketPool(
+                net::HttpNetworkSession::SocketPoolType::NORMAL_SOCKET_POOL)
+            ->GetInfoAsValue("", "", false);
 
-  int GetSocketCount(NetworkContext* network_context) {
-    return GetSocketPoolInfo(network_context, "idle_socket_count") +
-           GetSocketPoolInfo(network_context, "connecting_socket_count") +
-           GetSocketPoolInfo(network_context, "handed_out_socket_count");
-  }
-
-  int GetSSLSocketCount(NetworkContext* network_context) {
-    return GetSSLSocketPoolInfo(network_context, "idle_socket_count") +
-           GetSSLSocketPoolInfo(network_context, "connecting_socket_count") +
-           GetSSLSocketPoolInfo(network_context, "handed_out_socket_count");
+    int count = 0;
+    base::Value* active_socket_count = pool_info->FindPathOfType(
+        base::span<const base::StringPiece>{
+            {"groups", group_name, "active_socket_count"}},
+        base::Value::Type::INTEGER);
+    if (active_socket_count)
+      count += active_socket_count->GetInt();
+    base::Value* idle_sockets = pool_info->FindPathOfType(
+        base::span<const base::StringPiece>{
+            {"groups", group_name, "idle_sockets"}},
+        base::Value::Type::LIST);
+    if (idle_sockets)
+      count += idle_sockets->GetList().size();
+    base::Value* connect_jobs = pool_info->FindPathOfType(
+        base::span<const base::StringPiece>{
+            {"groups", group_name, "connect_jobs"}},
+        base::Value::Type::LIST);
+    if (connect_jobs)
+      count += connect_jobs->GetList().size();
+    return count;
   }
 
   GURL GetHttpUrlFromHttps(const GURL& https_url) {
@@ -3406,10 +3412,10 @@
                                      true);
   connection_listener.WaitForAcceptedConnections(1u);
 
-  int num_sockets = GetSocketCount(network_context.get());
+  int num_sockets = GetSocketCountForGroup(
+      network_context.get(),
+      "pm/" + net::HostPortPair::FromURL(server_http_url).ToString());
   EXPECT_EQ(num_sockets, 1);
-  int num_ssl_sockets = GetSSLSocketCount(network_context.get());
-  EXPECT_EQ(num_ssl_sockets, 0);
 
   const base::Time expiry =
       base::Time::Now() + base::TimeDelta::FromSeconds(1000);
@@ -3419,10 +3425,11 @@
                                      true);
   connection_listener.WaitForAcceptedConnections(1u);
 
-  num_sockets = GetSocketCount(network_context.get());
-  EXPECT_EQ(num_sockets, 2);
-  num_ssl_sockets = GetSSLSocketCount(network_context.get());
-  EXPECT_EQ(num_ssl_sockets, 1);
+  // If HSTS weren't respected, the initial connection would have been reused.
+  num_sockets = GetSocketCountForGroup(
+      network_context.get(),
+      "pm/ssl/" + net::HostPortPair::FromURL(server_http_url).ToString());
+  EXPECT_EQ(num_sockets, 1);
 }
 
 TEST_F(NetworkContextTest, PreconnectZero) {
diff --git a/services/network/public/cpp/cors/cors.cc b/services/network/public/cpp/cors/cors.cc
index 88ebef77..29d5ec5 100644
--- a/services/network/public/cpp/cors/cors.cc
+++ b/services/network/public/cpp/cors/cors.cc
@@ -367,7 +367,13 @@
       // See
       // https://w3c.github.io/device-memory/#sec-device-memory-client-hint-header
       // for more details.
-      "device-memory", "dpr", "width", "viewport-width"};
+      "device-memory", "dpr", "width", "viewport-width",
+
+      // The `Sec-CH-Lang` header field is a proposed replacement for
+      // `Accept-Language`, using the Client Hints infrastructure.
+      //
+      // https://tools.ietf.org/html/draft-west-lang-client-hint
+      "sec-ch-lang"};
   const std::string lower_name = base::ToLowerASCII(name);
   if (std::find(std::begin(safe_names), std::end(safe_names), lower_name) ==
       std::end(safe_names))
diff --git a/services/network/public/cpp/cors/cors_unittest.cc b/services/network/public/cpp/cors/cors_unittest.cc
index f3c501f..2a24891 100644
--- a/services/network/public/cpp/cors/cors_unittest.cc
+++ b/services/network/public/cpp/cors/cors_unittest.cc
@@ -379,6 +379,13 @@
       IsCorsSafelistedHeader("aCcEPT-lAngUAge", std::string(129, 'a')));
 }
 
+TEST_F(CorsTest, SafelistedSecCHLang) {
+  EXPECT_TRUE(IsCorsSafelistedHeader("Sec-CH-Lang", "\"en\", \"de\""));
+
+  // TODO(mkwst): Validate that `Sec-CH-Lang` is a structured header.
+  // https://crbug.com/924969
+}
+
 TEST_F(CorsTest, SafelistedContentLanguage) {
   EXPECT_TRUE(IsCorsSafelistedHeader("content-language", "en,ja"));
   EXPECT_TRUE(IsCorsSafelistedHeader("cONTent-LANguaGe", "en,ja"));
diff --git a/services/network/public/mojom/BUILD.gn b/services/network/public/mojom/BUILD.gn
index 9f8792d..6e3a61f 100644
--- a/services/network/public/mojom/BUILD.gn
+++ b/services/network/public/mojom/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//mojo/public/tools/bindings/mojom.gni")
+import("//net/features.gni")
 import("//services/network/public/cpp/features.gni")
 
 # These interfaces are put in their own target to avoid a circular dependency,
@@ -141,4 +142,9 @@
   if (is_linux && !is_chromeos) {
     enabled_features += [ "needs_crypt_config" ]
   }
+
+  if (trial_comparison_cert_verifier_supported) {
+    enabled_features += [ "is_trial_comparison_cert_verifier_supported" ]
+    sources += [ "trial_comparison_cert_verifier.mojom" ]
+  }
 }
diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom
index 1401e2c6..4a7ed8ae 100644
--- a/services/network/public/mojom/network_context.mojom
+++ b/services/network/public/mojom/network_context.mojom
@@ -38,6 +38,9 @@
 [EnableIf=is_ct_supported]
 import "services/network/public/mojom/ct_log_info.mojom";
 
+[EnableIf=is_trial_comparison_cert_verifier_supported]
+import "services/network/public/mojom/trial_comparison_cert_verifier.mojom";
+
 // Config for setting a custom proxy config that will be used if a request
 // matches the proxy rules and would otherwise be direct. This config allows
 // headers to be set on requests to the proxies from the config before and/or
@@ -160,10 +163,6 @@
   // cookies. Otherwise it should be false.
   bool persist_session_cookies = false;
 
-  // Points to the channel ID file. If a cookie file is specified, this must be
-  // specified as well. Otherwise an in-memory store is used.
-  mojo_base.mojom.FilePath? channel_id_path;
-
   // True if an HTTP cache should be used.
   bool http_cache_enabled = true;
   // Maximum size of the HTTP cache. 0 means to use the default size.
@@ -286,6 +285,12 @@
   [EnableIf=is_chromeos]
   AdditionalCertificates? initial_additional_certificates;
 
+  // Parameters for the cert verifier comparison trial. This is a temporary
+  // interface and embedders should not use it.
+  // See https://crbug.com/649026
+  [EnableIf=is_trial_comparison_cert_verifier_supported]
+  TrialComparisonCertVerifierParams? trial_comparison_cert_verifier_params;
+
   // Parameters for constructing the cookie manager.
   CookieManagerParams? cookie_manager_params;
 
diff --git a/services/network/public/mojom/trial_comparison_cert_verifier.mojom b/services/network/public/mojom/trial_comparison_cert_verifier.mojom
new file mode 100644
index 0000000..ac5fdec
--- /dev/null
+++ b/services/network/public/mojom/trial_comparison_cert_verifier.mojom
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// These interfaces support a trial comparing two different implementations of
+// certificate verification, where the certificate verification may occur in
+// one process and the configuration and reporting in another. They will be
+// removed when the trial is completed.
+// See https://crbug.com/649026
+
+module network.mojom;
+
+import "services/network/public/mojom/network_param.mojom";
+
+// Receives cert verifier trial configuration updates.
+interface TrialComparisonCertVerifierConfigClient {
+  OnTrialConfigUpdated(bool allowed);
+};
+
+// Sends reports of differences found in the cert verifier trial.
+interface TrialComparisonCertVerifierReportClient {
+  SendTrialReport(
+      string hostname, X509Certificate cert, bool enable_rev_checking,
+      bool require_rev_checking_local_anchors, bool enable_sha1_local_anchors,
+      bool disable_symantec_enforcement, CertVerifyResult primary_result,
+      CertVerifyResult trial_result);
+};
+
+// Parameters for initializing the cert verification trial.
+// |initial_allowed| is the initial setting for whether the trial is allowed.
+// |config_client_request| is the Mojo pipe over which trial configuration
+// updates are received.
+// |report_client| is the Mojo pipe used to send trial reports.
+struct TrialComparisonCertVerifierParams {
+  bool initial_allowed = false;
+  TrialComparisonCertVerifierConfigClient&? config_client_request;
+  TrialComparisonCertVerifierReportClient? report_client;
+};
diff --git a/services/network/trial_comparison_cert_verifier_mojo.cc b/services/network/trial_comparison_cert_verifier_mojo.cc
new file mode 100644
index 0000000..804a2375
--- /dev/null
+++ b/services/network/trial_comparison_cert_verifier_mojo.cc
@@ -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.
+
+#include "services/network/trial_comparison_cert_verifier_mojo.h"
+
+#include <utility>
+
+#include "net/cert/cert_verify_proc.h"
+#include "net/cert/trial_comparison_cert_verifier.h"
+
+namespace network {
+
+TrialComparisonCertVerifierMojo::TrialComparisonCertVerifierMojo(
+    bool initial_allowed,
+    mojom::TrialComparisonCertVerifierConfigClientRequest config_client_request,
+    mojom::TrialComparisonCertVerifierReportClientPtrInfo report_client,
+    scoped_refptr<net::CertVerifyProc> primary_verify_proc,
+    scoped_refptr<net::CertVerifyProc> trial_verify_proc)
+    : binding_(this, std::move(config_client_request)),
+      report_client_(std::move(report_client)) {
+  trial_comparison_cert_verifier_ =
+      std::make_unique<net::TrialComparisonCertVerifier>(
+          initial_allowed, primary_verify_proc, trial_verify_proc,
+          base::BindRepeating(
+              &mojom::TrialComparisonCertVerifierReportClient::SendTrialReport,
+              // Unretained safe because the report_callback will not be called
+              // after trial_comparison_cert_verifier_ is destroyed.
+              base::Unretained(report_client_.get())));
+}
+
+TrialComparisonCertVerifierMojo::~TrialComparisonCertVerifierMojo() = default;
+
+int TrialComparisonCertVerifierMojo::Verify(
+    const RequestParams& params,
+    net::CertVerifyResult* verify_result,
+    net::CompletionOnceCallback callback,
+    std::unique_ptr<Request>* out_req,
+    const net::NetLogWithSource& net_log) {
+  return trial_comparison_cert_verifier_->Verify(
+      params, verify_result, std::move(callback), out_req, net_log);
+}
+
+void TrialComparisonCertVerifierMojo::SetConfig(const Config& config) {
+  trial_comparison_cert_verifier_->SetConfig(config);
+}
+
+void TrialComparisonCertVerifierMojo::OnTrialConfigUpdated(bool allowed) {
+  trial_comparison_cert_verifier_->set_trial_allowed(allowed);
+}
+
+}  // namespace network
diff --git a/services/network/trial_comparison_cert_verifier_mojo.h b/services/network/trial_comparison_cert_verifier_mojo.h
new file mode 100644
index 0000000..03d4ebf
--- /dev/null
+++ b/services/network/trial_comparison_cert_verifier_mojo.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 SERVICES_NETWORK_TRIAL_COMPARISON_CERT_VERIFIER_MOJO_H_
+#define SERVICES_NETWORK_TRIAL_COMPARISON_CERT_VERIFIER_MOJO_H_
+
+#include <memory>
+
+#include "base/component_export.h"
+#include "base/macros.h"
+#include "base/observer_list.h"
+#include "base/optional.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "net/cert/cert_verifier.h"
+#include "services/network/public/mojom/trial_comparison_cert_verifier.mojom.h"
+
+namespace net {
+class CertVerifyProc;
+class CertVerifyResult;
+class TrialComparisonCertVerifier;
+}  // namespace net
+
+namespace network {
+
+// Wrapper around TrialComparisonCertVerifier that does trial configuration and
+// reporting over Mojo pipes.
+class COMPONENT_EXPORT(NETWORK_SERVICE) TrialComparisonCertVerifierMojo
+    : public net::CertVerifier,
+      public mojom::TrialComparisonCertVerifierConfigClient {
+ public:
+  // |initial_allowed| is the initial setting for whether the trial is allowed.
+  // |config_client_request| is the Mojo pipe over which trial configuration
+  // updates are received.
+  // |report_client| is the Mojo pipe used to send trial reports.
+  // |primary_verify_proc| and |trial_verify_proc| are the CertVerifyProc
+  // implementations to compare.
+  TrialComparisonCertVerifierMojo(
+      bool initial_allowed,
+      mojom::TrialComparisonCertVerifierConfigClientRequest
+          config_client_request,
+      mojom::TrialComparisonCertVerifierReportClientPtrInfo report_client,
+      scoped_refptr<net::CertVerifyProc> primary_verify_proc,
+      scoped_refptr<net::CertVerifyProc> trial_verify_proc);
+  ~TrialComparisonCertVerifierMojo() override;
+
+  // CertVerifier implementation
+  int Verify(const RequestParams& params,
+             net::CertVerifyResult* verify_result,
+             net::CompletionOnceCallback callback,
+             std::unique_ptr<Request>* out_req,
+             const net::NetLogWithSource& net_log) override;
+  void SetConfig(const Config& config) override;
+
+ private:
+  // mojom::TrialComparisonCertVerifierConfigClient implementation:
+  void OnTrialConfigUpdated(bool allowed) override;
+
+  mojo::Binding<mojom::TrialComparisonCertVerifierConfigClient> binding_;
+
+  mojom::TrialComparisonCertVerifierReportClientPtr report_client_;
+
+  std::unique_ptr<net::TrialComparisonCertVerifier>
+      trial_comparison_cert_verifier_;
+
+  DISALLOW_COPY_AND_ASSIGN(TrialComparisonCertVerifierMojo);
+};
+
+}  // namespace network
+
+#endif  // SERVICES_NETWORK_TRIAL_COMPARISON_CERT_VERIFIER_MOJO_H_
diff --git a/services/preferences/public/cpp/pref_service_factory.cc b/services/preferences/public/cpp/pref_service_factory.cc
index 41f017b..615d26d 100644
--- a/services/preferences/public/cpp/pref_service_factory.cc
+++ b/services/preferences/public/cpp/pref_service_factory.cc
@@ -68,8 +68,7 @@
                             std::vector<mojom::PrefRegistrationPtr> defaults) {
   for (auto& registration : defaults) {
     pref_registry->SetDefaultForeignPrefValue(
-        registration->key,
-        base::Value::ToUniquePtrValue(std::move(registration->default_value)),
+        registration->key, std::move(registration->default_value),
         registration->flags);
   }
 }
diff --git a/services/preferences/shared_pref_registry.cc b/services/preferences/shared_pref_registry.cc
index dc8ce18..f3777da6 100644
--- a/services/preferences/shared_pref_registry.cc
+++ b/services/preferences/shared_pref_registry.cc
@@ -146,9 +146,8 @@
                        << key << "\"";
 #endif
       registry_->RegisterForeignPref(key);
-      registry_->SetDefaultForeignPrefValue(
-          key, base::Value::ToUniquePtrValue(std::move(default_value)),
-          registration->flags);
+      registry_->SetDefaultForeignPrefValue(key, std::move(default_value),
+                                            registration->flags);
 
       observed_prefs->push_back(key);
       new_public_prefs.push_back(key);
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
index 35a5b1e..fc8a8f2 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.cc
@@ -61,7 +61,6 @@
 void TraceEventMetadataSource::AddGeneratorFunction(
     MetadataGeneratorFunction generator) {
   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
-  base::AutoLock lock(lock_);
   generator_functions_.push_back(generator);
 }
 
@@ -72,7 +71,6 @@
   auto trace_packet = trace_writer->NewTracePacket();
   ChromeEventBundleHandle event_bundle(trace_packet->set_chrome_events());
 
-  base::AutoLock lock(lock_);
   for (auto& generator : generator_functions_) {
     std::unique_ptr<base::DictionaryValue> metadata_dict = generator.Run();
     if (!metadata_dict) {
@@ -103,22 +101,20 @@
     const mojom::DataSourceConfig& data_source_config) {
   // TODO(eseckler): Once we support streaming of trace data, it would make
   // sense to emit the metadata on startup, so the UI can display it right away.
-  producer_client_ = producer_client;
-  target_buffer_ = data_source_config.target_buffer;
+  trace_writer_ =
+      producer_client->CreateTraceWriter(data_source_config.target_buffer);
 }
 
 void TraceEventMetadataSource::StopTracing(
     base::OnceClosure stop_complete_callback) {
-  if (producer_client_) {
+  if (trace_writer_) {
     // Write metadata at the end of tracing to make it less likely that it is
     // overridden by other trace data in perfetto's ring buffer.
     origin_task_runner_->PostTaskAndReply(
         FROM_HERE,
         base::BindOnce(&TraceEventMetadataSource::GenerateMetadata,
-                       base::Unretained(this),
-                       producer_client_->CreateTraceWriter(target_buffer_)),
+                       base::Unretained(this), std::move(trace_writer_)),
         std::move(stop_complete_callback));
-    producer_client_ = nullptr;
   } else {
     std::move(stop_complete_callback).Run();
   }
@@ -589,7 +585,7 @@
   base::Base64Encode(
       std::string(static_cast<const char*>(pickle.data()), pickle.size()),
       &buckets);
-  TRACE_EVENT_INSTANT2("benchmark", "UMAHistogramDelta",
+  TRACE_EVENT_INSTANT2("benchmark", "UMAHistogramSamples",
                        TRACE_EVENT_SCOPE_PROCESS, "name",
                        histogram->histogram_name(), "buckets", buckets);
 }
diff --git a/services/tracing/public/cpp/perfetto/trace_event_data_source.h b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
index 2ff06aae..bb1028d 100644
--- a/services/tracing/public/cpp/perfetto/trace_event_data_source.h
+++ b/services/tracing/public/cpp/perfetto/trace_event_data_source.h
@@ -49,9 +49,7 @@
 
   std::vector<MetadataGeneratorFunction> generator_functions_;
   scoped_refptr<base::SequencedTaskRunner> origin_task_runner_;
-  base::Lock lock_;
-  ProducerClient* producer_client_ = nullptr;
-  uint32_t target_buffer_ = 0;
+  std::unique_ptr<perfetto::TraceWriter> trace_writer_;
 
   DISALLOW_COPY_AND_ASSIGN(TraceEventMetadataSource);
 };
diff --git a/services/ws/client_root.cc b/services/ws/client_root.cc
index e78a2dc..4d97324f0 100644
--- a/services/ws/client_root.cc
+++ b/services/ws/client_root.cc
@@ -127,6 +127,16 @@
                                                 : window_->bounds());
 }
 
+void ClientRoot::AllocateLocalSurfaceIdAndNotifyClient() {
+  if (!ShouldAssignLocalSurfaceId())
+    return;
+
+  // Setting a null LocalSurfaceId forces allocation.
+  ProxyWindow::GetMayBeNull(window_)->set_local_surface_id(base::nullopt);
+  UpdateLocalSurfaceIdAndClientSurfaceEmbedder();
+  NotifyClientOfNewBounds(last_bounds_);
+}
+
 void ClientRoot::AttachChildFrameSinkId(ProxyWindow* proxy_window) {
   DCHECK(proxy_window->attached_frame_sink_id().is_valid());
   DCHECK(ProxyWindow::GetMayBeNull(window_)->frame_sink_id().is_valid());
diff --git a/services/ws/client_root.h b/services/ws/client_root.h
index c4cb0829..b74f8bdb 100644
--- a/services/ws/client_root.h
+++ b/services/ws/client_root.h
@@ -67,6 +67,8 @@
   // Called when the LocalSurfaceId of the embedder changes.
   void OnLocalSurfaceIdChanged();
 
+  void AllocateLocalSurfaceIdAndNotifyClient();
+
   // Attaches/unattaches proxy_window->attached_frame_sink_id() to the
   // HostFrameSinkManager.
   void AttachChildFrameSinkId(ProxyWindow* proxy_window);
diff --git a/services/ws/focus_handler.cc b/services/ws/focus_handler.cc
index ad9ad5d..1b98a85e 100644
--- a/services/ws/focus_handler.cc
+++ b/services/ws/focus_handler.cc
@@ -93,15 +93,6 @@
   return true;
 }
 
-void FocusHandler::SetCanFocus(aura::Window* window, bool can_focus) {
-  if (window && (window_tree_->IsClientCreatedWindow(window) ||
-                 window_tree_->IsClientRootWindow(window))) {
-    window->SetProperty(kCanFocus, can_focus);
-  } else {
-    DVLOG(1) << "SetCanFocus failed (invalid or unknown window)";
-  }
-}
-
 bool FocusHandler::IsFocusableWindow(aura::Window* window) const {
   if (!window)
     return true;  // Used to clear focus.
diff --git a/services/ws/focus_handler.h b/services/ws/focus_handler.h
index f53a92f..c44cbde 100644
--- a/services/ws/focus_handler.h
+++ b/services/ws/focus_handler.h
@@ -27,9 +27,6 @@
   // Sets focus to |window| (which may be null). Returns true on success.
   bool SetFocus(aura::Window* window);
 
-  // Sets whether |window| can be focused.
-  void SetCanFocus(aura::Window* window, bool can_focus);
-
  private:
   // Returns true if |window| can be focused.
   bool IsFocusableWindow(aura::Window* window) const;
diff --git a/services/ws/focus_handler_unittest.cc b/services/ws/focus_handler_unittest.cc
index e166fb2..6df49dd 100644
--- a/services/ws/focus_handler_unittest.cc
+++ b/services/ws/focus_handler_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "services/ws/event_test_utils.h"
 #include "services/ws/public/mojom/window_tree_constants.mojom.h"
+#include "services/ws/window_properties.h"
 #include "services/ws/window_service.h"
 #include "services/ws/window_service_test_setup.h"
 #include "services/ws/window_tree_test_helper.h"
@@ -78,13 +79,13 @@
   top_level->AddChild(window);
   // SetFocus() should still fail as |window| isn't visible.
   EXPECT_FALSE(setup.window_tree_test_helper()->SetFocus(window));
-  setup.window_tree_test_helper()->SetCanFocus(window, false);
+  window->SetProperty(kCanFocus, false);
   window->Show();
 
-  // SetFocus() should fail as SetCanFocus(false) was called.
+  // SetFocus() should fail as kCanFocus is false.
   EXPECT_FALSE(setup.window_tree_test_helper()->SetFocus(window));
 
-  setup.window_tree_test_helper()->SetCanFocus(window, true);
+  window->ClearProperty(kCanFocus);
   EXPECT_TRUE(setup.window_tree_test_helper()->SetFocus(window));
 }
 
diff --git a/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc b/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
index 3e822a3..1b0f0128 100644
--- a/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
+++ b/services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.cc
@@ -4,6 +4,8 @@
 
 #include "services/ws/public/cpp/gpu/client_gpu_memory_buffer_manager.h"
 
+#include <utility>
+
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/memory/shared_memory.h"
@@ -17,21 +19,15 @@
 #include "services/ws/public/mojom/constants.mojom.h"
 #include "ui/gfx/buffer_format_util.h"
 
-using DestructionCallback = base::Callback<void(const gpu::SyncToken& sync)>;
-
-namespace gpu {
-class GpuMemoryBufferSupport;
-}
-
 namespace ws {
-
 namespace {
 
 void NotifyDestructionOnCorrectThread(
     scoped_refptr<base::SingleThreadTaskRunner> task_runner,
-    const DestructionCallback& callback,
+    gpu::GpuMemoryBufferImpl::DestructionCallback callback,
     const gpu::SyncToken& sync_token) {
-  task_runner->PostTask(FROM_HERE, base::Bind(callback, sync_token));
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(std::move(callback), sync_token));
 }
 
 }  // namespace
@@ -46,15 +42,14 @@
   // The thread is owned by this object. Which means the task will not run if
   // the object has been destroyed. So Unretained() is safe.
   thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::Bind(&ClientGpuMemoryBufferManager::InitThread,
-                 base::Unretained(this), base::Passed(gpu.PassInterface())));
+      FROM_HERE, base::BindOnce(&ClientGpuMemoryBufferManager::InitThread,
+                                base::Unretained(this), gpu.PassInterface()));
 }
 
 ClientGpuMemoryBufferManager::~ClientGpuMemoryBufferManager() {
   thread_.task_runner()->PostTask(
-      FROM_HERE, base::Bind(&ClientGpuMemoryBufferManager::TearDownThread,
-                            base::Unretained(this)));
+      FROM_HERE, base::BindOnce(&ClientGpuMemoryBufferManager::TearDownThread,
+                                base::Unretained(this)));
   thread_.Stop();
 }
 
@@ -62,8 +57,8 @@
     mojom::GpuMemoryBufferFactoryPtrInfo gpu_info) {
   gpu_.Bind(std::move(gpu_info));
   gpu_.set_connection_error_handler(
-      base::Bind(&ClientGpuMemoryBufferManager::DisconnectGpuOnThread,
-                 base::Unretained(this)));
+      base::BindOnce(&ClientGpuMemoryBufferManager::DisconnectGpuOnThread,
+                     base::Unretained(this)));
   weak_ptr_ = weak_ptr_factory_.GetWeakPtr();
 }
 
@@ -102,7 +97,7 @@
   pending_allocation_waiters_.insert(wait);
   gpu_->CreateGpuMemoryBuffer(
       gfx::GpuMemoryBufferId(++counter_), size, format, usage,
-      base::Bind(
+      base::BindOnce(
           &ClientGpuMemoryBufferManager::OnGpuMemoryBufferAllocatedOnThread,
           base::Unretained(this), handle, wait));
 }
@@ -125,8 +120,8 @@
   if (!thread_.task_runner()->BelongsToCurrentThread()) {
     thread_.task_runner()->PostTask(
         FROM_HERE,
-        base::Bind(&ClientGpuMemoryBufferManager::DeletedGpuMemoryBuffer,
-                   base::Unretained(this), id, sync_token));
+        base::BindOnce(&ClientGpuMemoryBufferManager::DeletedGpuMemoryBuffer,
+                       base::Unretained(this), id, sync_token));
     return;
   }
 
@@ -149,22 +144,22 @@
                            base::WaitableEvent::InitialState::NOT_SIGNALED);
   thread_.task_runner()->PostTask(
       FROM_HERE,
-      base::Bind(&ClientGpuMemoryBufferManager::AllocateGpuMemoryBufferOnThread,
-                 base::Unretained(this), size, format, usage, &gmb_handle,
-                 &wait));
+      base::BindOnce(
+          &ClientGpuMemoryBufferManager::AllocateGpuMemoryBufferOnThread,
+          base::Unretained(this), size, format, usage, &gmb_handle, &wait));
   wait.Wait();
   if (gmb_handle.is_null())
     return nullptr;
 
   auto gmb_handle_id = gmb_handle.id;
-  DestructionCallback callback =
-      base::Bind(&ClientGpuMemoryBufferManager::DeletedGpuMemoryBuffer,
-                 weak_ptr_, gmb_handle_id);
-  std::unique_ptr<gpu::GpuMemoryBufferImpl> buffer(
+  auto callback =
+      base::BindOnce(&ClientGpuMemoryBufferManager::DeletedGpuMemoryBuffer,
+                     weak_ptr_, gmb_handle_id);
+  std::unique_ptr<gpu::GpuMemoryBufferImpl> buffer =
       gpu_memory_buffer_support_->CreateGpuMemoryBufferImplFromHandle(
           std::move(gmb_handle), size, format, usage,
-          base::Bind(&NotifyDestructionOnCorrectThread, thread_.task_runner(),
-                     callback)));
+          base::BindOnce(&NotifyDestructionOnCorrectThread,
+                         thread_.task_runner(), std::move(callback)));
   if (!buffer) {
     DeletedGpuMemoryBuffer(gmb_handle_id, gpu::SyncToken());
     return nullptr;
diff --git a/services/ws/public/mojom/window_tree.mojom b/services/ws/public/mojom/window_tree.mojom
index c789701..7559292 100644
--- a/services/ws/public/mojom/window_tree.mojom
+++ b/services/ws/public/mojom/window_tree.mojom
@@ -110,6 +110,11 @@
                   gfx.mojom.Rect bounds,
                   viz.mojom.LocalSurfaceId? local_surface_id);
 
+  // Asks the server to generate a new LocalSurfaceId for a window. The server
+  // responds with the new LocalSurfaceId by way of OnWindowBoundsChanged().
+  // This is only useful for top-levels.
+  AllocateLocalSurfaceId(uint64 window_id);
+
   SetWindowTransform(uint32 change_id,
                      uint64 window_id,
                      gfx.mojom.Transform transform);
@@ -285,14 +290,11 @@
 
   // Sets focus to the specified window, use 0 to clear focus. For a window to
   // get focus the following has to happen: the window is drawn, the window has
-  // been marked as focusable (see SetCanFocus()) and the window is in a
-  // container the WindowManager has identified as allowing activation
-  // (see WindowManagerClient::AddActivationParent()).
+  // been marked as focusable and the window is in a container the WindowManager
+  // has identified as allowing activation (see
+  // WindowManagerClient::AddActivationParent()).
   SetFocus(uint32 change_id, uint64 window_id);
 
-  // Marks the specified window as being able to receive focus.
-  SetCanFocus(uint64 window_id, bool can_focus);
-
   // Sets the cursor when the pointer is inside |window_id|.
   SetCursor(uint32 change_id, uint64 window_id, ui.mojom.Cursor cursor);
 
diff --git a/services/ws/window_tree.cc b/services/ws/window_tree.cc
index d948c31..61e7eaab 100644
--- a/services/ws/window_tree.cc
+++ b/services/ws/window_tree.cc
@@ -1547,6 +1547,20 @@
                                      local_surface_id));
 }
 
+void WindowTree::AllocateLocalSurfaceId(Id transport_window_id) {
+  const ClientWindowId window_id = MakeClientWindowId(transport_window_id);
+  aura::Window* window = GetWindowByClientId(window_id);
+  DVLOG(3) << "AllocateLocalSurfaceId client window_id="
+           << window_id.ToString();
+  if (!window || !IsTopLevel(window)) {
+    DVLOG(1) << "AllocateLocalSurfaceId failed (invalid window id)";
+    return;
+  }
+  ClientRoot* client_root = GetClientRootForWindow(window);
+  DCHECK(client_root);
+  client_root->AllocateLocalSurfaceIdAndNotifyClient();
+}
+
 void WindowTree::SetWindowTransform(uint32_t change_id,
                                     Id window_id,
                                     const gfx::Transform& transform) {
@@ -1873,11 +1887,6 @@
       change_id, SetFocusImpl(MakeClientWindowId(transport_window_id)));
 }
 
-void WindowTree::SetCanFocus(Id transport_window_id, bool can_focus) {
-  focus_handler_.SetCanFocus(GetWindowByTransportId(transport_window_id),
-                             can_focus);
-}
-
 void WindowTree::SetCursor(uint32_t change_id,
                            Id transport_window_id,
                            ui::Cursor cursor) {
@@ -2032,11 +2041,6 @@
     window_tree_client_->OnChangeCompleted(change_id, false);
     return;
   }
-  if (!ui::CanPerformDragOrResize(hit_test)) {
-    DVLOG(1) << "PerformWindowMove failed (incorrect hit_test)";
-    window_tree_client_->OnChangeCompleted(change_id, false);
-    return;
-  }
 
   window_moving_ = window;
   window_service_->delegate()->RunWindowMoveLoop(
diff --git a/services/ws/window_tree.h b/services/ws/window_tree.h
index cc3ef49..3710bc0 100644
--- a/services/ws/window_tree.h
+++ b/services/ws/window_tree.h
@@ -396,6 +396,7 @@
       Id window_id,
       const gfx::Rect& bounds,
       const base::Optional<viz::LocalSurfaceId>& local_surface_id) override;
+  void AllocateLocalSurfaceId(Id transport_window_id) override;
   void SetWindowTransform(uint32_t change_id,
                           Id window_id,
                           const gfx::Transform& transform) override;
@@ -454,7 +455,6 @@
                        uint32_t embed_flags,
                        EmbedUsingTokenCallback callback) override;
   void SetFocus(uint32_t change_id, Id transport_window_id) override;
-  void SetCanFocus(Id transport_window_id, bool can_focus) override;
   void SetCursor(uint32_t change_id,
                  Id transport_window_id,
                  ui::Cursor cursor) override;
diff --git a/services/ws/window_tree_test_helper.cc b/services/ws/window_tree_test_helper.cc
index 4d98b99..7a066e94 100644
--- a/services/ws/window_tree_test_helper.cc
+++ b/services/ws/window_tree_test_helper.cc
@@ -156,11 +156,6 @@
   return window_tree_->SetFocusImpl(ClientWindowIdForWindow(window));
 }
 
-void WindowTreeTestHelper::SetCanFocus(aura::Window* window, bool can_focus) {
-  window_tree_->SetCanFocus(window_tree_->TransportIdForWindow(window),
-                            can_focus);
-}
-
 void WindowTreeTestHelper::SetCursor(aura::Window* window, ui::Cursor cursor) {
   window_tree_->SetCursorImpl(ClientWindowIdForWindow(window), cursor);
 }
diff --git a/services/ws/window_tree_test_helper.h b/services/ws/window_tree_test_helper.h
index f9c4b9e8..9c585d23 100644
--- a/services/ws/window_tree_test_helper.h
+++ b/services/ws/window_tree_test_helper.h
@@ -98,7 +98,6 @@
   void SetEventTargetingPolicy(aura::Window* window,
                                mojom::EventTargetingPolicy policy);
   bool SetFocus(aura::Window* window);
-  void SetCanFocus(aura::Window* window, bool can_focus);
   void SetCursor(aura::Window* window, ui::Cursor cursor);
   void OnWindowInputEventAck(uint32_t event_id, mojom::EventResult result);
   bool StackAbove(aura::Window* above_window, aura::Window* below_window);
diff --git a/skia/BUILD.gn b/skia/BUILD.gn
index 590eb21..71883ad 100644
--- a/skia/BUILD.gn
+++ b/skia/BUILD.gn
@@ -275,12 +275,6 @@
     "ext/convolver.h",
     "ext/event_tracer_impl.cc",
     "ext/event_tracer_impl.h",
-    "ext/fontmgr_default_android.cc",
-    "ext/fontmgr_default_android.h",
-    "ext/fontmgr_default_linux.cc",
-    "ext/fontmgr_default_linux.h",
-    "ext/fontmgr_default_win.cc",
-    "ext/fontmgr_default_win.h",
     "ext/google_logging.cc",
     "ext/image_operations.cc",
     "ext/image_operations.h",
@@ -304,6 +298,16 @@
     "ext/skia_utils_win.h",
   ]
 
+  if (!is_mac && !is_ios) {
+    sources += [
+      "ext/fontmgr_default.cc",
+      "ext/fontmgr_default.h",
+      "ext/fontmgr_default_android.cc",
+      "ext/fontmgr_default_linux.cc",
+      "ext/fontmgr_default_win.cc",
+    ]
+  }
+
   if (!is_ios) {
     sources += [
       "ext/platform_canvas.cc",
@@ -487,7 +491,6 @@
       "//third_party/skia/src/ports/SkFontMgr_custom.cpp",
       "//third_party/skia/src/ports/SkFontMgr_fuchsia.cpp",
       "ext/fontmgr_default_fuchsia.cc",
-      "ext/fontmgr_default_fuchsia.h",
     ]
     deps += [
       "//third_party/fuchsia-sdk/sdk:fonts",
diff --git a/skia/ext/fontmgr_default.cc b/skia/ext/fontmgr_default.cc
new file mode 100644
index 0000000..d4b6201
--- /dev/null
+++ b/skia/ext/fontmgr_default.cc
@@ -0,0 +1,34 @@
+// 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 "skia/ext/fontmgr_default.h"
+
+#include "third_party/skia/include/core/SkFontMgr.h"
+
+namespace {
+
+SkDEBUGCODE(bool g_factory_called;)
+
+    // This is a purposefully leaky pointer that has ownership of the FontMgr.
+    SkFontMgr* g_fontmgr_override = nullptr;
+
+}  // namespace
+
+namespace skia {
+
+void OverrideDefaultSkFontMgr(sk_sp<SkFontMgr> fontmgr) {
+  SkASSERT(!g_factory_called);
+
+  SkSafeUnref(g_fontmgr_override);
+  g_fontmgr_override = fontmgr.release();
+}
+
+}  // namespace skia
+
+SK_API sk_sp<SkFontMgr> SkFontMgr::Factory() {
+  SkDEBUGCODE(g_factory_called = true;);
+
+  return g_fontmgr_override ? sk_ref_sp(g_fontmgr_override)
+                            : skia::CreateDefaultSkFontMgr();
+}
\ No newline at end of file
diff --git a/skia/ext/fontmgr_default.h b/skia/ext/fontmgr_default.h
new file mode 100644
index 0000000..92cbe9f
--- /dev/null
+++ b/skia/ext/fontmgr_default.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 SKIA_EXT_FONTMGR_DEFAULT_H_
+#define SKIA_EXT_FONTMGR_DEFAULT_H_
+
+#include "third_party/skia/include/core/SkTypes.h"
+
+class SkFontMgr;
+template <typename T>
+class sk_sp;
+
+namespace skia {
+
+// Allows to override the default SkFontMgr instance (returned from
+// SkFontMgr::RefDefault()). Must be called before RefDefault() is called for
+// the first time in the process.
+SK_API void OverrideDefaultSkFontMgr(sk_sp<SkFontMgr> fontmgr);
+
+// Create default SkFontMgr implementation for the current platform.
+SK_API sk_sp<SkFontMgr> CreateDefaultSkFontMgr();
+
+}  // namespace skia
+
+#endif  // SKIA_EXT_FONTMGR_DEFAULT_H_
diff --git a/skia/ext/fontmgr_default_android.cc b/skia/ext/fontmgr_default_android.cc
index 1b563374..18987e50 100644
--- a/skia/ext/fontmgr_default_android.cc
+++ b/skia/ext/fontmgr_default_android.cc
@@ -2,22 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "skia/ext/fontmgr_default_android.h"
+#include "skia/ext/fontmgr_default.h"
 
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/ports/SkFontMgr_android.h"
 
-namespace {
-// An owning leaky bare pointer.
-SkFontMgr* g_default_fontmgr;
-}  // namespace
+namespace skia {
 
-SK_API void SetDefaultSkiaFactory(sk_sp<SkFontMgr> fontmgr) {
-  SkASSERT(g_default_fontmgr == nullptr);
-  g_default_fontmgr = fontmgr.release();
+SK_API sk_sp<SkFontMgr> CreateDefaultSkFontMgr() {
+  return SkFontMgr_New_Android(nullptr);
 }
 
-SK_API sk_sp<SkFontMgr> SkFontMgr::Factory() {
-  return g_default_fontmgr ? sk_ref_sp(g_default_fontmgr)
-                           : SkFontMgr_New_Android(nullptr);
-}
+}  // namespace skia
\ No newline at end of file
diff --git a/skia/ext/fontmgr_default_android.h b/skia/ext/fontmgr_default_android.h
deleted file mode 100644
index 1dda9c6..0000000
--- a/skia/ext/fontmgr_default_android.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SKIA_EXT_FONTMGR_DEFAULT_ANDROID_H_
-#define SKIA_EXT_FONTMGR_DEFAULT_ANDROID_H_
-
-#include "third_party/skia/include/core/SkTypes.h"
-
-class SkFontMgr;
-template <typename T> class sk_sp;
-
-SK_API void SetDefaultSkiaFactory(sk_sp<SkFontMgr> fontmgr);
-
-#endif  // SKIA_EXT_FONTMGR_DEFAULT_ANDROID_H_
diff --git a/skia/ext/fontmgr_default_fuchsia.cc b/skia/ext/fontmgr_default_fuchsia.cc
index 42702d6..2e6811d 100644
--- a/skia/ext/fontmgr_default_fuchsia.cc
+++ b/skia/ext/fontmgr_default_fuchsia.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 "skia/ext/fontmgr_default_fuchsia.h"
+#include "skia/ext/fontmgr_default.h"
 
 #include <fuchsia/fonts/cpp/fidl.h>
 
@@ -10,21 +10,12 @@
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/ports/SkFontMgr_fuchsia.h"
 
-namespace {
-// This is a purposefully leaky pointer that has ownership of the FontMgr.
-SkFontMgr* g_default_fontmgr;
-}  // namespace
+namespace skia {
 
-void SetDefaultSkiaFactory(sk_sp<SkFontMgr> fontmgr) {
-  SkASSERT(g_default_fontmgr == nullptr);
-  g_default_fontmgr = fontmgr.release();
-}
-
-SK_API sk_sp<SkFontMgr> SkFontMgr::Factory() {
-  if (g_default_fontmgr) {
-    return sk_ref_sp(g_default_fontmgr);
-  }
+SK_API sk_sp<SkFontMgr> CreateDefaultSkFontMgr() {
   return SkFontMgr_New_Fuchsia(
       base::fuchsia::ComponentContext::GetDefault()
           ->ConnectToServiceSync<fuchsia::fonts::Provider>());
 }
+
+}  // namespace skia
\ No newline at end of file
diff --git a/skia/ext/fontmgr_default_fuchsia.h b/skia/ext/fontmgr_default_fuchsia.h
deleted file mode 100644
index 7af5743..0000000
--- a/skia/ext/fontmgr_default_fuchsia.h
+++ /dev/null
@@ -1,16 +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 SKIA_EXT_FONTMGR_DEFAULT_FUCHSIA_H_
-#define SKIA_EXT_FONTMGR_DEFAULT_FUCHSIA_H_
-
-#include "third_party/skia/include/core/SkTypes.h"
-
-class SkFontMgr;
-template <typename T>
-class sk_sp;
-
-void SK_API SetDefaultSkiaFactory(sk_sp<SkFontMgr> fontmgr);
-
-#endif  // SKIA_EXT_FONTMGR_DEFAULT_FUCHSIA_H_
diff --git a/skia/ext/fontmgr_default_linux.cc b/skia/ext/fontmgr_default_linux.cc
index 1885ba6..9b1ef8a 100644
--- a/skia/ext/fontmgr_default_linux.cc
+++ b/skia/ext/fontmgr_default_linux.cc
@@ -2,26 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "skia/ext/fontmgr_default_linux.h"
+#include "skia/ext/fontmgr_default.h"
 
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/ports/SkFontConfigInterface.h"
 #include "third_party/skia/include/ports/SkFontMgr_FontConfigInterface.h"
 
-namespace {
-// This is a purposefully leaky pointer that has ownership of the FontMgr.
-SkFontMgr* g_default_fontmgr;
-}  // namespace
+namespace skia {
 
-void SetDefaultSkiaFactory(sk_sp<SkFontMgr> fontmgr) {
-  SkASSERT(g_default_fontmgr == nullptr);
-  g_default_fontmgr = fontmgr.release();
-}
-
-SK_API sk_sp<SkFontMgr> SkFontMgr::Factory() {
-  if (g_default_fontmgr) {
-    return sk_ref_sp(g_default_fontmgr);
-  }
+SK_API sk_sp<SkFontMgr> CreateDefaultSkFontMgr() {
   sk_sp<SkFontConfigInterface> fci(SkFontConfigInterface::RefGlobal());
   return fci ? SkFontMgr_New_FCI(std::move(fci)) : nullptr;
 }
+
+}  // namespace skia
\ No newline at end of file
diff --git a/skia/ext/fontmgr_default_linux.h b/skia/ext/fontmgr_default_linux.h
deleted file mode 100644
index 29420074..0000000
--- a/skia/ext/fontmgr_default_linux.h
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2016 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SKIA_EXT_FONTMGR_DEFAULT_LINUX_H_
-#define SKIA_EXT_FONTMGR_DEFAULT_LINUX_H_
-
-#include "third_party/skia/include/core/SkTypes.h"
-
-class SkFontMgr;
-template <typename T> class sk_sp;
-
-void SK_API SetDefaultSkiaFactory(sk_sp<SkFontMgr> fontmgr);
-
-#endif  // SKIA_EXT_FONTMGR_DEFAULT_LINUX_H_
diff --git a/skia/ext/fontmgr_default_win.cc b/skia/ext/fontmgr_default_win.cc
index 9d3b870f..5903fa0 100644
--- a/skia/ext/fontmgr_default_win.cc
+++ b/skia/ext/fontmgr_default_win.cc
@@ -2,36 +2,15 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "skia/ext/fontmgr_default_win.h"
+#include "skia/ext/fontmgr_default.h"
 
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/ports/SkTypeface_win.h"
 
-namespace {
+namespace skia {
 
-// This is a leaky bare owning pointer.
-SkFontMgr* g_default_fontmgr;
-
-// The ppapi code currently calls SetDefaultSkiaFactory twice on Win8+.
-// This tracks when the global escapes and shouldno longer be set.
-SkDEBUGCODE(bool g_factory_called;)
-
-}  // namespace
-
-void SetDefaultSkiaFactory(sk_sp<SkFontMgr> fontmgr) {
-  SkASSERT(!g_factory_called);
-
-  SkSafeUnref(g_default_fontmgr);
-  g_default_fontmgr = fontmgr.release();
-}
-
-SK_API sk_sp<SkFontMgr> SkFontMgr::Factory() {
-  SkDEBUGCODE(g_factory_called = true;)
-
-  // This will be set when DirectWrite is in use, and an SkFontMgr has been
-  // created with the pre-sandbox warmed up one. Otherwise, we fallback to a
-  // GDI SkFontMgr which is used in the browser.
-  if (g_default_fontmgr)
-    return sk_ref_sp(g_default_fontmgr);
+SK_API sk_sp<SkFontMgr> CreateDefaultSkFontMgr() {
   return SkFontMgr_New_GDI();
 }
+
+}  // namespace skia
\ No newline at end of file
diff --git a/skia/ext/fontmgr_default_win.h b/skia/ext/fontmgr_default_win.h
deleted file mode 100644
index a41d94ad..0000000
--- a/skia/ext/fontmgr_default_win.h
+++ /dev/null
@@ -1,15 +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 SKIA_EXT_FONTMGR_DEFAULT_WIN_H_
-#define SKIA_EXT_FONTMGR_DEFAULT_WIN_H_
-
-#include "third_party/skia/include/core/SkTypes.h"
-
-class SkFontMgr;
-template <typename T> class sk_sp;
-
-void SK_API SetDefaultSkiaFactory(sk_sp<SkFontMgr> fontmgr);
-
-#endif  // SKIA_EXT_FONTMGR_DEFAULT_WIN_H_
diff --git a/storage/browser/BUILD.gn b/storage/browser/BUILD.gn
index e280660..a5adcde 100644
--- a/storage/browser/BUILD.gn
+++ b/storage/browser/BUILD.gn
@@ -131,6 +131,8 @@
     "fileapi/native_file_util.h",
     "fileapi/obfuscated_file_util.cc",
     "fileapi/obfuscated_file_util.h",
+    "fileapi/obfuscated_file_util_disk_delegate.cc",
+    "fileapi/obfuscated_file_util_disk_delegate.h",
     "fileapi/open_file_system_mode.h",
     "fileapi/plugin_private_file_system_backend.cc",
     "fileapi/plugin_private_file_system_backend.h",
diff --git a/storage/browser/blob/blob_memory_controller.cc b/storage/browser/blob/blob_memory_controller.cc
index 1d73842..1bcfaeb 100644
--- a/storage/browser/blob/blob_memory_controller.cc
+++ b/storage/browser/blob/blob_memory_controller.cc
@@ -71,10 +71,14 @@
 // Desktop:
 // * Ram -  20%, or 2 GB if x64.
 // * Disk - 10%
-BlobStorageLimits CalculateBlobStorageLimitsImpl(const FilePath& storage_dir,
-                                                 bool disk_enabled) {
+BlobStorageLimits CalculateBlobStorageLimitsImpl(
+    const FilePath& storage_dir,
+    bool disk_enabled,
+    base::Optional<int64_t> optional_memory_size_for_testing) {
   int64_t disk_size = 0ull;
-  int64_t memory_size = base::SysInfo::AmountOfPhysicalMemory();
+  int64_t memory_size = optional_memory_size_for_testing
+                            ? optional_memory_size_for_testing.value()
+                            : base::SysInfo::AmountOfPhysicalMemory();
   if (disk_enabled && CreateBlobDirectory(storage_dir) == base::File::FILE_OK)
     disk_size = base::SysInfo::AmountOfTotalDiskSpace(storage_dir);
 
@@ -91,6 +95,11 @@
     limits.max_blob_in_memory_space = static_cast<size_t>(memory_size / 5ll);
 #endif
   }
+  // Devices just on the edge (RAM == 256MB) should not fail because
+  // max_blob_in_memory_space turns out smaller than min_page_file_size
+  // causing the CHECK below to fail.
+  if (limits.max_blob_in_memory_space < limits.min_page_file_size)
+    limits.max_blob_in_memory_space = limits.min_page_file_size;
 
   // Don't do specialty configuration for error size (-1). Allow no disk.
   if (disk_size >= 0) {
@@ -744,12 +753,12 @@
     PostTaskAndReplyWithResult(
         file_runner_.get(), FROM_HERE,
         base::BindOnce(&CalculateBlobStorageLimitsImpl, blob_storage_dir_,
-                       true),
+                       true, amount_of_memory_for_testing_),
         base::BindOnce(&BlobMemoryController::OnStorageLimitsCalculated,
                        weak_factory_.GetWeakPtr()));
   } else {
-    OnStorageLimitsCalculated(
-        CalculateBlobStorageLimitsImpl(blob_storage_dir_, false));
+    OnStorageLimitsCalculated(CalculateBlobStorageLimitsImpl(
+        blob_storage_dir_, false, amount_of_memory_for_testing_));
   }
 }
 
diff --git a/storage/browser/blob/blob_memory_controller.h b/storage/browser/blob/blob_memory_controller.h
index 6824d436..4e51600 100644
--- a/storage/browser/blob/blob_memory_controller.h
+++ b/storage/browser/blob/blob_memory_controller.h
@@ -197,6 +197,10 @@
   // synchronously.
   void CallWhenStorageLimitsAreKnown(base::OnceClosure callback);
 
+  void set_amount_of_physical_memory_for_testing(int64_t amount_of_memory) {
+    amount_of_memory_for_testing_ = amount_of_memory;
+  }
+
  private:
   class FileQuotaAllocationTask;
   class MemoryQuotaAllocationTask;
@@ -274,6 +278,8 @@
   bool did_calculate_storage_limits_ = false;
   std::vector<base::OnceClosure> on_calculate_limits_callbacks_;
 
+  base::Optional<int64_t> amount_of_memory_for_testing_;
+
   // Memory bookkeeping. These numbers are all disjoint.
   // This is the amount of memory we're using for blobs in RAM, including the
   // in_flight_memory_used_.
diff --git a/storage/browser/blob/blob_memory_controller_unittest.cc b/storage/browser/blob/blob_memory_controller_unittest.cc
index fbc47664..b7b6201 100644
--- a/storage/browser/blob/blob_memory_controller_unittest.cc
+++ b/storage/browser/blob/blob_memory_controller_unittest.cc
@@ -5,6 +5,7 @@
 #include "storage/browser/blob/blob_memory_controller.h"
 
 #include "base/bind.h"
+#include "base/bind_helpers.h"
 #include "base/files/file_util.h"
 #include "base/files/scoped_temp_dir.h"
 #include "base/message_loop/message_loop.h"
@@ -1169,4 +1170,15 @@
   return;
 }
 
+TEST_F(BlobMemoryControllerTest, LowMemoryDevice) {
+  BlobMemoryController controller(temp_dir_.GetPath(), nullptr);
+  // Make 1% of physical memory size just less than min_page_file_size
+  controller.set_amount_of_physical_memory_for_testing(
+      controller.limits().min_page_file_size * 99);
+  base::RunLoop loop;
+  controller.CallWhenStorageLimitsAreKnown(loop.QuitClosure());
+  loop.Run();
+  EXPECT_TRUE(controller.limits().IsValid());
+}
+
 }  // namespace storage
diff --git a/storage/browser/database/database_quota_client.cc b/storage/browser/database/database_quota_client.cc
index 1fe1a66f..09701f9 100644
--- a/storage/browser/database/database_quota_client.cc
+++ b/storage/browser/database/database_quota_client.cc
@@ -7,6 +7,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <utility>
 #include <vector>
 
 #include "base/bind.h"
@@ -190,8 +191,8 @@
 
   base::PostTaskAndReplyWithResult(
       db_tracker_->task_runner(), FROM_HERE,
-      base::BindOnce(&DatabaseTracker::DeleteDataForOrigin, db_tracker_,
-                     storage::GetIdentifierFromOrigin(origin), delete_callback),
+      base::BindOnce(&DatabaseTracker::DeleteDataForOrigin, db_tracker_, origin,
+                     delete_callback),
       net::CompletionOnceCallback(delete_callback));
 }
 
diff --git a/storage/browser/database/database_quota_client_unittest.cc b/storage/browser/database/database_quota_client_unittest.cc
index 3d9986d..417a885 100644
--- a/storage/browser/database/database_quota_client_unittest.cc
+++ b/storage/browser/database/database_quota_client_unittest.cc
@@ -6,6 +6,7 @@
 
 #include <map>
 #include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/callback_helpers.h"
@@ -70,7 +71,7 @@
     return true;
   }
 
-  int DeleteDataForOrigin(const std::string& origin_identifier,
+  int DeleteDataForOrigin(const url::Origin& origin,
                           net::CompletionOnceCallback callback) override {
     ++delete_called_count_;
     if (async_delete()) {
@@ -109,7 +110,7 @@
 
     void AddMockDatabase(const base::string16& name, int size) {
       EXPECT_TRUE(database_info_.find(name) == database_info_.end());
-      database_info_[name].first = size;
+      database_info_[name].size = size;
       total_size_ += size;
     }
   };
diff --git a/storage/browser/database/database_tracker.cc b/storage/browser/database/database_tracker.cc
index e4a8bbd..a7ef758 100644
--- a/storage/browser/database/database_tracker.cc
+++ b/storage/browser/database/database_tracker.cc
@@ -5,9 +5,8 @@
 #include "storage/browser/database/database_tracker.h"
 
 #include <stdint.h>
+
 #include <algorithm>
-#include <utility>
-#include <vector>
 
 #include "base/bind.h"
 #include "base/files/file_enumerator.h"
@@ -61,7 +60,7 @@
 int64_t OriginInfo::GetDatabaseSize(const base::string16& database_name) const {
   auto it = database_info_.find(database_name);
   if (it != database_info_.end())
-    return it->second.first;
+    return it->second.size;
   return 0;
 }
 
@@ -69,10 +68,18 @@
     const base::string16& database_name) const {
   auto it = database_info_.find(database_name);
   if (it != database_info_.end())
-    return it->second.second;
+    return it->second.description;
   return base::string16();
 }
 
+base::Time OriginInfo::GetDatabaseLastModified(
+    const base::string16& database_name) const {
+  auto it = database_info_.find(database_name);
+  if (it != database_info_.end())
+    return it->second.last_modified;
+  return base::Time();
+}
+
 OriginInfo::OriginInfo(const std::string& origin_identifier, int64_t total_size)
     : origin_identifier_(origin_identifier), total_size_(total_size) {}
 
@@ -418,7 +425,7 @@
     base::Move(database, new_file);
   }
   base::DeleteFile(origin_dir, true);
-  base::DeleteFile(new_origin_dir, true); // might fail on windows.
+  base::DeleteFile(new_origin_dir, true);  // Might fail on windows.
 
   if (is_incognito_) {
     incognito_origin_directories_.erase(origin_identifier);
@@ -590,6 +597,16 @@
       }
       origin_info.SetDatabaseSize(db.database_name, db_file_size);
       origin_info.SetDatabaseDescription(db.database_name, db.description);
+
+      base::FilePath path =
+          GetFullDBFilePath(origin_identifier, db.database_name);
+      base::File::Info file_info;
+      // TODO(jsbell): Avoid duplicate base::GetFileInfo calls between this and
+      // the GetDBFileSize() call above.
+      if (base::GetFileInfo(path, &file_info)) {
+        origin_info.SetDatabaseLastModified(db.database_name,
+                                            file_info.last_modified);
+      }
     }
   }
 
@@ -744,7 +761,7 @@
   return net::OK;
 }
 
-int DatabaseTracker::DeleteDataForOrigin(const std::string& origin,
+int DatabaseTracker::DeleteDataForOrigin(const url::Origin& origin,
                                          net::CompletionOnceCallback callback) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   if (!LazyInit())
@@ -752,16 +769,18 @@
 
   DatabaseSet to_be_deleted;
 
+  const std::string identifier = GetIdentifierFromOrigin(origin);
+
   std::vector<DatabaseDetails> details;
-  if (!databases_table_->
-          GetAllDatabaseDetailsForOriginIdentifier(origin, &details))
+  if (!databases_table_->GetAllDatabaseDetailsForOriginIdentifier(identifier,
+                                                                  &details))
     return net::ERR_FAILED;
   for (const auto& db : details) {
     // Check if the database is opened by any renderer.
-    if (database_connections_.IsDatabaseOpened(origin, db.database_name))
-      to_be_deleted[origin].insert(db.database_name);
+    if (database_connections_.IsDatabaseOpened(identifier, db.database_name))
+      to_be_deleted[identifier].insert(db.database_name);
     else
-      DeleteClosedDatabase(origin, db.database_name);
+      DeleteClosedDatabase(identifier, db.database_name);
   }
 
   if (!to_be_deleted.empty()) {
diff --git a/storage/browser/database/database_tracker.h b/storage/browser/database/database_tracker.h
index 07e0630c..942c9c1 100644
--- a/storage/browser/database/database_tracker.h
+++ b/storage/browser/database/database_tracker.h
@@ -10,7 +10,9 @@
 #include <map>
 #include <memory>
 #include <set>
+#include <string>
 #include <utility>
+#include <vector>
 
 #include "base/component_export.h"
 #include "base/files/file.h"
@@ -24,6 +26,7 @@
 #include "base/time/time.h"
 #include "net/base/completion_once_callback.h"
 #include "storage/common/database/database_connections.h"
+#include "url/origin.h"
 
 namespace content {
 class DatabaseTracker_TestHelper_Test;
@@ -58,17 +61,25 @@
 
   const std::string& GetOriginIdentifier() const { return origin_identifier_; }
   int64_t TotalSize() const { return total_size_; }
+  base::Time LastModified() const { return last_modified_; }
   void GetAllDatabaseNames(std::vector<base::string16>* databases) const;
   int64_t GetDatabaseSize(const base::string16& database_name) const;
   base::string16 GetDatabaseDescription(
       const base::string16& database_name) const;
+  base::Time GetDatabaseLastModified(const base::string16& database_name) const;
 
  protected:
+  struct DBInfo {
+    base::string16 description;
+    int64_t size;
+    base::Time last_modified;
+  };
   OriginInfo(const std::string& origin_identifier, int64_t total_size);
 
   std::string origin_identifier_;
   int64_t total_size_;
-  std::map<base::string16, std::pair<int64_t, base::string16>> database_info_;
+  base::Time last_modified_;
+  std::map<base::string16, DBInfo> database_info_;
 };
 
 // This class manages the main database and keeps track of open databases.
@@ -157,7 +168,7 @@
   // success, net::FAILED if not all databases could be deleted, and
   // net::ERR_IO_PENDING and |callback| is invoked upon completion, if non-null.
   // virtual for unit testing only
-  virtual int DeleteDataForOrigin(const std::string& origin_identifier,
+  virtual int DeleteDataForOrigin(const url::Origin& origin,
                                   net::CompletionOnceCallback callback);
 
   bool IsIncognitoProfile() const { return is_incognito_; }
@@ -185,7 +196,7 @@
  private:
   friend class base::RefCountedThreadSafe<DatabaseTracker>;
   friend class content::DatabaseTracker_TestHelper_Test;
-  friend class content::MockDatabaseTracker; // for testing
+  friend class content::MockDatabaseTracker;  // for testing
 
   using DatabaseSet = std::map<std::string, std::set<base::string16>>;
 
@@ -199,14 +210,20 @@
                          int64_t new_size) {
       int64_t old_size = 0;
       if (database_info_.find(database_name) != database_info_.end())
-        old_size = database_info_[database_name].first;
-      database_info_[database_name].first = new_size;
+        old_size = database_info_[database_name].size;
+      database_info_[database_name].size = new_size;
       if (new_size != old_size)
         total_size_ += new_size - old_size;
     }
     void SetDatabaseDescription(const base::string16& database_name,
                                 const base::string16& description) {
-      database_info_[database_name].second = description;
+      database_info_[database_name].description = description;
+    }
+    void SetDatabaseLastModified(const base::string16& database_name,
+                                 const base::Time& last_modified) {
+      database_info_[database_name].last_modified = last_modified;
+      if (last_modified > last_modified_)
+        last_modified_ = last_modified;
     }
   };
 
diff --git a/storage/browser/fileapi/copy_or_move_file_validator_unittest.cc b/storage/browser/fileapi/copy_or_move_file_validator_unittest.cc
index 2d1cd422..78a17f6a 100644
--- a/storage/browser/fileapi/copy_or_move_file_validator_unittest.cc
+++ b/storage/browser/fileapi/copy_or_move_file_validator_unittest.cc
@@ -76,7 +76,8 @@
     storage::FileSystemBackend* src_file_system_backend =
         file_system_context_->GetFileSystemBackend(src_type_);
     src_file_system_backend->ResolveURL(
-        FileSystemURL::CreateForTest(origin_, src_type_, base::FilePath()),
+        FileSystemURL::CreateForTest(url::Origin::Create(origin_), src_type_,
+                                     base::FilePath()),
         storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
         base::BindOnce(&ExpectOk));
     base::RunLoop().RunUntilIdle();
diff --git a/storage/browser/fileapi/copy_or_move_operation_delegate.cc b/storage/browser/fileapi/copy_or_move_operation_delegate.cc
index 5cb82bf..9e8a664 100644
--- a/storage/browser/fileapi/copy_or_move_operation_delegate.cc
+++ b/storage/browser/fileapi/copy_or_move_operation_delegate.cc
@@ -1030,7 +1030,7 @@
   src_root_.virtual_path().AppendRelativePath(src_url.virtual_path(),
                                               &relative);
   return file_system_context()->CreateCrackedFileSystemURL(
-      dest_root_.origin(), dest_root_.mount_type(), relative);
+      dest_root_.origin().GetURL(), dest_root_.mount_type(), relative);
 }
 
 }  // namespace storage
diff --git a/storage/browser/fileapi/copy_or_move_operation_delegate_unittest.cc b/storage/browser/fileapi/copy_or_move_operation_delegate_unittest.cc
index 8210336..8929c32 100644
--- a/storage/browser/fileapi/copy_or_move_operation_delegate_unittest.cc
+++ b/storage/browser/fileapi/copy_or_move_operation_delegate_unittest.cc
@@ -219,7 +219,8 @@
     storage::FileSystemBackend* backend =
         file_system_context_->GetFileSystemBackend(src_type_);
     backend->ResolveURL(
-        FileSystemURL::CreateForTest(origin_, src_type_, base::FilePath()),
+        FileSystemURL::CreateForTest(url::Origin::Create(origin_), src_type_,
+                                     base::FilePath()),
         storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
         base::BindOnce(&ExpectOk));
     backend = file_system_context_->GetFileSystemBackend(dest_type_);
@@ -235,7 +236,8 @@
             std::move(factory));
     }
     backend->ResolveURL(
-        FileSystemURL::CreateForTest(origin_, dest_type_, base::FilePath()),
+        FileSystemURL::CreateForTest(url::Origin::Create(origin_), dest_type_,
+                                     base::FilePath()),
         storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
         base::BindOnce(&ExpectOk));
     scoped_task_environment_.RunUntilIdle();
@@ -297,8 +299,7 @@
     for (size_t i = 0; i < test_case_size; ++i) {
       const FileSystemTestCaseRecord& test_case = test_cases[i];
       FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
-          root.origin(),
-          root.mount_type(),
+          root.origin().GetURL(), root.mount_type(),
           root.virtual_path().Append(test_case.path));
       if (test_case.is_directory)
         result = CreateDirectory(url);
@@ -331,8 +332,7 @@
       ASSERT_EQ(base::File::FILE_OK, ReadDirectory(dir, &entries));
       for (size_t i = 0; i < entries.size(); ++i) {
         FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
-            dir.origin(),
-            dir.mount_type(),
+            dir.origin().GetURL(), dir.mount_type(),
             dir.virtual_path().Append(entries[i].name));
         base::FilePath relative;
         root.virtual_path().AppendRelativePath(url.virtual_path(), &relative);
diff --git a/storage/browser/fileapi/dragged_file_util_unittest.cc b/storage/browser/fileapi/dragged_file_util_unittest.cc
index 298f59a..c3f54f6fe 100644
--- a/storage/browser/fileapi/dragged_file_util_unittest.cc
+++ b/storage/browser/fileapi/dragged_file_util_unittest.cc
@@ -70,9 +70,7 @@
                           const FileSystemURL& dir,
                           const base::FilePath::StringType& name) {
   return file_system_context->CreateCrackedFileSystemURL(
-      dir.origin(),
-      dir.mount_type(),
-      dir.virtual_path().Append(name));
+      dir.origin().GetURL(), dir.mount_type(), dir.virtual_path().Append(name));
 }
 
 base::FilePath GetRelativeVirtualPath(const FileSystemURL& root,
@@ -91,8 +89,7 @@
                           const FileSystemURL& other_root,
                           const FileSystemURL& url) {
   return file_system_context->CreateCrackedFileSystemURL(
-      other_root.origin(),
-      other_root.mount_type(),
+      other_root.origin().GetURL(), other_root.mount_type(),
       other_root.virtual_path().Append(GetRelativeVirtualPath(root, url)));
 }
 
diff --git a/storage/browser/fileapi/external_mount_points.cc b/storage/browser/fileapi/external_mount_points.cc
index 939d3c7..601a2183 100644
--- a/storage/browser/fileapi/external_mount_points.cc
+++ b/storage/browser/fileapi/external_mount_points.cc
@@ -193,7 +193,7 @@
 }
 
 FileSystemURL ExternalMountPoints::CreateCrackedFileSystemURL(
-    const GURL& origin,
+    const url::Origin& origin,
     FileSystemType type,
     const base::FilePath& path) const {
   return CrackFileSystemURL(FileSystemURL(origin, type, path));
@@ -236,8 +236,7 @@
     const std::string& mount_name,
     const base::FilePath& path) const {
   return CreateCrackedFileSystemURL(
-      origin,
-      storage::kFileSystemTypeExternal,
+      url::Origin::Create(origin), storage::kFileSystemTypeExternal,
       // Avoid using FilePath::Append as path may be an absolute path.
       base::FilePath(CreateVirtualRootPath(mount_name).value() +
                      base::FilePath::kSeparators[0] + path.value()));
diff --git a/storage/browser/fileapi/external_mount_points.h b/storage/browser/fileapi/external_mount_points.h
index c339725c4..a793823f 100644
--- a/storage/browser/fileapi/external_mount_points.h
+++ b/storage/browser/fileapi/external_mount_points.h
@@ -79,7 +79,7 @@
                         FileSystemMountOption* mount_option) const override;
   FileSystemURL CrackURL(const GURL& url) const override;
   FileSystemURL CreateCrackedFileSystemURL(
-      const GURL& origin,
+      const url::Origin& origin,
       FileSystemType type,
       const base::FilePath& path) const override;
 
diff --git a/storage/browser/fileapi/external_mount_points_unittest.cc b/storage/browser/fileapi/external_mount_points_unittest.cc
index 100c1ed..7c768b1 100644
--- a/storage/browser/fileapi/external_mount_points_unittest.cc
+++ b/storage/browser/fileapi/external_mount_points_unittest.cc
@@ -270,7 +270,8 @@
   scoped_refptr<storage::ExternalMountPoints> mount_points(
       storage::ExternalMountPoints::CreateRefCounted());
 
-  const GURL kTestOrigin("http://chromium.org");
+  const url::Origin kTestOrigin =
+      url::Origin::Create(GURL("http://chromium.org"));
 
   mount_points->RegisterFileSystem("c",
                                    storage::kFileSystemTypeNativeLocal,
diff --git a/storage/browser/fileapi/file_system_context.cc b/storage/browser/fileapi/file_system_context.cc
index e9fde96..75bafcc6 100644
--- a/storage/browser/fileapi/file_system_context.cc
+++ b/storage/browser/fileapi/file_system_context.cc
@@ -472,7 +472,8 @@
     const GURL& origin,
     FileSystemType type,
     const base::FilePath& path) const {
-  return CrackFileSystemURL(FileSystemURL(origin, type, path));
+  return CrackFileSystemURL(
+      FileSystemURL(url::Origin::Create(origin), type, path));
 }
 
 #if defined(OS_CHROMEOS)
diff --git a/storage/browser/fileapi/file_system_context_unittest.cc b/storage/browser/fileapi/file_system_context_unittest.cc
index 555c8916..8cf9e34 100644
--- a/storage/browser/fileapi/file_system_context_unittest.cc
+++ b/storage/browser/fileapi/file_system_context_unittest.cc
@@ -87,7 +87,7 @@
                                   const std::string& expect_filesystem_id) {
     EXPECT_TRUE(url.is_valid());
 
-    EXPECT_EQ(expect_origin, url.origin());
+    EXPECT_EQ(expect_origin, url.origin().GetURL());
     EXPECT_EQ(expect_mount_type, url.mount_type());
     EXPECT_EQ(expect_type, url.type());
     EXPECT_EQ(expect_path, url.path());
diff --git a/storage/browser/fileapi/file_system_dir_url_request_job.cc b/storage/browser/fileapi/file_system_dir_url_request_job.cc
index 7acfdaa..a079a31 100644
--- a/storage/browser/fileapi/file_system_dir_url_request_job.cc
+++ b/storage/browser/fileapi/file_system_dir_url_request_job.cc
@@ -155,7 +155,7 @@
 void FileSystemDirURLRequestJob::GetMetadata(size_t index) {
   const filesystem::mojom::DirectoryEntry& entry = entries_[index];
   const FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
-      url_.origin(), url_.type(),
+      url_.origin().GetURL(), url_.type(),
       url_.path().Append(base::FilePath(entry.name)));
   DCHECK(url.is_valid());
   file_system_context_->operation_runner()->GetMetadata(
diff --git a/storage/browser/fileapi/file_system_operation_impl.cc b/storage/browser/fileapi/file_system_operation_impl.cc
index b9a92bec..db2527c 100644
--- a/storage/browser/fileapi/file_system_operation_impl.cc
+++ b/storage/browser/fileapi/file_system_operation_impl.cc
@@ -433,7 +433,7 @@
   DCHECK(quota_manager_proxy);
   DCHECK(quota_manager_proxy->quota_manager());
   quota_manager_proxy->quota_manager()->GetUsageAndQuota(
-      url::Origin::Create(url.origin()),
+      url::Origin::Create(url.origin().GetURL()),
       FileSystemTypeToQuotaStorageType(url.type()),
       base::BindOnce(&FileSystemOperationImpl::DidGetUsageAndQuotaAndRunTask,
                      weak_ptr_, std::move(task), std::move(error_callback)));
diff --git a/storage/browser/fileapi/file_system_url.cc b/storage/browser/fileapi/file_system_url.cc
index 184c475..b90cf29 100644
--- a/storage/browser/fileapi/file_system_url.cc
+++ b/storage/browser/fileapi/file_system_url.cc
@@ -18,11 +18,11 @@
 }  // namespace
 
 FileSystemURL::FileSystemURL()
-    : is_valid_(false),
+    : is_null_(true),
+      is_valid_(false),
       mount_type_(kFileSystemTypeUnknown),
       type_(kFileSystemTypeUnknown),
-      mount_option_(FlushPolicy::NO_FLUSH_ON_COMPLETION) {
-}
+      mount_option_(FlushPolicy::NO_FLUSH_ON_COMPLETION) {}
 
 FileSystemURL::FileSystemURL(const FileSystemURL& other) = default;
 
@@ -37,14 +37,14 @@
   return FileSystemURL(url);
 }
 
-FileSystemURL FileSystemURL::CreateForTest(const GURL& origin,
+FileSystemURL FileSystemURL::CreateForTest(const url::Origin& origin,
                                            FileSystemType mount_type,
                                            const base::FilePath& virtual_path) {
   return FileSystemURL(origin, mount_type, virtual_path);
 }
 
 FileSystemURL FileSystemURL::CreateForTest(
-    const GURL& origin,
+    const url::Origin& origin,
     FileSystemType mount_type,
     const base::FilePath& virtual_path,
     const std::string& mount_filesystem_id,
@@ -63,28 +63,31 @@
 }
 
 FileSystemURL::FileSystemURL(const GURL& url)
-    : mount_type_(kFileSystemTypeUnknown),
+    : is_null_(false),
+      mount_type_(kFileSystemTypeUnknown),
       type_(kFileSystemTypeUnknown),
       mount_option_(FlushPolicy::NO_FLUSH_ON_COMPLETION) {
-  is_valid_ = ParseFileSystemSchemeURL(url, &origin_, &mount_type_,
-                                       &virtual_path_);
+  GURL origin_url;
+  is_valid_ =
+      ParseFileSystemSchemeURL(url, &origin_url, &mount_type_, &virtual_path_);
+  origin_ = url::Origin::Create(origin_url);
   path_ = virtual_path_;
   type_ = mount_type_;
 }
 
-FileSystemURL::FileSystemURL(const GURL& origin,
+FileSystemURL::FileSystemURL(const url::Origin& origin,
                              FileSystemType mount_type,
                              const base::FilePath& virtual_path)
-    : is_valid_(true),
+    : is_null_(false),
+      is_valid_(true),
       origin_(origin),
       mount_type_(mount_type),
       virtual_path_(virtual_path.NormalizePathSeparators()),
       type_(mount_type),
       path_(virtual_path.NormalizePathSeparators()),
-      mount_option_(FlushPolicy::NO_FLUSH_ON_COMPLETION) {
-}
+      mount_option_(FlushPolicy::NO_FLUSH_ON_COMPLETION) {}
 
-FileSystemURL::FileSystemURL(const GURL& origin,
+FileSystemURL::FileSystemURL(const url::Origin& origin,
                              FileSystemType mount_type,
                              const base::FilePath& virtual_path,
                              const std::string& mount_filesystem_id,
@@ -92,7 +95,8 @@
                              const base::FilePath& cracked_path,
                              const std::string& filesystem_id,
                              const FileSystemMountOption& mount_option)
-    : is_valid_(true),
+    : is_null_(false),
+      is_valid_(true),
       origin_(origin),
       mount_type_(mount_type),
       virtual_path_(virtual_path.NormalizePathSeparators()),
@@ -100,8 +104,7 @@
       type_(cracked_type),
       path_(cracked_path.NormalizePathSeparators()),
       filesystem_id_(filesystem_id),
-      mount_option_(mount_option) {
-}
+      mount_option_(mount_option) {}
 
 FileSystemURL::~FileSystemURL() = default;
 
@@ -109,7 +112,7 @@
   if (!is_valid_)
     return GURL();
 
-  std::string url = GetFileSystemRootURI(origin_, mount_type_).spec();
+  std::string url = GetFileSystemRootURI(origin_.GetURL(), mount_type_).spec();
   if (url.empty())
     return GURL();
 
@@ -130,7 +133,7 @@
   if (!is_valid_)
     return "invalid filesystem: URL";
   std::ostringstream ss;
-  ss << GetFileSystemRootURI(origin_, mount_type_);
+  ss << GetFileSystemRootURI(origin_.GetURL(), mount_type_);
 
   // filesystem_id_ will be non empty for (and only for) cracked URLs.
   if (!filesystem_id_.empty()) {
@@ -157,11 +160,13 @@
 }
 
 bool FileSystemURL::operator==(const FileSystemURL& that) const {
-  return origin_ == that.origin_ &&
-      type_ == that.type_ &&
-      path_ == that.path_ &&
-      filesystem_id_ == that.filesystem_id_ &&
-      is_valid_ == that.is_valid_;
+  if (is_null_ && that.is_null_) {
+    return true;
+  } else {
+    return origin_ == that.origin_ && type_ == that.type_ &&
+           path_ == that.path_ && filesystem_id_ == that.filesystem_id_ &&
+           is_valid_ == that.is_valid_;
+  }
 }
 
 bool FileSystemURL::Comparator::operator()(const FileSystemURL& lhs,
diff --git a/storage/browser/fileapi/file_system_url.h b/storage/browser/fileapi/file_system_url.h
index 8f1111c..8220812 100644
--- a/storage/browser/fileapi/file_system_url.h
+++ b/storage/browser/fileapi/file_system_url.h
@@ -13,6 +13,7 @@
 #include "storage/common/fileapi/file_system_mount_option.h"
 #include "storage/common/fileapi/file_system_types.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace storage {
 
@@ -93,10 +94,10 @@
   // Methods for creating FileSystemURL without attempting to crack them.
   // Should be used only in tests.
   static FileSystemURL CreateForTest(const GURL& url);
-  static FileSystemURL CreateForTest(const GURL& origin,
+  static FileSystemURL CreateForTest(const url::Origin& origin,
                                      FileSystemType mount_type,
                                      const base::FilePath& virtual_path);
-  static FileSystemURL CreateForTest(const GURL& origin,
+  static FileSystemURL CreateForTest(const url::Origin& origin,
                                      FileSystemType mount_type,
                                      const base::FilePath& virtual_path,
                                      const std::string& mount_filesystem_id,
@@ -109,7 +110,7 @@
   bool is_valid() const { return is_valid_; }
 
   // Returns the origin part of this URL. See the class comment for details.
-  const GURL& origin() const { return origin_; }
+  const url::Origin& origin() const { return origin_; }
 
   // Returns the type part of this URL. See the class comment for details.
   FileSystemType type() const { return type_; }
@@ -159,11 +160,11 @@
   friend class IsolatedContext;
 
   explicit FileSystemURL(const GURL& filesystem_url);
-  FileSystemURL(const GURL& origin,
+  FileSystemURL(const url::Origin& origin,
                 FileSystemType mount_type,
                 const base::FilePath& virtual_path);
   // Creates a cracked FileSystemURL.
-  FileSystemURL(const GURL& origin,
+  FileSystemURL(const url::Origin& origin,
                 FileSystemType mount_type,
                 const base::FilePath& virtual_path,
                 const std::string& mount_filesystem_id,
@@ -172,10 +173,13 @@
                 const std::string& filesystem_id,
                 const FileSystemMountOption& mount_option);
 
+  // Used to determine if a FileSystemURL was default constructed.
+  bool is_null_ = false;
+
   bool is_valid_;
 
   // Values parsed from the original URL.
-  GURL origin_;
+  url::Origin origin_;
   FileSystemType mount_type_;
   base::FilePath virtual_path_;
 
diff --git a/storage/browser/fileapi/file_system_url_unittest.cc b/storage/browser/fileapi/file_system_url_unittest.cc
index f016f4d1..dca590c 100644
--- a/storage/browser/fileapi/file_system_url_unittest.cc
+++ b/storage/browser/fileapi/file_system_url_unittest.cc
@@ -52,7 +52,7 @@
   FileSystemURL url = CreateFileSystemURL(
      "filesystem:http://chromium.org/persistent/directory/file");
   ASSERT_TRUE(url.is_valid());
-  EXPECT_EQ("http://chromium.org/", url.origin().spec());
+  EXPECT_EQ("http://chromium.org/", url.origin().GetURL().spec());
   EXPECT_EQ(kFileSystemTypePersistent, url.type());
   EXPECT_EQ(FPL("file"), VirtualPath::BaseName(url.path()).value());
   EXPECT_EQ(FPL("directory"), url.path().DirName().value());
@@ -62,7 +62,7 @@
   FileSystemURL url = CreateFileSystemURL(
       "filesystem:http://chromium.org/temporary/directory/file");
   ASSERT_TRUE(url.is_valid());
-  EXPECT_EQ("http://chromium.org/", url.origin().spec());
+  EXPECT_EQ("http://chromium.org/", url.origin().GetURL().spec());
   EXPECT_EQ(kFileSystemTypeTemporary, url.type());
   EXPECT_EQ(FPL("file"), VirtualPath::BaseName(url.path()).value());
   EXPECT_EQ(FPL("directory"), url.path().DirName().value());
@@ -72,7 +72,7 @@
   FileSystemURL url = CreateFileSystemURL(
       "filesystem:http://chromium.org/temporary/////directory/file");
   ASSERT_TRUE(url.is_valid());
-  EXPECT_EQ("http://chromium.org/", url.origin().spec());
+  EXPECT_EQ("http://chromium.org/", url.origin().GetURL().spec());
   EXPECT_EQ(kFileSystemTypeTemporary, url.type());
   EXPECT_EQ(FPL("file"), VirtualPath::BaseName(url.path()).value());
   EXPECT_EQ(FPL("directory"), url.path().DirName().value());
@@ -191,7 +191,7 @@
   const base::FilePath kPath(FPL("dir/file"));
 
   const FileSystemURL kURL1 = FileSystemURL::CreateForTest(
-      kOrigin, kFileSystemTypeTemporary, kPath);
+      url::Origin::Create(kOrigin), kFileSystemTypeTemporary, kPath);
   EXPECT_EQ("filesystem:http://example.com/temporary/" +
             NormalizedUTF8Path(kPath),
             kURL1.DebugString());
@@ -199,19 +199,19 @@
 
 TEST(FileSystemURLTest, IsInSameFileSystem) {
   FileSystemURL url_foo_temp_a = FileSystemURL::CreateForTest(
-      GURL("http://foo"), kFileSystemTypeTemporary,
+      url::Origin::Create(GURL("http://foo")), kFileSystemTypeTemporary,
       base::FilePath::FromUTF8Unsafe("a"));
   FileSystemURL url_foo_temp_b = FileSystemURL::CreateForTest(
-      GURL("http://foo"), kFileSystemTypeTemporary,
+      url::Origin::Create(GURL("http://foo")), kFileSystemTypeTemporary,
       base::FilePath::FromUTF8Unsafe("b"));
   FileSystemURL url_foo_perm_a = FileSystemURL::CreateForTest(
-      GURL("http://foo"), kFileSystemTypePersistent,
+      url::Origin::Create(GURL("http://foo")), kFileSystemTypePersistent,
       base::FilePath::FromUTF8Unsafe("a"));
   FileSystemURL url_bar_temp_a = FileSystemURL::CreateForTest(
-      GURL("http://bar"), kFileSystemTypeTemporary,
+      url::Origin::Create(GURL("http://bar")), kFileSystemTypeTemporary,
       base::FilePath::FromUTF8Unsafe("a"));
   FileSystemURL url_bar_perm_a = FileSystemURL::CreateForTest(
-      GURL("http://bar"), kFileSystemTypePersistent,
+      url::Origin::Create(GURL("http://bar")), kFileSystemTypePersistent,
       base::FilePath::FromUTF8Unsafe("a"));
 
   EXPECT_TRUE(url_foo_temp_a.IsInSameFileSystem(url_foo_temp_a));
@@ -225,7 +225,7 @@
   // Move constructor.
   {
     FileSystemURL original = FileSystemURL::CreateForTest(
-        GURL("http://foo"), kFileSystemTypeTemporary,
+        url::Origin::Create(GURL("http://foo")), kFileSystemTypeTemporary,
         base::FilePath::FromUTF8Unsafe("a"));
     EXPECT_TRUE(original.is_valid());
     FileSystemURL new_url(std::move(original));
@@ -236,7 +236,7 @@
   // Move operator.
   {
     FileSystemURL original = FileSystemURL::CreateForTest(
-        GURL("http://foo"), kFileSystemTypeTemporary,
+        url::Origin::Create(GURL("http://foo")), kFileSystemTypeTemporary,
         base::FilePath::FromUTF8Unsafe("a"));
     EXPECT_TRUE(original.is_valid());
     FileSystemURL new_url;
diff --git a/storage/browser/fileapi/isolated_context.cc b/storage/browser/fileapi/isolated_context.cc
index 763711f7..6d930b8 100644
--- a/storage/browser/fileapi/isolated_context.cc
+++ b/storage/browser/fileapi/isolated_context.cc
@@ -353,7 +353,7 @@
 }
 
 FileSystemURL IsolatedContext::CreateCrackedFileSystemURL(
-    const GURL& origin,
+    const url::Origin& origin,
     FileSystemType type,
     const base::FilePath& path) const {
   return CrackFileSystemURL(FileSystemURL(origin, type, path));
diff --git a/storage/browser/fileapi/isolated_context.h b/storage/browser/fileapi/isolated_context.h
index f5477a16..5ad38f6a7 100644
--- a/storage/browser/fileapi/isolated_context.h
+++ b/storage/browser/fileapi/isolated_context.h
@@ -153,7 +153,7 @@
                         FileSystemMountOption* mount_option) const override;
   FileSystemURL CrackURL(const GURL& url) const override;
   FileSystemURL CreateCrackedFileSystemURL(
-      const GURL& origin,
+      const url::Origin& origin,
       FileSystemType type,
       const base::FilePath& path) const override;
 
diff --git a/storage/browser/fileapi/isolated_context_unittest.cc b/storage/browser/fileapi/isolated_context_unittest.cc
index f453f17..b3c7354 100644
--- a/storage/browser/fileapi/isolated_context_unittest.cc
+++ b/storage/browser/fileapi/isolated_context_unittest.cc
@@ -255,13 +255,14 @@
               names_[i]).Append(relatives[j].path);
 
       FileSystemURL cracked = isolated_context()->CreateCrackedFileSystemURL(
-          GURL("http://chromium.org"), kFileSystemTypeIsolated, virtual_path);
+          url::Origin::Create(GURL("http://chromium.org")),
+          kFileSystemTypeIsolated, virtual_path);
 
       ASSERT_EQ(relatives[j].valid, cracked.is_valid());
 
       if (!relatives[j].valid)
         continue;
-      ASSERT_EQ(GURL("http://chromium.org"), cracked.origin());
+      ASSERT_EQ("http://chromium.org", cracked.origin().Serialize());
       ASSERT_EQ(kTestPaths[i].Append(relatives[j].path)
                     .NormalizePathSeparators().value(),
                 cracked.path().value());
diff --git a/storage/browser/fileapi/mount_points.h b/storage/browser/fileapi/mount_points.h
index 8ca7ef6..7a36e5b 100644
--- a/storage/browser/fileapi/mount_points.h
+++ b/storage/browser/fileapi/mount_points.h
@@ -14,6 +14,9 @@
 #include "storage/common/fileapi/file_system_util.h"
 
 class GURL;
+namespace url {
+class Origin;
+}
 
 namespace storage {
 class FileSystemMountOption;
@@ -64,7 +67,7 @@
   // If the the URL is not valid or does not belong to any of the mount points
   // registered in this context, returns empty, invalid FileSystemURL.
   virtual FileSystemURL CreateCrackedFileSystemURL(
-      const GURL& origin,
+      const url::Origin& origin,
       storage::FileSystemType type,
       const base::FilePath& path) const = 0;
 
diff --git a/storage/browser/fileapi/obfuscated_file_util.cc b/storage/browser/fileapi/obfuscated_file_util.cc
index c4c66252..300432e 100644
--- a/storage/browser/fileapi/obfuscated_file_util.cc
+++ b/storage/browser/fileapi/obfuscated_file_util.cc
@@ -25,7 +25,6 @@
 #include "storage/browser/fileapi/file_observers.h"
 #include "storage/browser/fileapi/file_system_context.h"
 #include "storage/browser/fileapi/file_system_operation_context.h"
-#include "storage/browser/fileapi/native_file_util.h"
 #include "storage/browser/fileapi/sandbox_file_system_backend.h"
 #include "storage/browser/fileapi/sandbox_isolated_origin_database.h"
 #include "storage/browser/fileapi/sandbox_origin_database.h"
@@ -211,9 +210,8 @@
     : public ObfuscatedFileUtil::AbstractOriginEnumerator {
  public:
   using OriginRecord = SandboxOriginDatabase::OriginRecord;
-  ObfuscatedOriginEnumerator(
-      SandboxOriginDatabaseInterface* origin_database,
-      const base::FilePath& base_file_path)
+  ObfuscatedOriginEnumerator(SandboxOriginDatabaseInterface* origin_database,
+                             const base::FilePath& base_file_path)
       : base_file_path_(base_file_path) {
     if (origin_database)
       origin_database->ListAllOrigins(&origins_);
@@ -242,6 +240,8 @@
     }
     base::FilePath path =
         base_file_path_.Append(current_.path).AppendASCII(type_string);
+    // TODO(https://crbug.com/93417): Ensure that there are no paths on disk in
+    // incognito.
     return base::DirectoryExists(path);
   }
 
@@ -265,7 +265,10 @@
       db_flush_delay_seconds_(10 * 60),  // 10 mins.
       get_type_string_for_url_(std::move(get_type_string_for_url)),
       known_type_strings_(known_type_strings),
-      sandbox_delegate_(sandbox_delegate) {
+      sandbox_delegate_(sandbox_delegate),
+      delegate_(std::make_unique<ObfuscatedFileUtilDiskDelegate>()) {
+  // TODO(https://crbug.com/93417): |delegate_| to be initialized with an
+  // instance of |ObfuscatedFileUtilMemoryDelegate| if |is_incognito| is true.
   DCHECK(!get_type_string_for_url_.is_null());
   DETACH_FROM_SEQUENCE(sequence_checker_);
   DCHECK(!is_incognito ||
@@ -287,7 +290,8 @@
   if (file.IsValid() && file_flags & base::File::FLAG_WRITE &&
       context->quota_limit_type() == storage::kQuotaLimitTypeUnlimited &&
       sandbox_delegate_) {
-    sandbox_delegate_->StickyInvalidateUsageCache(url.origin(), url.type());
+    sandbox_delegate_->StickyInvalidateUsageCache(url.origin().GetURL(),
+                                                  url.type());
   }
   return file;
 }
@@ -465,9 +469,8 @@
       return base::File::FILE_ERROR_FAILED;
     return base::File::FILE_OK;
   }
-  return NativeFileUtil::Touch(
-      DataPathToLocalPath(url, file_info.data_path),
-      last_access_time, last_modified_time);
+  return delegate_->Touch(DataPathToLocalPath(url, file_info.data_path),
+                          last_access_time, last_modified_time);
 }
 
 base::File::Error ObfuscatedFileUtil::Truncate(
@@ -485,7 +488,7 @@
   int64_t growth = length - file_info.size;
   if (!AllocateQuota(context, growth))
     return base::File::FILE_ERROR_NO_SPACE;
-  error = NativeFileUtil::Truncate(local_path, length);
+  error = delegate_->Truncate(local_path, length);
   if (error == base::File::FILE_OK) {
     UpdateUsage(context, url, growth);
     context->change_observers()->Notify(&FileChangeObserver::OnModifyFile, url);
@@ -583,20 +586,16 @@
   error = base::File::FILE_ERROR_FAILED;
   if (copy) {
     if (overwrite) {
-      error = NativeFileUtil::CopyOrMoveFile(
-          src_local_path,
-          dest_local_path,
-          option,
-          storage::NativeFileUtil::CopyOrMoveModeForDestination(
-              dest_url, true /* copy */));
+      error = delegate_->CopyOrMoveFile(
+          src_local_path, dest_local_path, option,
+          delegate_->CopyOrMoveModeForDestination(dest_url, true /* copy */));
     } else {  // non-overwrite
       error = CreateFile(context, src_local_path, dest_url, &dest_file_info);
     }
   } else {
     if (overwrite) {
       if (db->OverwritingMoveFile(src_file_id, dest_file_id)) {
-        if (base::File::FILE_OK !=
-            NativeFileUtil::DeleteFile(dest_local_path))
+        if (base::File::FILE_OK != delegate_->DeleteFile(dest_local_path))
           LOG(WARNING) << "Leaked a backing file.";
         error = base::File::FILE_OK;
       } else {
@@ -643,7 +642,8 @@
     return base::File::FILE_ERROR_FAILED;
 
   base::File::Info src_platform_file_info;
-  if (!base::GetFileInfo(src_file_path, &src_platform_file_info))
+  if (delegate_->GetFileInfo(src_file_path, &src_platform_file_info) !=
+      base::File::FILE_OK)
     return base::File::FILE_ERROR_NOT_FOUND;
 
   FileId dest_file_id;
@@ -688,12 +688,9 @@
   if (overwrite) {
     base::FilePath dest_local_path =
         DataPathToLocalPath(dest_url, dest_file_info.data_path);
-    error = NativeFileUtil::CopyOrMoveFile(
-        src_file_path,
-        dest_local_path,
-        FileSystemOperation::OPTION_NONE,
-        storage::NativeFileUtil::CopyOrMoveModeForDestination(dest_url,
-                                                              true /* copy */));
+    error = delegate_->CopyOrMoveFile(
+        src_file_path, dest_local_path, FileSystemOperation::OPTION_NONE,
+        delegate_->CopyOrMoveModeForDestination(dest_url, true /* copy */));
   } else {
     error = CreateFile(context, src_file_path, dest_url, &dest_file_info);
   }
@@ -752,7 +749,7 @@
   if (error == base::File::FILE_ERROR_NOT_FOUND)
     return base::File::FILE_OK;
 
-  error = NativeFileUtil::DeleteFile(local_path);
+  error = delegate_->DeleteFile(local_path);
   if (base::File::FILE_OK != error)
     LOG(WARNING) << "Leaked a backing file.";
   return base::File::FILE_OK;
@@ -800,6 +797,8 @@
     *file_info = base::File::Info();
     *error = base::File::FILE_ERROR_NOT_A_FILE;
   }
+  // An empty ScopedFile does not have any on-disk operation, therefore it can
+  // be handled the same way by on-disk and in-memory implementations.
   return storage::ScopedFile();
 }
 
@@ -854,8 +853,10 @@
     return origin_dir;
   base::FilePath path = origin_dir.AppendASCII(type_string);
   base::File::Error error = base::File::FILE_OK;
-  if (!base::DirectoryExists(path) &&
-      (!create || !base::CreateDirectory(path))) {
+  if (!delegate_->DirectoryExists(path) &&
+      (!create || delegate_->CreateDirectory(path, false /* exclusive */,
+                                             true /* recursive */) !=
+                      base::File::FILE_OK)) {
     error = create ?
           base::File::FILE_ERROR_FAILED :
           base::File::FILE_ERROR_NOT_FOUND;
@@ -884,9 +885,9 @@
         GetDirectoryForOriginAndType(origin, type_string, false, &error);
     if (error == base::File::FILE_ERROR_FAILED)
       return false;
-    if (error == base::File::FILE_OK &&
-        !origin_type_path.empty() &&
-        !base::DeleteFile(origin_type_path, true /* recursive */)) {
+    if (error == base::File::FILE_OK && !origin_type_path.empty() &&
+        !delegate_->DeleteFileOrDirectory(origin_type_path,
+                                          true /* recursive */)) {
       return false;
     }
 
@@ -896,7 +897,7 @@
     for (const std::string& type : known_type_strings_) {
       if (type == type_string)
         continue;
-      if (base::DirectoryExists(origin_path.AppendASCII(type))) {
+      if (delegate_->DirectoryExists(origin_path.AppendASCII(type))) {
         // Other type's directory exists; just return true here.
         return true;
       }
@@ -909,7 +910,7 @@
     origin_database_->RemovePathForOrigin(
         storage::GetIdentifierFromOrigin(origin));
   }
-  return base::DeleteFile(origin_path, true /* recursive */);
+  return delegate_->DeleteFileOrDirectory(origin_path, true /* recursive */);
 }
 
 void ObfuscatedFileUtil::CloseFileSystemForOriginAndType(
@@ -928,15 +929,15 @@
   }
 }
 
-ObfuscatedFileUtil::AbstractOriginEnumerator*
+std::unique_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator>
 ObfuscatedFileUtil::CreateOriginEnumerator() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
   std::vector<SandboxOriginDatabase::OriginRecord> origins;
 
   InitOriginDatabase(GURL(), false);
-  return new ObfuscatedOriginEnumerator(
-      origin_database_.get(), file_system_directory_);
+  return std::make_unique<ObfuscatedOriginEnumerator>(origin_database_.get(),
+                                                      file_system_directory_);
 }
 
 void ObfuscatedFileUtil::DestroyDirectoryDatabase(
@@ -1009,7 +1010,7 @@
     base::File::Error* error_code) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   return GetDirectoryForOriginAndType(
-      url.origin(), CallGetTypeStringForURL(url), create, error_code);
+      url.origin().GetURL(), CallGetTypeStringForURL(url), create, error_code);
 }
 
 std::string ObfuscatedFileUtil::CallGetTypeStringForURL(
@@ -1049,10 +1050,9 @@
   if (local_info->data_path.empty())
     return base::File::FILE_ERROR_INVALID_OPERATION;
   base::FilePath local_path = DataPathToLocalPath(url, local_info->data_path);
-  base::File::Error error = NativeFileUtil::GetFileInfo(
-      local_path, file_info);
+  base::File::Error error = delegate_->GetFileInfo(local_path, file_info);
   // We should not follow symbolic links in sandboxed file system.
-  if (base::IsLink(local_path)) {
+  if (delegate_->IsLink(local_path)) {
     LOG(WARNING) << "Found a symbolic file.";
     error = base::File::FILE_ERROR_NOT_FOUND;
   }
@@ -1080,27 +1080,28 @@
   if (error != base::File::FILE_OK)
     return base::File(error);
 
-  if (base::PathExists(dest_local_path)) {
-    if (!base::DeleteFile(dest_local_path, false /* recursive */))
+  if (delegate_->PathExists(dest_local_path)) {
+    if (!delegate_->DeleteFileOrDirectory(dest_local_path,
+                                          false /* recursive */))
       return base::File(base::File::FILE_ERROR_FAILED);
     LOG(WARNING) << "A stray file detected";
     InvalidateUsageCache(context, dest_url.origin(), dest_url.type());
   }
 
-  base::File file = NativeFileUtil::CreateOrOpen(dest_local_path, file_flags);
+  base::File file = delegate_->CreateOrOpen(dest_local_path, file_flags);
   if (!file.IsValid())
     return file;
 
   if (!file.created()) {
     file.Close();
-    base::DeleteFile(dest_local_path, false /* recursive */);
+    delegate_->DeleteFile(dest_local_path);
     return base::File(base::File::FILE_ERROR_FAILED);
   }
 
   error = CommitCreateFile(root, dest_local_path, db, dest_file_info);
   if (error != base::File::FILE_OK) {
     file.Close();
-    base::DeleteFile(dest_local_path, false /* recursive */);
+    delegate_->DeleteFile(dest_local_path);
     return base::File(error);
   }
 
@@ -1123,23 +1124,22 @@
 
   bool created = false;
   if (src_file_path.empty()) {
-    if (base::PathExists(dest_local_path)) {
-      if (!base::DeleteFile(dest_local_path, false /* recursive */))
+    if (delegate_->PathExists(dest_local_path)) {
+      if (!delegate_->DeleteFileOrDirectory(dest_local_path,
+                                            false /* recursive */))
         return base::File::FILE_ERROR_FAILED;
       LOG(WARNING) << "A stray file detected";
       InvalidateUsageCache(context, dest_url.origin(), dest_url.type());
     }
 
-    error = NativeFileUtil::EnsureFileExists(dest_local_path, &created);
+    error = delegate_->EnsureFileExists(dest_local_path, &created);
   } else {
-    error = NativeFileUtil::CopyOrMoveFile(
-        src_file_path,
-        dest_local_path,
-        FileSystemOperation::OPTION_NONE,
-        storage::NativeFileUtil::CopyOrMoveModeForDestination(dest_url,
-                                                              true /* copy */));
+    error = delegate_->CopyOrMoveFile(
+        src_file_path, dest_local_path, FileSystemOperation::OPTION_NONE,
+        delegate_->CopyOrMoveModeForDestination(dest_url, true /* copy */));
     created = true;
   }
+
   if (error != base::File::FILE_OK)
     return error;
   if (!created)
@@ -1193,8 +1193,8 @@
     const FileSystemURL& url, bool create) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  std::string key = GetDirectoryDatabaseKey(
-      url.origin(), CallGetTypeStringForURL(url));
+  std::string key = GetDirectoryDatabaseKey(url.origin().GetURL(),
+                                            CallGetTypeStringForURL(url));
   if (key.empty())
     return nullptr;
 
@@ -1244,9 +1244,9 @@
   }
 
   base::FilePath path = file_system_directory_.Append(directory_name);
-  bool exists_in_fs = base::DirectoryExists(path);
+  bool exists_in_fs = delegate_->DirectoryExists(path);
   if (!exists_in_db && exists_in_fs) {
-    if (!base::DeleteFile(path, true)) {
+    if (!delegate_->DeleteFileOrDirectory(path, true)) {
       if (error_code)
         *error_code = base::File::FILE_ERROR_FAILED;
       return base::FilePath();
@@ -1255,7 +1255,9 @@
   }
 
   if (!exists_in_fs) {
-    if (!create || !base::CreateDirectory(path)) {
+    if (!create || delegate_->CreateDirectory(path, false /* exclusive */,
+                                              true /* recursive */) !=
+                       base::File::FILE_OK) {
       if (error_code)
         *error_code = create ?
             base::File::FILE_ERROR_FAILED :
@@ -1272,10 +1274,10 @@
 
 void ObfuscatedFileUtil::InvalidateUsageCache(
     FileSystemOperationContext* context,
-    const GURL& origin,
+    const url::Origin& origin,
     FileSystemType type) {
   if (sandbox_delegate_)
-    sandbox_delegate_->InvalidateUsageCache(origin, type);
+    sandbox_delegate_->InvalidateUsageCache(origin.GetURL(), type);
 }
 
 void ObfuscatedFileUtil::MarkUsed() {
@@ -1311,12 +1313,16 @@
   if (origin_database_)
     return true;
 
-  if (!create && !base::DirectoryExists(file_system_directory_))
-    return false;
-  if (!base::CreateDirectory(file_system_directory_)) {
-    LOG(WARNING) << "Failed to create FileSystem directory: " <<
-        file_system_directory_.value();
-    return false;
+  if (!delegate_->DirectoryExists(file_system_directory_)) {
+    if (!create)
+      return false;
+    if (delegate_->CreateDirectory(
+            file_system_directory_, false /* exclusive */,
+            true /* recursive */) != base::File::FILE_OK) {
+      LOG(WARNING) << "Failed to create FileSystem directory: "
+                   << file_system_directory_.value();
+      return false;
+    }
   }
 
   SandboxPrioritizedOriginDatabase* prioritized_origin_database =
@@ -1358,8 +1364,8 @@
   base::FilePath new_local_path = root->AppendASCII(
       base::StringPrintf("%02" PRId64, directory_number));
 
-  error = NativeFileUtil::CreateDirectory(
-      new_local_path, false /* exclusive */, false /* recursive */);
+  error = delegate_->CreateDirectory(new_local_path, false /* exclusive */,
+                                     false /* recursive */);
   if (error != base::File::FILE_OK)
     return error;
 
@@ -1425,7 +1431,7 @@
     AllocateQuota(context, delta);
   }
 
-  base::File file = NativeFileUtil::CreateOrOpen(local_path, file_flags);
+  base::File file = delegate_->CreateOrOpen(local_path, file_flags);
   if (!file.IsValid()) {
     error = file.error_details();
     if (error == base::File::FILE_ERROR_NOT_FOUND) {
diff --git a/storage/browser/fileapi/obfuscated_file_util.h b/storage/browser/fileapi/obfuscated_file_util.h
index 38b8b2a..1c9b3ae 100644
--- a/storage/browser/fileapi/obfuscated_file_util.h
+++ b/storage/browser/fileapi/obfuscated_file_util.h
@@ -23,6 +23,9 @@
 #include "storage/browser/blob/shareable_file_reference.h"
 #include "storage/browser/fileapi/file_system_file_util.h"
 #include "storage/browser/fileapi/file_system_url.h"
+// TODO(https://crbug.com/93417): To be replaced with
+// obfuscated_file_util_delegate.h
+#include "storage/browser/fileapi/obfuscated_file_util_disk_delegate.h"
 #include "storage/browser/fileapi/sandbox_directory_database.h"
 #include "storage/browser/fileapi/sandbox_file_system_backend_delegate.h"
 #include "storage/common/fileapi/file_system_types.h"
@@ -188,7 +191,7 @@
   // This method and all methods of its returned class must be called only on
   // the FILE thread.  The caller is responsible for deleting the returned
   // object.
-  AbstractOriginEnumerator* CreateOriginEnumerator();
+  std::unique_ptr<AbstractOriginEnumerator> CreateOriginEnumerator();
 
   // Deletes a directory database from the database list in the ObfuscatedFSFU
   // and destroys the database on the disk.
@@ -296,7 +299,7 @@
                                        base::File::Error* error_code);
 
   void InvalidateUsageCache(FileSystemOperationContext* context,
-                            const GURL& origin,
+                            const url::Origin& origin,
                             FileSystemType type);
 
   void MarkUsed();
@@ -339,6 +342,10 @@
   // Not owned.
   SandboxFileSystemBackendDelegate* sandbox_delegate_;
 
+  // TODO(https://crbug.com/93417): To be replaced with
+  // ObfuscatedFileUtilDelegate.
+  std::unique_ptr<ObfuscatedFileUtilDiskDelegate> delegate_;
+
   DISALLOW_COPY_AND_ASSIGN(ObfuscatedFileUtil);
 };
 
diff --git a/storage/browser/fileapi/obfuscated_file_util_disk_delegate.cc b/storage/browser/fileapi/obfuscated_file_util_disk_delegate.cc
new file mode 100644
index 0000000..7d2acb3
--- /dev/null
+++ b/storage/browser/fileapi/obfuscated_file_util_disk_delegate.cc
@@ -0,0 +1,89 @@
+// 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 "storage/browser/fileapi/obfuscated_file_util_disk_delegate.h"
+
+#include "base/files/file_util.h"
+#include "storage/browser/fileapi/native_file_util.h"
+
+namespace storage {
+
+ObfuscatedFileUtilDiskDelegate::ObfuscatedFileUtilDiskDelegate() {}
+
+bool ObfuscatedFileUtilDiskDelegate::DirectoryExists(
+    const base::FilePath& path) {
+  return base::DirectoryExists(path);
+}
+
+bool ObfuscatedFileUtilDiskDelegate::DeleteFileOrDirectory(
+    const base::FilePath& path,
+    bool recursive) {
+  return base::DeleteFile(path, recursive);
+}
+
+bool ObfuscatedFileUtilDiskDelegate::IsLink(const base::FilePath& file_path) {
+  return base::IsLink(file_path);
+}
+
+bool ObfuscatedFileUtilDiskDelegate::PathExists(const base::FilePath& path) {
+  return base::PathExists(path);
+}
+
+NativeFileUtil::CopyOrMoveMode
+ObfuscatedFileUtilDiskDelegate::CopyOrMoveModeForDestination(
+    const FileSystemURL& dest_url,
+    bool copy) {
+  return NativeFileUtil::CopyOrMoveModeForDestination(dest_url, copy);
+}
+
+base::File ObfuscatedFileUtilDiskDelegate::CreateOrOpen(
+    const base::FilePath& path,
+    int file_flags) {
+  return NativeFileUtil::CreateOrOpen(path, file_flags);
+}
+
+base::File::Error ObfuscatedFileUtilDiskDelegate::EnsureFileExists(
+    const base::FilePath& path,
+    bool* created) {
+  return NativeFileUtil::EnsureFileExists(path, created);
+}
+base::File::Error ObfuscatedFileUtilDiskDelegate::CreateDirectory(
+    const base::FilePath& path,
+    bool exclusive,
+    bool recursive) {
+  return NativeFileUtil::CreateDirectory(path, exclusive, recursive);
+}
+
+base::File::Error ObfuscatedFileUtilDiskDelegate::GetFileInfo(
+    const base::FilePath& path,
+    base::File::Info* file_info) {
+  return NativeFileUtil::GetFileInfo(path, file_info);
+}
+base::File::Error ObfuscatedFileUtilDiskDelegate::Touch(
+    const base::FilePath& path,
+    const base::Time& last_access_time,
+    const base::Time& last_modified_time) {
+  return NativeFileUtil::Touch(path, last_access_time, last_modified_time);
+}
+
+base::File::Error ObfuscatedFileUtilDiskDelegate::Truncate(
+    const base::FilePath& path,
+    int64_t length) {
+  return NativeFileUtil::Truncate(path, length);
+}
+
+base::File::Error ObfuscatedFileUtilDiskDelegate::CopyOrMoveFile(
+    const base::FilePath& src_path,
+    const base::FilePath& dest_path,
+    FileSystemOperation::CopyOrMoveOption option,
+    NativeFileUtil::CopyOrMoveMode mode) {
+  return NativeFileUtil::CopyOrMoveFile(src_path, dest_path, option, mode);
+}
+
+base::File::Error ObfuscatedFileUtilDiskDelegate::DeleteFile(
+    const base::FilePath& path) {
+  return NativeFileUtil::DeleteFile(path);
+}
+
+}  // namespace storage
diff --git a/storage/browser/fileapi/obfuscated_file_util_disk_delegate.h b/storage/browser/fileapi/obfuscated_file_util_disk_delegate.h
new file mode 100644
index 0000000..612ebb42
--- /dev/null
+++ b/storage/browser/fileapi/obfuscated_file_util_disk_delegate.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 STORAGE_BROWSER_FILEAPI_OBFUSCATED_FILE_UTIL_DISK_DELEGATE_H_
+#define STORAGE_BROWSER_FILEAPI_OBFUSCATED_FILE_UTIL_DISK_DELEGATE_H_
+
+#include "base/component_export.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "storage/browser/fileapi/native_file_util.h"
+
+namespace storage {
+
+// This delegate performs all ObfuscatedFileUtil tasks that actually touch disk.
+
+// TODO(https://crbug.com/93417): To be driven from abstract class
+// |ObfuscatedFileUtilDelegate|.
+class COMPONENT_EXPORT(STORAGE_BROWSER) ObfuscatedFileUtilDiskDelegate {
+ public:
+  ObfuscatedFileUtilDiskDelegate();
+  ~ObfuscatedFileUtilDiskDelegate() = default;
+
+  bool DirectoryExists(const base::FilePath& path);
+  bool DeleteFileOrDirectory(const base::FilePath& path, bool recursive);
+  bool IsLink(const base::FilePath& file_path);
+  bool PathExists(const base::FilePath& path);
+
+  NativeFileUtil::CopyOrMoveMode CopyOrMoveModeForDestination(
+      const FileSystemURL& dest_url,
+      bool copy);
+  base::File CreateOrOpen(const base::FilePath& path, int file_flags);
+  base::File::Error EnsureFileExists(const base::FilePath& path, bool* created);
+  base::File::Error CreateDirectory(const base::FilePath& path,
+                                    bool exclusive,
+                                    bool recursive);
+  base::File::Error GetFileInfo(const base::FilePath& path,
+                                base::File::Info* file_info);
+  base::File::Error Touch(const base::FilePath& path,
+                          const base::Time& last_access_time,
+                          const base::Time& last_modified_time);
+  base::File::Error Truncate(const base::FilePath& path, int64_t length);
+  base::File::Error CopyOrMoveFile(const base::FilePath& src_path,
+                                   const base::FilePath& dest_path,
+                                   FileSystemOperation::CopyOrMoveOption option,
+                                   NativeFileUtil::CopyOrMoveMode mode);
+  base::File::Error DeleteFile(const base::FilePath& path);
+
+  DISALLOW_COPY_AND_ASSIGN(ObfuscatedFileUtilDiskDelegate);
+};
+
+}  // namespace storage
+
+#endif  // STORAGE_BROWSER_FILEAPI_OBFUSCATED_FILE_UTIL_DISK_DELEGATE_H_
diff --git a/storage/browser/fileapi/obfuscated_file_util_unittest.cc b/storage/browser/fileapi/obfuscated_file_util_unittest.cc
index 0640bf1..6bfce69 100644
--- a/storage/browser/fileapi/obfuscated_file_util_unittest.cc
+++ b/storage/browser/fileapi/obfuscated_file_util_unittest.cc
@@ -714,7 +714,8 @@
     std::unique_ptr<ObfuscatedFileUtil> file_util =
         CreateObfuscatedFileUtil(storage_policy_.get());
     const FileSystemURL url = FileSystemURL::CreateForTest(
-        origin_, kFileSystemTypePersistent, base::FilePath());
+        url::Origin::Create(origin_), kFileSystemTypePersistent,
+        base::FilePath());
 
     // Create DirectoryDatabase for isolated origin.
     SandboxDirectoryDatabase* db =
@@ -722,8 +723,8 @@
     ASSERT_TRUE(db != nullptr);
 
     // Destory it.
-    file_util->DestroyDirectoryDatabase(
-        url.origin(), GetTypeString(url.type()));
+    file_util->DestroyDirectoryDatabase(url.origin().GetURL(),
+                                        GetTypeString(url.type()));
     ASSERT_TRUE(file_util->directories_.empty());
   }
 
@@ -732,7 +733,8 @@
     std::unique_ptr<ObfuscatedFileUtil> file_util =
         CreateObfuscatedFileUtil(storage_policy_.get());
     const FileSystemURL url = FileSystemURL::CreateForTest(
-        origin_, kFileSystemTypePersistent, base::FilePath());
+        url::Origin::Create(origin_), kFileSystemTypePersistent,
+        base::FilePath());
 
     // Create DirectoryDatabase for isolated origin.
     SandboxDirectoryDatabase* db =
@@ -741,7 +743,7 @@
     ASSERT_EQ(1U, file_util->directories_.size());
 
     // Remove isolated.
-    storage_policy_->RemoveIsolated(url.origin());
+    storage_policy_->RemoveIsolated(url.origin().GetURL());
 
     // This should still get the same database.
     SandboxDirectoryDatabase* db2 =
@@ -1579,7 +1581,7 @@
       EXPECT_TRUE(created);
     }
   }
-  enumerator.reset(ofu()->CreateOriginEnumerator());
+  enumerator = ofu()->CreateOriginEnumerator();
   EXPECT_TRUE(enumerator.get());
   std::set<GURL> origins_found;
   GURL origin_url;
diff --git a/storage/browser/fileapi/plugin_private_file_system_backend_unittest.cc b/storage/browser/fileapi/plugin_private_file_system_backend_unittest.cc
index 9bda697..948eddf 100644
--- a/storage/browser/fileapi/plugin_private_file_system_backend_unittest.cc
+++ b/storage/browser/fileapi/plugin_private_file_system_backend_unittest.cc
@@ -58,8 +58,7 @@
   FileSystemURL CreateURL(const GURL& root_url, const std::string& relative) {
     FileSystemURL root = context_->CrackURL(root_url);
     return context_->CreateCrackedFileSystemURL(
-        root.origin(),
-        root.mount_type(),
+        root.origin().GetURL(), root.mount_type(),
         root.virtual_path().AppendASCII(relative));
   }
 
diff --git a/storage/browser/fileapi/recursive_operation_delegate.cc b/storage/browser/fileapi/recursive_operation_delegate.cc
index 9cdc107..0b250bbb 100644
--- a/storage/browser/fileapi/recursive_operation_delegate.cc
+++ b/storage/browser/fileapi/recursive_operation_delegate.cc
@@ -115,8 +115,7 @@
 
   for (size_t i = 0; i < entries.size(); i++) {
     FileSystemURL url = file_system_context_->CreateCrackedFileSystemURL(
-        parent.origin(),
-        parent.mount_type(),
+        parent.origin().GetURL(), parent.mount_type(),
         parent.virtual_path().Append(entries[i].name));
     if (entries[i].type == filesystem::mojom::FsFileType::DIRECTORY)
       pending_directory_stack_.top().push(url);
diff --git a/storage/browser/fileapi/sandbox_file_stream_writer.cc b/storage/browser/fileapi/sandbox_file_stream_writer.cc
index 54e35a0..7418c64a 100644
--- a/storage/browser/fileapi/sandbox_file_stream_writer.cc
+++ b/storage/browser/fileapi/sandbox_file_stream_writer.cc
@@ -162,8 +162,7 @@
 
   DCHECK(quota_manager_proxy->quota_manager());
   quota_manager_proxy->quota_manager()->GetUsageAndQuota(
-      url::Origin::Create(url_.origin()),
-      FileSystemTypeToQuotaStorageType(url_.type()),
+      url_.origin(), FileSystemTypeToQuotaStorageType(url_.type()),
       base::BindOnce(&SandboxFileStreamWriter::DidGetUsageAndQuota,
                      weak_factory_.GetWeakPtr(), std::move(callback)));
 }
diff --git a/storage/browser/fileapi/sandbox_file_system_backend.cc b/storage/browser/fileapi/sandbox_file_system_backend.cc
index 15b8a73..5e2f0d1 100644
--- a/storage/browser/fileapi/sandbox_file_system_backend.cc
+++ b/storage/browser/fileapi/sandbox_file_system_backend.cc
@@ -75,8 +75,9 @@
     return;
   }
 
-  delegate_->OpenFileSystem(url.origin(), url.type(), mode, std::move(callback),
-                            GetFileSystemRootURI(url.origin(), url.type()));
+  delegate_->OpenFileSystem(
+      url.origin().GetURL(), url.type(), mode, std::move(callback),
+      GetFileSystemRootURI(url.origin().GetURL(), url.type()));
 }
 
 AsyncFileUtil* SandboxFileSystemBackend::GetAsyncFileUtil(
@@ -113,7 +114,7 @@
     return nullptr;
 
   SpecialStoragePolicy* policy = delegate_->special_storage_policy();
-  if (policy && policy->IsStorageUnlimited(url.origin()))
+  if (policy && policy->IsStorageUnlimited(url.origin().GetURL()))
     operation_context->set_quota_limit_type(storage::kQuotaLimitTypeUnlimited);
   else
     operation_context->set_quota_limit_type(storage::kQuotaLimitTypeLimited);
diff --git a/storage/browser/fileapi/sandbox_file_system_backend_delegate.cc b/storage/browser/fileapi/sandbox_file_system_backend_delegate.cc
index 3e30c7fe..074d0fe 100644
--- a/storage/browser/fileapi/sandbox_file_system_backend_delegate.cc
+++ b/storage/browser/fileapi/sandbox_file_system_backend_delegate.cc
@@ -96,7 +96,7 @@
     : public SandboxFileSystemBackendDelegate::OriginEnumerator {
  public:
   explicit SandboxObfuscatedOriginEnumerator(ObfuscatedFileUtil* file_util) {
-    enum_.reset(file_util->CreateOriginEnumerator());
+    enum_ = file_util->CreateOriginEnumerator();
   }
   ~SandboxObfuscatedOriginEnumerator() override = default;
 
@@ -539,7 +539,7 @@
 
 bool SandboxFileSystemBackendDelegate::IsAccessValid(
     const FileSystemURL& url) const {
-  if (!IsAllowedScheme(url.origin()))
+  if (!IsAllowedScheme(url.origin().GetURL()))
     return false;
 
   if (url.path().ReferencesParent())
diff --git a/storage/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc b/storage/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc
index 7ee2713..baaf6c6 100644
--- a/storage/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc
+++ b/storage/browser/fileapi/sandbox_file_system_backend_delegate_unittest.cc
@@ -28,8 +28,7 @@
 FileSystemURL CreateFileSystemURL(const char* path) {
   const GURL kOrigin("http://foo/");
   return storage::FileSystemURL::CreateForTest(
-      kOrigin,
-      storage::kFileSystemTypeTemporary,
+      url::Origin::Create(kOrigin), storage::kFileSystemTypeTemporary,
       base::FilePath::FromUTF8Unsafe(path));
 }
 
@@ -97,7 +96,7 @@
 
   // Access from non-allowed scheme should be disallowed.
   EXPECT_FALSE(IsAccessValid(
-      FileSystemURL::CreateForTest(GURL("unknown://bar"),
+      FileSystemURL::CreateForTest(url::Origin::Create(GURL("unknown://bar")),
                                    storage::kFileSystemTypeTemporary,
                                    base::FilePath::FromUTF8Unsafe("foo"))));
 
diff --git a/storage/browser/fileapi/sandbox_file_system_backend_unittest.cc b/storage/browser/fileapi/sandbox_file_system_backend_unittest.cc
index e9df0c5..420a0ce 100644
--- a/storage/browser/fileapi/sandbox_file_system_backend_unittest.cc
+++ b/storage/browser/fileapi/sandbox_file_system_backend_unittest.cc
@@ -113,8 +113,9 @@
                    base::FilePath* root_path) {
     base::File::Error error = base::File::FILE_OK;
     backend_->ResolveURL(
-        FileSystemURL::CreateForTest(origin_url, type, base::FilePath()), mode,
-        base::BindOnce(&DidOpenFileSystem, &error));
+        FileSystemURL::CreateForTest(url::Origin::Create(origin_url), type,
+                                     base::FilePath()),
+        mode, base::BindOnce(&DidOpenFileSystem, &error));
     base::RunLoop().RunUntilIdle();
     if (error != base::File::FILE_OK)
       return false;
diff --git a/storage/browser/fileapi/sandbox_quota_observer.cc b/storage/browser/fileapi/sandbox_quota_observer.cc
index ee7a324..9347190 100644
--- a/storage/browser/fileapi/sandbox_quota_observer.cc
+++ b/storage/browser/fileapi/sandbox_quota_observer.cc
@@ -42,7 +42,7 @@
 
   if (quota_manager_proxy_.get()) {
     quota_manager_proxy_->NotifyStorageModified(
-        storage::QuotaClient::kFileSystem, url::Origin::Create(url.origin()),
+        storage::QuotaClient::kFileSystem, url.origin(),
         FileSystemTypeToQuotaStorageType(url.type()), delta);
   }
 
@@ -79,7 +79,7 @@
 void SandboxQuotaObserver::OnAccess(const FileSystemURL& url) {
   if (quota_manager_proxy_.get()) {
     quota_manager_proxy_->NotifyStorageAccessed(
-        storage::QuotaClient::kFileSystem, url::Origin::Create(url.origin()),
+        storage::QuotaClient::kFileSystem, url.origin(),
         FileSystemTypeToQuotaStorageType(url.type()));
   }
 }
@@ -101,7 +101,7 @@
   base::File::Error error = base::File::FILE_OK;
   base::FilePath path =
       SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType(
-          sandbox_file_util_, url.origin(), url.type(), &error);
+          sandbox_file_util_, url.origin().GetURL(), url.type(), &error);
   if (error != base::File::FILE_OK) {
     LOG(WARNING) << "Could not get usage cache path for: "
                  << url.DebugString();
diff --git a/storage/browser/test/test_file_system_backend.cc b/storage/browser/test/test_file_system_backend.cc
index 17b5e5f8..c1919c3 100644
--- a/storage/browser/test/test_file_system_backend.cc
+++ b/storage/browser/test/test_file_system_backend.cc
@@ -143,9 +143,10 @@
 void TestFileSystemBackend::ResolveURL(const FileSystemURL& url,
                                        storage::OpenFileSystemMode mode,
                                        OpenFileSystemCallback callback) {
-  std::move(callback).Run(GetFileSystemRootURI(url.origin(), url.type()),
-                          GetFileSystemName(url.origin(), url.type()),
-                          base::File::FILE_OK);
+  std::move(callback).Run(
+      GetFileSystemRootURI(url.origin().GetURL(), url.type()),
+      GetFileSystemName(url.origin().GetURL(), url.type()),
+      base::File::FILE_OK);
 }
 
 storage::AsyncFileUtil* TestFileSystemBackend::GetAsyncFileUtil(
diff --git a/styleguide/web/es.md b/styleguide/web/es.md
index d4db948..75ad869 100644
--- a/styleguide/web/es.md
+++ b/styleguide/web/es.md
@@ -83,11 +83,10 @@
 });
 </script>
 
-[TOC]
+# ECMAScript Features in Chromium
 
-> **TBD:** Do we need to differentiate per-project?
-
-> **TBD:** Cross-platform build support? As in: transpilers?
+This doc extends the [style guide](web.md#JavaScript) by specifying which new
+features of ES2015 and beyond are allowed in Chromium.
 
 You can propose changing the status of a feature by sending an email to
 chromium-dev@chromium.org. Include a short blurb on what the feature is and why
@@ -95,6 +94,8 @@
 previous discussion. If the list arrives at some consensus, send a codereview
 to change this file accordingly, linking to your discussion thread.
 
+[TOC]
+
 # ES2015 Support In Chromium
 
 This is a list of [ECMAScript 6 a.k.a.
@@ -1134,4 +1135,4 @@
 
 **Discussion Notes / Link to Thread:**
 
----
\ No newline at end of file
+---
diff --git a/styleguide/web/web.md b/styleguide/web/web.md
index fa57e37..56230a7 100644
--- a/styleguide/web/web.md
+++ b/styleguide/web/web.md
@@ -302,7 +302,8 @@
 ### Style
 
 See the [Google JavaScript Style
-Guide](https://google.github.io/styleguide/jsguide.html).
+Guide](https://google.github.io/styleguide/jsguide.html) as well as
+[ECMAScript Features in Chromium](es.md).
 
 * Use `$('element-id')` instead of `document.getElementById`
 
diff --git a/testing/buildbot/chromium.chromiumos.json b/testing/buildbot/chromium.chromiumos.json
index 869dfd7..d74d8ce 100644
--- a/testing/buildbot/chromium.chromiumos.json
+++ b/testing/buildbot/chromium.chromiumos.json
@@ -436,9 +436,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_ash_unittests",
+        "name": "non_single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -515,10 +515,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests"
         ],
-        "name": "single_process_mash_browser_tests",
+        "name": "non_single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 20
@@ -627,6 +627,18 @@
       },
       {
         "args": [
+          "--disable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests"
+        ],
+        "name": "non_single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-perfetto",
           "--gtest_filter=TracingControllerTest.*"
         ],
@@ -638,18 +650,6 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests"
-        ],
-        "name": "single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_content_browsertests",
@@ -667,9 +667,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_content_unittests",
+        "name": "non_single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -723,9 +723,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_exo_unittests",
+        "name": "non_single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -794,9 +794,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_interactive_ui_tests",
+        "name": "non_single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 3
@@ -1049,9 +1049,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_unit_tests",
+        "name": "non_single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1132,9 +1132,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_ash_unittests",
+        "name": "non_single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1211,10 +1211,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests"
         ],
-        "name": "single_process_mash_browser_tests",
+        "name": "non_single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 10
@@ -1323,6 +1323,18 @@
       },
       {
         "args": [
+          "--disable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests"
+        ],
+        "name": "non_single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-perfetto",
           "--gtest_filter=TracingControllerTest.*"
         ],
@@ -1334,18 +1346,6 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests"
-        ],
-        "name": "single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=VizDisplayCompositor"
         ],
         "name": "viz_content_browsertests",
@@ -1363,9 +1363,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_content_unittests",
+        "name": "non_single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1419,9 +1419,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_exo_unittests",
+        "name": "non_single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -1490,9 +1490,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_interactive_ui_tests",
+        "name": "non_single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 3
@@ -1744,9 +1744,9 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash"
+          "--disable-features=SingleProcessMash"
         ],
-        "name": "single_process_mash_unit_tests",
+        "name": "non_single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
diff --git a/testing/buildbot/chromium.gpu.fyi.json b/testing/buildbot/chromium.gpu.fyi.json
index 20d8fdc..233858f 100644
--- a/testing/buildbot/chromium.gpu.fyi.json
+++ b/testing/buildbot/chromium.gpu.fyi.json
@@ -5939,60 +5939,6 @@
           "--browser=release",
           "--passthrough",
           "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl2_conformance_gl_passthrough_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-18.0.5",
-              "os": "Ubuntu",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "idempotent": false,
-          "shards": 20
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
-          "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc",
-          "--webgl-conformance-version=2.0.1",
-          "--read-abbreviated-json-results-from=../../content/test/data/gpu/webgl2_conformance_tests_output.json"
-        ],
-        "isolate_name": "telemetry_gpu_integration_test",
-        "name": "webgl2_conformance_tests",
-        "should_retry_with_patch": false,
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "gpu": "8086:5912-18.0.5",
-              "os": "Ubuntu",
-              "pool": "Chrome-GPU"
-            }
-          ],
-          "idempotent": false,
-          "shards": 20
-        }
-      },
-      {
-        "args": [
-          "webgl_conformance",
-          "--show-stdout",
-          "--browser=release",
-          "--passthrough",
-          "-v",
           "--extra-browser-args=--enable-logging=stderr --js-flags=--expose-gc --use-gl=angle --use-angle=gl --use-cmd-decoder=passthrough"
         ],
         "isolate_name": "telemetry_gpu_integration_test",
diff --git a/testing/buildbot/chromium.memory.json b/testing/buildbot/chromium.memory.json
index 8b3d577..8654341 100644
--- a/testing/buildbot/chromium.memory.json
+++ b/testing/buildbot/chromium.memory.json
@@ -4172,10 +4172,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_ash_unittests",
+        "name": "non_single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4280,11 +4280,11 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_browser_tests",
+        "name": "non_single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 30
@@ -4430,6 +4430,19 @@
       },
       {
         "args": [
+          "--disable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "non_single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-perfetto",
           "--gtest_filter=TracingControllerTest.*",
           "--test-launcher-print-test-stdio=always"
@@ -4442,19 +4455,6 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "name": "single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=VizDisplayCompositor",
           "--test-launcher-print-test-stdio=always"
         ],
@@ -4476,10 +4476,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_content_unittests",
+        "name": "non_single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4552,10 +4552,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_exo_unittests",
+        "name": "non_single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -4645,10 +4645,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_interactive_ui_tests",
+        "name": "non_single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "shards": 3
@@ -5015,10 +5015,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_unit_tests",
+        "name": "non_single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true
         },
@@ -5168,10 +5168,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_ash_unittests",
+        "name": "non_single_process_mash_ash_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5342,11 +5342,11 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--override-use-software-gl-for-tests",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_browser_tests",
+        "name": "non_single_process_mash_browser_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5581,6 +5581,25 @@
       },
       {
         "args": [
+          "--disable-features=SingleProcessMash",
+          "--override-use-software-gl-for-tests",
+          "--test-launcher-print-test-stdio=always"
+        ],
+        "name": "non_single_process_mash_content_browsertests",
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "cpu": "x86-64",
+              "os": "Ubuntu-14.04"
+            }
+          ],
+          "shards": 5
+        },
+        "test": "content_browsertests"
+      },
+      {
+        "args": [
           "--enable-perfetto",
           "--gtest_filter=TracingControllerTest.*",
           "--test-launcher-print-test-stdio=always"
@@ -5599,25 +5618,6 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
-          "--override-use-software-gl-for-tests",
-          "--test-launcher-print-test-stdio=always"
-        ],
-        "name": "single_process_mash_content_browsertests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "cpu": "x86-64",
-              "os": "Ubuntu-14.04"
-            }
-          ],
-          "shards": 5
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
           "--enable-features=VizDisplayCompositor",
           "--test-launcher-print-test-stdio=always"
         ],
@@ -5652,10 +5652,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_content_unittests",
+        "name": "non_single_process_mash_content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5776,10 +5776,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_exo_unittests",
+        "name": "non_single_process_mash_exo_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -5929,10 +5929,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_interactive_ui_tests",
+        "name": "non_single_process_mash_interactive_ui_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
@@ -6524,10 +6524,10 @@
       },
       {
         "args": [
-          "--enable-features=SingleProcessMash",
+          "--disable-features=SingleProcessMash",
           "--test-launcher-print-test-stdio=always"
         ],
-        "name": "single_process_mash_unit_tests",
+        "name": "non_single_process_mash_unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
           "dimension_sets": [
diff --git a/testing/buildbot/chromium.perf.fyi.json b/testing/buildbot/chromium.perf.fyi.json
index 43e4103..784671b0 100644
--- a/testing/buildbot/chromium.perf.fyi.json
+++ b/testing/buildbot/chromium.perf.fyi.json
@@ -118,8 +118,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 25,
-          "upload_test_results": true
+          "shards": 25
         },
         "trigger_script": {
           "args": [
@@ -209,8 +208,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 7,
-          "upload_test_results": true
+          "shards": 7
         },
         "trigger_script": {
           "args": [
@@ -255,8 +253,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 7,
-          "upload_test_results": true
+          "shards": 7
         },
         "trigger_script": {
           "args": [
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 0aa98c44..ec8f9f6 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -31,8 +31,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -70,8 +69,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -109,8 +107,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -152,8 +149,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 16,
-          "upload_test_results": true
+          "shards": 16
         },
         "trigger_script": {
           "args": [
@@ -199,8 +195,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 16,
-          "upload_test_results": true
+          "shards": 16
         },
         "trigger_script": {
           "args": [
@@ -246,8 +241,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 8,
-          "upload_test_results": true
+          "shards": 8
         },
         "trigger_script": {
           "args": [
@@ -287,8 +281,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -324,8 +317,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -361,8 +353,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -400,8 +391,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -439,8 +429,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -480,8 +469,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 5,
-          "upload_test_results": true
+          "shards": 5
         },
         "trigger_script": {
           "args": [
@@ -521,8 +509,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -558,8 +545,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -595,8 +581,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -635,8 +620,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 5,
-          "upload_test_results": true
+          "shards": 5
         },
         "trigger_script": {
           "args": [
@@ -688,8 +672,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 19,
-          "upload_test_results": true
+          "shards": 19
         },
         "trigger_script": {
           "args": [
@@ -732,8 +715,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -771,8 +753,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -810,8 +791,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -849,8 +829,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -888,8 +867,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -927,8 +905,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -970,8 +947,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 16,
-          "upload_test_results": true
+          "shards": 16
         },
         "trigger_script": {
           "args": [
@@ -1023,8 +999,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1060,8 +1035,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1097,8 +1071,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1134,8 +1107,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1171,8 +1143,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1208,8 +1179,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1249,8 +1219,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 26,
-          "upload_test_results": true
+          "shards": 26
         },
         "trigger_script": {
           "args": [
@@ -1290,8 +1259,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1327,8 +1295,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1368,8 +1335,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 26,
-          "upload_test_results": true
+          "shards": 26
         },
         "trigger_script": {
           "args": [
@@ -1409,8 +1375,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1446,8 +1411,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1483,8 +1447,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1520,8 +1483,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1557,8 +1519,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1598,8 +1559,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 26,
-          "upload_test_results": true
+          "shards": 26
         },
         "trigger_script": {
           "args": [
@@ -1645,8 +1605,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1682,8 +1641,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1719,8 +1677,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1756,8 +1713,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1793,8 +1749,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 1,
-          "upload_test_results": true
+          "shards": 1
         },
         "trigger_script": {
           "args": [
@@ -1834,8 +1789,7 @@
           "hard_timeout": 36000,
           "ignore_task_failure": false,
           "io_timeout": 1800,
-          "shards": 26,
-          "upload_test_results": true
+          "shards": 26
         },
         "trigger_script": {
           "args": [
diff --git a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
index b13ed1c8..844c171 100644
--- a/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
+++ b/testing/buildbot/filters/mojo.fyi.network_webview_instrumentation_test_apk.filter
@@ -14,12 +14,10 @@
 # https://crbug.com/893566
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testBaseUrlOfLoadDataSentInRefererHeader
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testCalledForUnsupportedSchemes
--org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testCalledWithCorrectRefererHeader
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testContentIdIframe
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testLoadDataShouldTriggerShouldInterceptRequest
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testLoadDataUrlShouldTriggerShouldInterceptRequest
 -org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testLoadDataWithBaseUrlTriggersShouldInterceptRequest
--org.chromium.android_webview.test.AwContentsClientShouldInterceptRequestTest.testNotCalledForHttpRedirect
 
 # https://crbug.com/893568
 -org.chromium.android_webview.test.AwContentsTest.testDownload
diff --git a/testing/buildbot/test_suite_exceptions.pyl b/testing/buildbot/test_suite_exceptions.pyl
index 3ef5a8b..97a864f 100644
--- a/testing/buildbot/test_suite_exceptions.pyl
+++ b/testing/buildbot/test_suite_exceptions.pyl
@@ -924,6 +924,29 @@
       },
     },
   },
+  'non_single_process_mash_browser_tests': {
+    'modifications': {
+      # chromium.chromiumos
+      'linux-chromeos-dbg': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+      # chromium.memory
+      'Linux Chromium OS ASan LSan Tests (1)': {
+        # These are very slow on the ASAN trybot for some reason.
+        # crbug.com/794372
+        'swarming': {
+          'shards': 30,
+        },
+      },
+      'Linux ChromiumOS MSan Tests': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+    },
+  },
   'not_site_per_process_webkit_layout_tests': {
     'remove_from': [
       # chromium.linux
@@ -991,29 +1014,6 @@
       'Linux MSan Tests',  # https://crbug.com/831676
     ],
   },
-  'single_process_mash_browser_tests': {
-    'modifications': {
-      # chromium.chromiumos
-      'linux-chromeos-dbg': {
-        'swarming': {
-          'shards': 20,
-        },
-      },
-      # chromium.memory
-      'Linux Chromium OS ASan LSan Tests (1)': {
-        # These are very slow on the ASAN trybot for some reason.
-        # crbug.com/794372
-        'swarming': {
-          'shards': 30,
-        },
-      },
-      'Linux ChromiumOS MSan Tests': {
-        'swarming': {
-          'shards': 20,
-        },
-      },
-    },
-  },
   'sizes': {
     'remove_from': [
       'win32-dbg',
@@ -1323,8 +1323,16 @@
       'linux-chromeos-dbg',  # https://crbug.com/859307
     ],
   },
+  'webgl2_conformance_gl_passthrough_tests': {
+    'remove_from': [
+      # https://crbug.com/927470
+      'Linux FYI Experimental Release (Intel HD 630)',
+    ],
+  },
   'webgl2_conformance_tests': {
     'remove_from': [
+      # https://crbug.com/927470
+      'Linux FYI Experimental Release (Intel HD 630)',
       # The Mac NVIDIA Retina bots don't have the capacity to run
       # this test suite on mac_optional_gpu_tests_rel.
       'Optional Mac Retina Release (NVIDIA)',
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 8e4ceee..e54bcc6 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3605,57 +3605,57 @@
       },
       'ozone_unittests': {},
       'ozone_x11_unittests': {},
-      'single_process_mash_ash_unittests': {
+      'non_single_process_mash_ash_unittests': {
         'test': 'ash_unittests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
         ],
       },
-      'single_process_mash_browser_tests': {
+      'non_single_process_mash_browser_tests': {
         'test': 'browser_tests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
           '--override-use-software-gl-for-tests'
         ],
         'swarming': {
           'shards': 10,
         },
       },
-      'single_process_mash_content_unittests': {
+      'non_single_process_mash_content_unittests': {
         'test': 'content_unittests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
         ],
       },
-      'single_process_mash_content_browsertests': {
+      'non_single_process_mash_content_browsertests': {
         'test': 'content_browsertests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
           '--override-use-software-gl-for-tests'
         ],
         'swarming': {
           'shards': 5,
         },
       },
-      'single_process_mash_exo_unittests': {
+      'non_single_process_mash_exo_unittests': {
         'test': 'exo_unittests',
         'args': [
-          '--enable-features=SingleProcessMash',
+          '--disable-features=SingleProcessMash',
         ],
       },
-      'single_process_mash_interactive_ui_tests': {
+      'non_single_process_mash_interactive_ui_tests': {
         'test': 'interactive_ui_tests',
         'args': [
-          '--enable-features=SingleProcessMash'
+          '--disable-features=SingleProcessMash'
         ],
         'swarming': {
           'shards': 3,
         },
       },
-      'single_process_mash_unit_tests': {
+      'non_single_process_mash_unit_tests': {
         'test': 'unit_tests',
         'args': [
-          '--enable-features=SingleProcessMash'
+          '--disable-features=SingleProcessMash'
         ],
       },
       'ui_chromeos_unittests': {},
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index e919cd9a..b986124 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1335,6 +1335,21 @@
             ]
         }
     ],
+    "D3D11VideoDecoderWin": [
+        {
+            "platforms": [
+                "windows"
+            ],
+            "experiments": [
+                {
+                    "name": "D3D11VideoDecoderEnabled",
+                    "enable_features": [
+                        "D3D11VideoDecoder"
+                    ]
+                }
+            ]
+        }
+    ],
     "DataCompressionProxyLoFi": [
         {
             "platforms": [
@@ -4273,26 +4288,6 @@
             ]
         }
     ],
-    "SequenceManagerRollout": [
-        {
-            "platforms": [
-                "android",
-                "chromeos",
-                "ios",
-                "linux",
-                "mac",
-                "windows"
-            ],
-            "experiments": [
-                {
-                    "name": "Enabled",
-                    "enable_features": [
-                        "BlinkMainThreadUsesSequenceManager"
-                    ]
-                }
-            ]
-        }
-    ],
     "ServiceWorkerServicification": [
         {
             "platforms": [
diff --git a/third_party/blink/common/BUILD.gn b/third_party/blink/common/BUILD.gn
index 7cc97b3..7a278c3 100644
--- a/third_party/blink/common/BUILD.gn
+++ b/third_party/blink/common/BUILD.gn
@@ -118,6 +118,7 @@
   testonly = true
 
   sources = [
+    "client_hints/client_hints_unittest.cc",
     "device_memory/approximated_device_memory_unittest.cc",
     "feature_policy/feature_policy_unittest.cc",
     "frame/user_activation_state_unittest.cc",
diff --git a/third_party/blink/common/client_hints/client_hints.cc b/third_party/blink/common/client_hints/client_hints.cc
index a4bc77c..5777f6cc 100644
--- a/third_party/blink/common/client_hints/client_hints.cc
+++ b/third_party/blink/common/client_hints/client_hints.cc
@@ -5,15 +5,24 @@
 #include "third_party/blink/public/common/client_hints/client_hints.h"
 
 #include "base/stl_util.h"
+#include "base/strings/string_tokenizer.h"
 
 namespace blink {
 
+const char* const kClientHintsNameMapping[] = {
+    "device-memory", "dpr",      "width", "viewport-width",
+    "rtt",           "downlink", "ect",   "lang"};
+
 const char* const kClientHintsHeaderMapping[] = {
     "device-memory", "dpr",      "width", "viewport-width",
-    "rtt",           "downlink", "ect"};
+    "rtt",           "downlink", "ect",   "sec-ch-lang"};
 
-const size_t kClientHintsHeaderMappingCount =
-    base::size(kClientHintsHeaderMapping);
+const size_t kClientHintsMappingsCount = base::size(kClientHintsNameMapping);
+
+static_assert(base::size(kClientHintsNameMapping) ==
+                  base::size(kClientHintsHeaderMapping),
+              "The Client Hint name and header mappings must contain the same "
+              "number of entries.");
 
 const char* const kWebEffectiveConnectionTypeMapping[] = {
     "4g" /* Unknown */, "4g" /* Offline */, "slow-2g" /* Slow 2G */,
@@ -23,4 +32,18 @@
 const size_t kWebEffectiveConnectionTypeMappingCount =
     base::size(kWebEffectiveConnectionTypeMapping);
 
+std::string SerializeLangClientHint(const std::string& raw_language_list) {
+  base::StringTokenizer t(raw_language_list, ",");
+  std::string result;
+  while (t.GetNext()) {
+    if (!result.empty())
+      result.append(", ");
+
+    result.append("\"");
+    result.append(t.token().c_str());
+    result.append("\"");
+  }
+  return result;
+}
+
 }  // namespace blink
diff --git a/third_party/blink/common/client_hints/client_hints_unittest.cc b/third_party/blink/common/client_hints/client_hints_unittest.cc
new file mode 100644
index 0000000..aaeb354
--- /dev/null
+++ b/third_party/blink/common/client_hints/client_hints_unittest.cc
@@ -0,0 +1,26 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/common/client_hints/client_hints.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+
+TEST(ClientHintsTest, SerializeLangClientHint) {
+  std::string header = SerializeLangClientHint("");
+  EXPECT_TRUE(header.empty());
+
+  header = SerializeLangClientHint("es");
+  EXPECT_EQ(std::string("\"es\""), header);
+
+  header = SerializeLangClientHint("en-US,fr,de");
+  EXPECT_EQ(std::string("\"en-US\", \"fr\", \"de\""), header);
+
+  header = SerializeLangClientHint("en-US,fr,de,ko,zh-CN,ja");
+  EXPECT_EQ(std::string("\"en-US\", \"fr\", \"de\", \"ko\", \"zh-CN\", \"ja\""),
+            header);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index 1f3e44b42..8ee50bc 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/public/common/features.h"
 
 #include "build/build_config.h"
+#include "services/network/public/cpp/features.h"
 
 namespace blink {
 namespace features {
@@ -75,6 +76,11 @@
     "OffMainThreadDedicatedWorkerScriptFetch",
     base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Enable off-the-main-thread shared worker script fetch.
+// (https://crbug.com/924041)
+const base::Feature kOffMainThreadSharedWorkerScriptFetch{
+    "OffMainThreadSharedWorkerScriptFetch", base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Onion souping for all DOMStorage. https://crbug.com/781870
 const base::Feature kOnionSoupDOMStorage{"OnionSoupDOMStorage",
                                          base::FEATURE_DISABLED_BY_DEFAULT};
@@ -114,7 +120,7 @@
 // WebURLLoaderClient::DidStartLoadingResponseBody() instead of
 // WebURLLoaderClient::DidReceiveData().
 const base::Feature kResourceLoadViaDataPipe{"ResourceLoadViaDataPipe",
-                                             base::FEATURE_DISABLED_BY_DEFAULT};
+                                             base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kServiceWorkerImportedScriptUpdateCheck{
     "ServiceWorkerImportedScriptUpdateCheck",
@@ -190,5 +196,18 @@
 const base::Feature kWebFontsCacheAwareTimeoutAdaption{
     "WebFontsCacheAwareTimeoutAdaption", base::FEATURE_ENABLED_BY_DEFAULT};
 
+bool IsOffMainThreadSharedWorkerScriptFetchEnabled() {
+  // Off-the-main-thread shared worker script fetch depends on PlzSharedWorker
+  // (NetworkService).
+  DCHECK(!base::FeatureList::IsEnabled(
+             features::kOffMainThreadSharedWorkerScriptFetch) ||
+         base::FeatureList::IsEnabled(network::features::kNetworkService))
+      << "OffMainThreadSharedWorkerScriptFetch is enabled but NetworkService "
+      << "isn't. OffMainThreadSharedWorkerScriptFetch requires NetworkService.";
+  return base::FeatureList::IsEnabled(network::features::kNetworkService) &&
+         base::FeatureList::IsEnabled(
+             features::kOffMainThreadSharedWorkerScriptFetch);
+}
+
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/manual_tests/scheduling-isInputPending.html b/third_party/blink/manual_tests/scheduling-isInputPending.html
new file mode 100644
index 0000000..e9c8b37c
--- /dev/null
+++ b/third_party/blink/manual_tests/scheduling-isInputPending.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset=utf-8 />
+  <title>IsInputPending: clicks should be reported during main thread contention</title>
+  </title>
+  <div>
+    Click the button twice and expect two green lines of text.
+    This test should be run under --enable-blink-features=ExperimentalIsInputPending
+  </div>
+</head>
+<button onclick="next()">Start</button>
+<p id="preClickResult"></p>
+<p id="postClickResult"></p>
+<script>
+
+function hasPendingClick() {
+  return navigator.scheduling.isInputPending(['click']);
+}
+
+// |expected| is the expected value from isInputPending(['click']).
+function createResult(actual, expected) {
+  const result = document.createElement('div');
+  if (actual == expected) {
+    result.innerHTML = 'PASSED: isInputPending was ' + actual;
+    result.style = 'color:green';
+  }
+  else {
+    result.innerHTML = 'FAILED: isInputPending was ' + actual;
+    result.style = 'color:red';
+  }
+  return result;
+}
+
+let state = "start";
+
+function next() {
+  switch (state) {
+    case "start":
+      // Don't return from the event loop until a click is detected.
+      while (!hasPendingClick()) {}
+
+      const preClickResult = document.querySelector("#preClickResult");
+      preClickResult.appendChild(createResult(true, true));
+
+      state = "clicked";
+      break;
+    case "clicked":
+      // In the click event handler, expect no pending clicks.
+      const postClickResult = document.querySelector("#postClickResult");
+      postClickResult.appendChild(createResult(hasPendingClick(), false));
+
+      state = "end";
+      break;
+    case "end":
+      break;
+  }
+}
+
+</script>
+</html>
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index c47eea98..3bc3ff6 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -176,6 +176,7 @@
     "platform/modules/service_worker/web_service_worker_request.h",
     "platform/modules/service_worker/web_service_worker_response.h",
     "platform/modules/service_worker/web_service_worker_stream_handle.h",
+    "platform/notification_data_conversions.h",
     "platform/platform.h",
     "platform/pointer_properties.h",
     "platform/scheduler/web_rail_mode_observer.h",
@@ -288,9 +289,8 @@
     "platform/web_media_stream_center.h",
     "platform/web_media_stream_source.h",
     "platform/web_media_stream_track.h",
-    "platform/web_memory_coordinator.h",
     "platform/web_memory_pressure_level.h",
-    "platform/web_memory_state.h",
+    "platform/web_memory_pressure_listener.h",
     "platform/web_menu_source_type.h",
     "platform/web_mixed_content.h",
     "platform/web_mixed_content_context_type.h",
diff --git a/third_party/blink/public/common/client_hints/client_hints.h b/third_party/blink/public/common/client_hints/client_hints.h
index e3beead..9304aa8e 100644
--- a/third_party/blink/public/common/client_hints/client_hints.h
+++ b/third_party/blink/public/common/client_hints/client_hints.h
@@ -6,17 +6,19 @@
 #define THIRD_PARTY_BLINK_PUBLIC_COMMON_CLIENT_HINTS_CLIENT_HINTS_H_
 
 #include <stddef.h>
+#include <string>
 
 #include "third_party/blink/public/common/common_export.h"
 
 namespace blink {
 
-// Mapping from WebClientHintsType to the header value for enabling the
-// corresponding client hint. The ordering should match the ordering of enums in
+// Mapping from WebClientHintsType to the hint's name in header values (e.g.
+// kLang => "lang"), and to the hint's outgoing header (e.g. kLang =>
+// "sec-ch-lang"). The ordering matches the ordering of enums in
 // third_party/blink/public/platform/web_client_hints_type.h.
+BLINK_COMMON_EXPORT extern const char* const kClientHintsNameMapping[];
 BLINK_COMMON_EXPORT extern const char* const kClientHintsHeaderMapping[];
-
-BLINK_COMMON_EXPORT extern const size_t kClientHintsHeaderMappingCount;
+BLINK_COMMON_EXPORT extern const size_t kClientHintsMappingsCount;
 
 // Mapping from WebEffectiveConnectionType to the header value. This value is
 // sent to the origins and is returned by the JavaScript API. The ordering
@@ -29,6 +31,12 @@
 
 BLINK_COMMON_EXPORT extern const size_t kWebEffectiveConnectionTypeMappingCount;
 
+// Given a comma-separated, ordered list of language codes, return the list
+// formatted as a structured header, as described in
+// https://tools.ietf.org/html/draft-west-lang-client-hint-00#section-2.1
+std::string BLINK_COMMON_EXPORT
+SerializeLangClientHint(const std::string& raw_language_list);
+
 }  // namespace blink
 
 #endif  // THIRD_PARTY_BLINK_PUBLIC_COMMON_CLIENT_HINTS_CLIENT_HINTS_H_
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index 3b4e1a7..46cd2ca 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -29,6 +29,8 @@
 BLINK_COMMON_EXPORT extern const base::Feature kNavigationPredictor;
 BLINK_COMMON_EXPORT extern const base::Feature
     kOffMainThreadDedicatedWorkerScriptFetch;
+BLINK_COMMON_EXPORT extern const base::Feature
+    kOffMainThreadSharedWorkerScriptFetch;
 BLINK_COMMON_EXPORT extern const base::Feature kOnionSoupDOMStorage;
 BLINK_COMMON_EXPORT extern const base::Feature kPlzDedicatedWorker;
 BLINK_COMMON_EXPORT extern const base::Feature kPortals;
@@ -65,6 +67,9 @@
 BLINK_COMMON_EXPORT extern const base::Feature
     kWebFontsCacheAwareTimeoutAdaption;
 
+// Returns true when off-the-main-thread shared worker script fetch is enabled.
+BLINK_COMMON_EXPORT bool IsOffMainThreadSharedWorkerScriptFetchEnabled();
+
 }  // namespace features
 }  // namespace blink
 
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index d0d93fd..f81cae6 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -83,6 +83,7 @@
     "speech/speech_recognition_result.mojom",
     "speech/speech_recognizer.mojom",
     "use_counter/css_property_id.mojom",
+    "v8_cache_options.mojom",
     "wake_lock/wake_lock.mojom",
     "webaudio/audio_context_manager.mojom",
     "worker/dedicated_worker_factory.mojom",
@@ -190,6 +191,7 @@
     # so we put these service worker mojom files here rather than mojom_platform
     # target.
     "service_worker/controller_service_worker.mojom",
+    "service_worker/embedded_worker.mojom",
     "service_worker/service_worker.mojom",
     "service_worker/service_worker_container.mojom",
     "service_worker/service_worker_object.mojom",
diff --git a/third_party/blink/public/mojom/service_worker/embedded_worker.mojom b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
new file mode 100644
index 0000000..2b941c1
--- /dev/null
+++ b/third_party/blink/public/mojom/service_worker/embedded_worker.mojom
@@ -0,0 +1,181 @@
+// 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.
+
+module blink.mojom;
+
+import "mojo/public/mojom/base/string16.mojom";
+import "mojo/public/mojom/base/time.mojom";
+import "mojo/public/mojom/base/unguessable_token.mojom";
+import "third_party/blink/public/mojom/devtools/console_message.mojom";
+import "third_party/blink/public/mojom/devtools/devtools_agent.mojom";
+import "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom";
+import "third_party/blink/public/mojom/renderer_preference_watcher.mojom";
+import "third_party/blink/public/mojom/renderer_preferences.mojom";
+import "third_party/blink/public/mojom/script/script_type.mojom";
+import "third_party/blink/public/mojom/service_worker/controller_service_worker.mojom";
+import "third_party/blink/public/mojom/service_worker/service_worker.mojom";
+import "third_party/blink/public/mojom/service_worker/service_worker_installed_scripts_manager.mojom";
+import "third_party/blink/public/mojom/service_worker/service_worker_event_status.mojom";
+import "third_party/blink/public/mojom/service_worker/service_worker_provider.mojom";
+import "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom";
+import "third_party/blink/public/mojom/v8_cache_options.mojom";
+import "third_party/blink/public/mojom/worker/worker_content_settings_proxy.mojom";
+import "third_party/blink/public/platform/web_feature.mojom";
+import "url/mojom/url.mojom";
+
+// Parameters to launch a service worker. This is passed from the browser to the
+// renderer at mojom::EmbeddedWorkerInstanceClient::StartWorker().
+struct EmbeddedWorkerStartParams {
+  // DEPRECATED: This is only used in unit tests.
+  // TODO(https://crbug.com/927651): Remove this.
+  int32 embedded_worker_id;
+
+  // The id of the service worker being started. This remains fixed even if the
+  // worker is stopped and restarted, or even if the browser restarts.
+  int64 service_worker_version_id;
+  // This service worker's registration's scope:
+  // https://w3c.github.io/ServiceWorker/#service-worker-registration-scope
+  url.mojom.Url scope;
+  // This service worker's script url:
+  // https://w3c.github.io/ServiceWorker/#dom-serviceworker-scripturl
+  url.mojom.Url script_url;
+  // This service worker's script type:
+  // https://w3c.github.io/ServiceWorker/#dfn-type
+  ScriptType script_type;
+  // The string used for "user-agent" HTTP header.
+  string user_agent;
+  // The id to talk with the DevTools agent for the worker.
+  int32 worker_devtools_agent_route_id;
+  // Unique token identifying this worker for DevTools.
+  mojo_base.mojom.UnguessableToken devtools_worker_token;
+  // When true, worker script evaluation is blocked until
+  // EmbeddedWorkerInstanceClient::ResumeAfterDownload() is called.
+  bool pause_after_download;
+  // True if starting the worker should wait until DevTools gets ready.
+  bool wait_for_debugger;
+  // True if this service worker has been installed.
+  bool is_installed;
+  // Determines how eagerly V8 creates the code cache.
+  V8CacheOptions v8_cache_options;
+  // Used to set up fetch requests.
+  RendererPreferences renderer_preferences;
+
+  // Used to talk to the service worker from the browser process.
+  ServiceWorker& service_worker_request;
+  // S13nServiceWorker: cloned and passed to each controllee to directly
+  // dispatch events from the controllees.
+  ControllerServiceWorker& controller_request;
+  // Information to transfer installed scripts from the browser to the renderer.
+  ServiceWorkerInstalledScriptsInfo? installed_scripts_info;
+  // Interface for the renderer to send the status updates to the browser.
+  associated EmbeddedWorkerInstanceHost instance_host;
+  // Information for creating ServiceWorkerProviderContext on the renderer.
+  ServiceWorkerProviderInfoForStartWorker provider_info;
+  // Interface for the renderer to query the content settings in the browser.
+  WorkerContentSettingsProxy content_settings_proxy;
+  // Interface for keeping track of the renderer preferences.
+  RendererPreferenceWatcher& preference_watcher_request;
+  // S13nServiceWorker: Used to load subresources in the service worker.
+  // This allows the service worker to load chrome-extension:// URLs which
+  // the renderer's default loader factory can't load.
+  URLLoaderFactoryBundle? subresource_loader_factories;
+};
+
+// Holds timing information about the start worker sequence for UMA.
+//
+// Keep this in sync with the validation check in
+// EmbeddedWorkerInstance::OnStarted.
+// TODO(falken): Make a typemap just for the validation check?
+struct EmbeddedWorkerStartTiming {
+  // When the start worker message was received by the renderer.
+  mojo_base.mojom.TimeTicks start_worker_received_time;
+  // When JavaScript evaluation on the worker thread started.
+  mojo_base.mojom.TimeTicks script_evaluation_start_time;
+  // When JavaScript evaluation on the worker thread finished.
+  mojo_base.mojom.TimeTicks script_evaluation_end_time;
+};
+
+// EmbeddedWorkerInstanceClient is the renderer-side ("Client") of
+// EmbeddedWorkerInstanceHost. It allows control of a renderer-side
+// embedded worker. The browser uses this interface to start, stop, and
+// issue commands to the worker.
+//
+// This interface is the master interface of a dedicated message pipe. It has
+// some interfaces associated with it, like EmbeddedWorkerInstanceHost.
+interface EmbeddedWorkerInstanceClient {
+  // Called back as various functions in EmbeddedWorkerInstanceHost, such
+  // as OnThreadStarted(), OnStarted().
+  StartWorker(EmbeddedWorkerStartParams params);
+  // The response is sent back via EmbeddedWorkerInstanceHost.OnStopped().
+  StopWorker();
+  ResumeAfterDownload();
+  AddMessageToConsole(ConsoleMessageLevel level, string message);
+  // Returns a DevToolsAgent interface for this embedded worker, used for
+  // remote debugging. See DevToolsAgent for details.
+  BindDevToolsAgent(associated DevToolsAgentHost agent_host,
+                    associated DevToolsAgent& agent);
+};
+
+// EmbeddedWorkerInstanceHost is the browser-side ("Host") of
+// EmbeddedWorkerInstanceClient. It allows control of a browser-side embedded
+// worker instance. The renderer uses this interface to report embedded worker
+// state back to the browser, or request termination of the worker. This
+// interface is associated with the master interface
+// EmbeddedWorkerInstanceClient, so it lives on the same message pipe as
+// EmbeddedWorkerInstanceClient.
+interface EmbeddedWorkerInstanceHost {
+  // S13nServiceWorker:
+  // Called when the worker requests to be terminated. The worker will request
+  // to be terminated when it realizes it has been idle for some time. The
+  // browser doesn't terminate the worker when there are inflight events or
+  // DevTools is attached, and in that case the callback will be called with
+  // false. Note that the browser can terminate the worker at any time even if
+  // RequestTermination() is not called. For example, if the worker thread is
+  // continuously busy and the browser's periodic ping message has been missed,
+  // the browser will terminate the service worker.
+  RequestTermination() => (bool will_be_terminated);
+
+  // Tells the browser process that this service worker used |feature|, for
+  // UseCounter purposes. The browser process propagates the feature usage bit
+  // to all clients controlled by the service worker. See
+  // https://crbug.com/376039 for background.
+  // Note: Because CountFeature() is possible to be called on the main thread
+  // during service worker startup and is also called on the worker thread after
+  // that, we put it here rather than interface ServiceWorkerHost, so that we
+  // can still keep interface ServiceWorkerHost being used solely on the worker
+  // thread in the renderer process.
+  CountFeature(WebFeature feature);
+
+  // The following are called during startup:
+  //
+  // Indicates that the worker is ready for inspection. This message is needed
+  // because DevTools requires the shadow page to have been created before
+  // inspecting the worker.
+  OnReadyForInspection();
+  // Indicates that the worker has finished loading the main script.
+  //
+  // This is only called for new (non-installed) workers. It's used so the
+  // browser process knows it can resume the paused worker via
+  // ResumeAfterDownloaded().
+  OnScriptLoaded();
+  // Indicates that initial JavaScript evaluation is starting. This is useful
+  // for the browser process to start enforcing timeouts on script execution.
+  OnScriptEvaluationStart();
+  // Indicates that the worker has started. |thread_id| is the platform
+  // thread id the worker runs on.
+  OnStarted(ServiceWorkerStartStatus status,
+            int32 thread_id,
+            EmbeddedWorkerStartTiming start_timing);
+
+  // Reports that an uncaught exception occurred in the worker.
+  OnReportException(mojo_base.mojom.String16 error_message, int32 line_number,
+                    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,
+                         mojo_base.mojom.String16 message, int32 line_number,
+                         url.mojom.Url source_url);
+  // Indicates that the worker has stopped.
+  OnStopped();
+};
diff --git a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
index dcd55e3..63d23fd8 100644
--- a/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
+++ b/third_party/blink/public/mojom/service_worker/service_worker_provider.mojom
@@ -19,8 +19,9 @@
 
 // S13nServiceWorker:
 // Sent from the browser process to the renderer. Contains parameters for the
-// WebServiceWorkerNetworkProvider used for starting a shared worker.
-struct ServiceWorkerProviderInfoForSharedWorker {
+// WebServiceWorkerNetworkProvider used for starting a web worker (dedicated
+// worker or shared worker).
+struct ServiceWorkerProviderInfoForWorker {
   int32 provider_id;
   associated ServiceWorkerContainerHost host_ptr_info;
   associated ServiceWorkerContainer& client_request;
diff --git a/third_party/blink/public/mojom/v8_cache_options.mojom b/third_party/blink/public/mojom/v8_cache_options.mojom
new file mode 100644
index 0000000..e6d2f40
--- /dev/null
+++ b/third_party/blink/public/mojom/v8_cache_options.mojom
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module blink.mojom;
+
+// Cache options for V8.
+// TODO(leonhsl): Use this to replace blink::WebSettings::V8CacheOptions and
+// blink::V8CacheOptions.
+enum V8CacheOptions {
+  // Use whatever the current default is.
+  kDefault,
+
+  // V8 caching turned off.
+  kNone,
+
+  // Use code caching.
+  kCode,
+
+  // Generate the code cache in the first load.
+  kCodeWithoutHeatCheck,
+
+  // Generate the full code cache in the first load.
+  kFullCodeWithoutHeatCheck,
+};
diff --git a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
index 6a5a3ecd..e5de25e 100644
--- a/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
+++ b/third_party/blink/public/mojom/worker/shared_worker_factory.mojom
@@ -43,7 +43,7 @@
       // The info about the service worker host in the browser process that
       // provides support for this shared worker to be a service worker client.
       // Null when S13nServiceWorker is disabled.
-      ServiceWorkerProviderInfoForSharedWorker? service_worker_provider_info,
+      ServiceWorkerProviderInfoForWorker? service_worker_provider_info,
 
       // NetworkService:
       // The ID of the AppCacheHost in the browser process that serves resources
diff --git a/third_party/blink/public/platform/notification_data_conversions.h b/third_party/blink/public/platform/notification_data_conversions.h
new file mode 100644
index 0000000..96aa2fb
--- /dev/null
+++ b/third_party/blink/public/platform/notification_data_conversions.h
@@ -0,0 +1,19 @@
+// 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_PUBLIC_PLATFORM_NOTIFICATION_DATA_CONVERSIONS_H_
+#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_NOTIFICATION_DATA_CONVERSIONS_H_
+
+#include "third_party/blink/public/common/notifications/platform_notification_data.h"
+#include "third_party/blink/public/platform/modules/notifications/web_notification_data.h"
+
+namespace blink {
+
+// Converts blink::PlatformNotificationData to Blink WebNotificationData.
+BLINK_PLATFORM_EXPORT WebNotificationData
+ToWebNotificationData(const PlatformNotificationData& platform_data);
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_NOTIFICATION_DATA_CONVERSIONS_H_
diff --git a/third_party/blink/public/platform/scheduler/test/web_fake_thread_scheduler.h b/third_party/blink/public/platform/scheduler/test/web_fake_thread_scheduler.h
index 453890c..f8fa447 100644
--- a/third_party/blink/public/platform/scheduler/test/web_fake_thread_scheduler.h
+++ b/third_party/blink/public/platform/scheduler/test/web_fake_thread_scheduler.h
@@ -34,6 +34,10 @@
   void DidHandleInputEventOnCompositorThread(
       const WebInputEvent& web_input_event,
       InputEventState event_state) override;
+  void WillPostInputEventToMainThread(
+      WebInputEvent::Type web_input_event_type) override;
+  void WillHandleInputEventOnMainThread(
+      WebInputEvent::Type web_input_event_type) override;
   void DidHandleInputEventOnMainThread(const WebInputEvent& web_input_event,
                                        WebInputEventResult result) override;
   void DidAnimateForInputOnCompositorThread() override;
diff --git a/third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h b/third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h
index 016ee9d..9fc5cee 100644
--- a/third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h
+++ b/third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h
@@ -37,6 +37,8 @@
   MOCK_METHOD0(DidCommitFrameToCompositor, void());
   MOCK_METHOD2(DidHandleInputEventOnCompositorThread,
                void(const WebInputEvent&, InputEventState));
+  MOCK_METHOD1(WillPostInputEventToMainThread, void(WebInputEvent::Type));
+  MOCK_METHOD1(WillHandleInputEventOnMainThread, void(WebInputEvent::Type));
   MOCK_METHOD2(DidHandleInputEventOnMainThread,
                void(const WebInputEvent&, WebInputEventResult));
   MOCK_METHOD0(DidAnimateForInputOnCompositorThread, void());
diff --git a/third_party/blink/public/platform/scheduler/web_thread_scheduler.h b/third_party/blink/public/platform/scheduler/web_thread_scheduler.h
index c1485f8c..77378f8 100644
--- a/third_party/blink/public/platform/scheduler/web_thread_scheduler.h
+++ b/third_party/blink/public/platform/scheduler/web_thread_scheduler.h
@@ -15,6 +15,7 @@
 #include "third_party/blink/public/platform/scheduler/web_rail_mode_observer.h"
 #include "third_party/blink/public/platform/scheduler/web_render_widget_scheduling_state.h"
 #include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/public/platform/web_scoped_virtual_time_pauser.h"
 #include "v8/include/v8.h"
@@ -27,7 +28,6 @@
 
 namespace blink {
 class Thread;
-class WebInputEvent;
 }  // namespace blink
 
 namespace viz {
@@ -127,6 +127,17 @@
       const WebInputEvent& web_input_event,
       InputEventState event_state);
 
+  // Tells the scheduler that an input event of the given type is about to be
+  // posted to the main thread. Must be followed later by a call to
+  // WillHandleInputEventOnMainThread. Called by the compositor thread.
+  virtual void WillPostInputEventToMainThread(
+      WebInputEvent::Type web_input_event_type);
+
+  // Tells the scheduler the input event of the given type is about to be
+  // handled. Called on the main thread.
+  virtual void WillHandleInputEventOnMainThread(
+      WebInputEvent::Type web_input_event_type);
+
   // Tells the scheduler that the system processed an input event. Must be
   // called from the main thread.
   virtual void DidHandleInputEventOnMainThread(
diff --git a/third_party/blink/public/platform/web_client_hints_types.mojom b/third_party/blink/public/platform/web_client_hints_types.mojom
index c31602e..3a41a1c 100644
--- a/third_party/blink/public/platform/web_client_hints_types.mojom
+++ b/third_party/blink/public/platform/web_client_hints_types.mojom
@@ -23,6 +23,7 @@
   kRtt = 4,
   kDownlink = 5,
   kEct = 6,
+  kLang = 7,
 
   // Warning: Before adding a new client hint, read the warning at the top.
 };
diff --git a/third_party/blink/public/platform/web_content_settings_client.h b/third_party/blink/public/platform/web_content_settings_client.h
index 3fff8c71..b8721b53 100644
--- a/third_party/blink/public/platform/web_content_settings_client.h
+++ b/third_party/blink/public/platform/web_content_settings_client.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_CONTENT_SETTINGS_CLIENT_H_
 #define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_CONTENT_SETTINGS_CLIENT_H_
 
+#include <memory>
+
 #include "base/time/time.h"
 #include "third_party/blink/public/platform/web_client_hints_type.h"
 #include "third_party/blink/public/platform/web_content_setting_callbacks.h"
@@ -12,7 +14,6 @@
 namespace blink {
 
 class WebSecurityOrigin;
-class WebString;
 class WebURL;
 
 // This class provides the content settings information which tells
@@ -24,11 +25,7 @@
   virtual std::unique_ptr<WebContentSettingsClient> Clone() { return nullptr; }
 
   // Controls whether access to Web Databases is allowed for this frame.
-  virtual bool AllowDatabase(const WebString& name,
-                             const WebString& display_name,
-                             unsigned estimated_size) {
-    return true;
-  }
+  virtual bool AllowDatabase() { return true; }
 
   // Controls whether access to File System is allowed for this frame.
   virtual bool RequestFileSystemAccessSync() { return true; }
diff --git a/third_party/blink/public/platform/web_feature.mojom b/third_party/blink/public/platform/web_feature.mojom
index 7bb5f050..f01e5cdf 100644
--- a/third_party/blink/public/platform/web_feature.mojom
+++ b/third_party/blink/public/platform/web_feature.mojom
@@ -2214,6 +2214,8 @@
   kCSSValueAppearanceButtonForNonButtonRendered = 2774,
   kCSSValueAppearanceButtonForOthersRendered = 2775,
   kCustomCursorIntersectsViewport = 2776,
+  kClientHintsLang = 2777,
+  kLinkRelPreloadImageSrcset = 2778,
 
   // 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_memory_coordinator.h b/third_party/blink/public/platform/web_memory_coordinator.h
deleted file mode 100644
index 1c378f86..0000000
--- a/third_party/blink/public/platform/web_memory_coordinator.h
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEMORY_COORDINATOR_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEMORY_COORDINATOR_H_
-
-#include "third_party/blink/public/platform/web_common.h"
-#include "third_party/blink/public/platform/web_memory_pressure_level.h"
-#include "third_party/blink/public/platform/web_memory_state.h"
-
-namespace blink {
-
-class WebMemoryCoordinator {
- public:
-  // Called when a memory pressure notification is received.
-  // TODO(bashi): Deprecating. Remove this when MemoryPressureListener is
-  // gone.
-  BLINK_PLATFORM_EXPORT static void OnMemoryPressure(WebMemoryPressureLevel);
-
-  BLINK_PLATFORM_EXPORT static void OnMemoryStateChange(MemoryState);
-
-  BLINK_PLATFORM_EXPORT static void OnPurgeMemory();
-};
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/blink/public/platform/web_memory_pressure_listener.h b/third_party/blink/public/platform/web_memory_pressure_listener.h
new file mode 100644
index 0000000..11a1654
--- /dev/null
+++ b/third_party/blink/public/platform/web_memory_pressure_listener.h
@@ -0,0 +1,23 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEMORY_PRESSURE_LISTENER_H_
+#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEMORY_PRESSURE_LISTENER_H_
+
+#include "third_party/blink/public/platform/web_common.h"
+#include "third_party/blink/public/platform/web_memory_pressure_level.h"
+
+namespace blink {
+
+class WebMemoryPressureListener {
+ public:
+  // Called when a memory pressure notification is received.
+  BLINK_PLATFORM_EXPORT static void OnMemoryPressure(WebMemoryPressureLevel);
+
+  BLINK_PLATFORM_EXPORT static void OnPurgeMemory();
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEMORY_PRESSURE_LISTENER_H_
diff --git a/third_party/blink/public/platform/web_memory_state.h b/third_party/blink/public/platform/web_memory_state.h
deleted file mode 100644
index 53d79b7..0000000
--- a/third_party/blink/public/platform/web_memory_state.h
+++ /dev/null
@@ -1,20 +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_PUBLIC_PLATFORM_WEB_MEMORY_STATE_H_
-#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_WEB_MEMORY_STATE_H_
-
-namespace blink {
-
-// These numbers must correspond to base::MemoryState.
-enum class MemoryState {
-  UNKNOWN = -1,
-  NORMAL = 0,
-  THROTTLED = 1,
-  SUSPENDED = 2,
-};
-
-}  // namespace blink
-
-#endif
diff --git a/third_party/blink/public/platform/web_runtime_features.h b/third_party/blink/public/platform/web_runtime_features.h
index b5f20e6..80e6ed0c 100644
--- a/third_party/blink/public/platform/web_runtime_features.h
+++ b/third_party/blink/public/platform/web_runtime_features.h
@@ -142,6 +142,8 @@
       bool);
   BLINK_PLATFORM_EXPORT static void EnablePaymentApp(bool);
   BLINK_PLATFORM_EXPORT static void EnablePaymentRequest(bool);
+  BLINK_PLATFORM_EXPORT static void EnablePaymentRequestHasEnrolledInstrument(
+      bool);
   BLINK_PLATFORM_EXPORT static void EnablePermissionsAPI(bool);
   BLINK_PLATFORM_EXPORT static void EnablePictureInPicture(bool);
   BLINK_PLATFORM_EXPORT static void EnablePictureInPictureAPI(bool);
@@ -221,6 +223,9 @@
   BLINK_PLATFORM_EXPORT static void EnableMergeBlockingNonBlockingPools(bool);
   BLINK_PLATFORM_EXPORT static void EnableGetDisplayMedia(bool);
   BLINK_PLATFORM_EXPORT static void EnableForbidSyncXHRInPageDismissal(bool);
+  BLINK_PLATFORM_EXPORT static void EnableShadowDOMV0(bool);
+  BLINK_PLATFORM_EXPORT static void EnableCustomElementsV0(bool);
+  BLINK_PLATFORM_EXPORT static void EnableHTMLImports(bool);
 
  private:
   WebRuntimeFeatures();
diff --git a/third_party/blink/public/web/web_script_controller.h b/third_party/blink/public/web/web_script_controller.h
index 79d3721c..1943783 100644
--- a/third_party/blink/public/web/web_script_controller.h
+++ b/third_party/blink/public/web/web_script_controller.h
@@ -31,6 +31,8 @@
 #ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SCRIPT_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_PUBLIC_WEB_WEB_SCRIPT_CONTROLLER_H_
 
+#include <memory>
+
 #include "third_party/blink/public/platform/web_common.h"
 
 namespace v8 {
@@ -42,9 +44,8 @@
 class WebScriptController {
  public:
   // Registers a v8 extension to be available on webpages. Will only affect
-  // v8 contexts initialized after this call. Takes ownership of the
-  // v8::Extension object passed.
-  BLINK_EXPORT static void RegisterExtension(v8::Extension*);
+  // v8 contexts initialized after this call.
+  BLINK_EXPORT static void RegisterExtension(std::unique_ptr<v8::Extension>);
 
  private:
   WebScriptController() = delete;
diff --git a/third_party/blink/public/web/web_widget.h b/third_party/blink/public/web/web_widget.h
index 7064cd2..8b5d4ef 100644
--- a/third_party/blink/public/web/web_widget.h
+++ b/third_party/blink/public/web/web_widget.h
@@ -227,10 +227,6 @@
   // Returns true if the WebWidget created is of type WebFrameWidget.
   virtual bool IsWebFrameWidget() const { return false; }
 
-  // The WebLayerTreeView initialized on this WebWidgetClient will be going away
-  // and is no longer safe to access.
-  virtual void WillCloseLayerTreeView() {}
-
   // Calling WebWidgetClient::requestPointerLock() will result in one
   // return call to didAcquirePointerLock() or didNotAcquirePointerLock().
   virtual void DidAcquirePointerLock() {}
diff --git a/third_party/blink/renderer/bindings/core/v8/BUILD.gn b/third_party/blink/renderer/bindings/core/v8/BUILD.gn
index a367823..a339271 100644
--- a/third_party/blink/renderer/bindings/core/v8/BUILD.gn
+++ b/third_party/blink/renderer/bindings/core/v8/BUILD.gn
@@ -181,8 +181,9 @@
 ]
 
 generate_origin_trial_features("bindings_core_origin_trial_features") {
-  sources = core_idl_files + core_idl_with_modules_dependency_files +
-            core_global_constructors_generated_idl_files
+  sources =
+      core_idl_files + core_idl_with_modules_dependency_files +
+      core_global_constructors_generated_idl_files + core_dependency_idl_files
   component = "core"
   output_dir = bindings_core_output_dir + "/v8"
   deps = [
diff --git a/third_party/blink/renderer/bindings/core/v8/script_controller.cc b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
index 1ce14d0..3151eb6 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_controller.cc
+++ b/third_party/blink/renderer/bindings/core/v8/script_controller.cc
@@ -32,6 +32,9 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
 
+#include <memory>
+#include <utility>
+
 #include "third_party/blink/public/web/web_settings.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"
@@ -181,13 +184,14 @@
 
 }  // namespace
 
-void ScriptController::RegisterExtensionIfNeeded(v8::Extension* extension) {
+void ScriptController::RegisterExtensionIfNeeded(
+    std::unique_ptr<v8::Extension> extension) {
   for (const auto* extension_name : RegisteredExtensionNames()) {
     if (!strcmp(extension_name, extension->name()))
       return;
   }
   RegisteredExtensionNames().push_back(extension->name());
-  v8::RegisterExtension(extension);
+  v8::RegisterExtension(std::move(extension));
 }
 
 v8::ExtensionConfiguration ScriptController::ExtensionsFor(
diff --git a/third_party/blink/renderer/bindings/core/v8/script_controller.h b/third_party/blink/renderer/bindings/core/v8/script_controller.h
index 946948e0..d18a808 100644
--- a/third_party/blink/renderer/bindings/core/v8/script_controller.h
+++ b/third_party/blink/renderer/bindings/core/v8/script_controller.h
@@ -31,6 +31,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_CONTROLLER_H_
 #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_CONTROLLER_H_
 
+#include <memory>
+
 #include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_source_location_type.h"
 #include "third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h"
@@ -144,9 +146,8 @@
   void ClearForClose();
 
   // Registers a v8 extension to be available on webpages. Will only
-  // affect v8 contexts initialized after this call. Takes ownership of
-  // the v8::Extension object passed.
-  static void RegisterExtensionIfNeeded(v8::Extension*);
+  // affect v8 contexts initialized after this call.
+  static void RegisterExtensionIfNeeded(std::unique_ptr<v8::Extension>);
   static v8::ExtensionConfiguration ExtensionsFor(const ExecutionContext*);
 
  private:
diff --git a/third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.cc b/third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.cc
index 741b2289..25b324c 100644
--- a/third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.cc
+++ b/third_party/blink/renderer/bindings/core/v8/v8_gc_for_context_dispose.cc
@@ -35,7 +35,7 @@
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/bindings/v8_per_isolate_data.h"
 #include "third_party/blink/renderer/platform/histogram.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/time.h"
@@ -76,8 +76,8 @@
   // memory use and trigger a V8+Blink GC. However, on Android, if the frame
   // will not be reused, the process will likely to be killed soon so skip this.
   if (is_main_frame && frame_reuse_status == WindowProxy::kFrameWillBeReused &&
-      ((MemoryCoordinator::IsLowEndDevice() &&
-        MemoryCoordinator::IsCurrentlyLowMemory()) ||
+      ((MemoryPressureListenerRegistry::IsLowEndDevice() &&
+        MemoryPressureListenerRegistry::IsCurrentlyLowMemory()) ||
        force_page_navigation_gc_)) {
     size_t pre_gc_memory_usage = GetMemoryUsage();
     V8PerIsolateData::MainThreadIsolate()->MemoryPressureNotification(
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index de758912..e01fe47 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1866,7 +1866,6 @@
     "feature_policy/policy_test.cc",
     "fetch/blob_bytes_consumer_test.cc",
     "fetch/body_stream_buffer_test.cc",
-    "fetch/buffering_bytes_consumer_test.cc",
     "fetch/bytes_consumer_tee_test.cc",
     "fetch/bytes_consumer_test_util.cc",
     "fetch/bytes_consumer_test_util.h",
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index b4b12a1..d353f25 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -194,6 +194,7 @@
                     "frame/report.idl",
                     "frame/report_body.idl",
                     "frame/reporting_observer.idl",
+                    "frame/scheduling.idl",
                     "frame/test_report_body.idl",
                     "frame/user_activation.idl",
                     "frame/visual_viewport.idl",
@@ -543,6 +544,7 @@
                     "frame/navigator_id.idl",
                     "frame/navigator_language.idl",
                     "frame/navigator_on_line.idl",
+                    "frame/navigator_scheduling.idl",
                     "frame/navigator_user_activation.idl",
                     "frame/window_event_handlers.idl",
                     "frame/window_or_worker_global_scope.idl",
diff --git a/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc b/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc
index 90f7ecbe..944103d 100644
--- a/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc
+++ b/third_party/blink/renderer/core/css/resolver/css_to_style_map.cc
@@ -597,19 +597,14 @@
   image.SetFill(border_image_slice.Fill());
 }
 
-static BorderImageLength ToBorderImageLength(
-    CSSValue& value,
-    const CSSToLengthConversionData& conversion_data) {
+static BorderImageLength ToBorderImageLength(const StyleResolverState& state,
+                                             const CSSValue& value) {
   if (value.IsPrimitiveValue()) {
     const CSSPrimitiveValue& primitive_value = ToCSSPrimitiveValue(value);
     if (primitive_value.IsNumber())
       return primitive_value.GetDoubleValue();
-    if (primitive_value.IsPercentage())
-      return Length(primitive_value.GetDoubleValue(), kPercent);
-    return primitive_value.ComputeLength<Length>(conversion_data);
   }
-  DCHECK_EQ(ToCSSIdentifierValue(value).GetValueID(), CSSValueAuto);
-  return Length(kAuto);
+  return StyleBuilderConverter::ConvertLengthOrAuto(state, value);
 }
 
 BorderImageLengthBox CSSToStyleMap::MapNinePieceImageQuad(
@@ -619,13 +614,11 @@
     return BorderImageLengthBox(Length(kAuto));
 
   const CSSQuadValue& slices = ToCSSQuadValue(value);
-
   // Set up a border image length box to represent our image slices.
-  return BorderImageLengthBox(
-      ToBorderImageLength(*slices.Top(), state.CssToLengthConversionData()),
-      ToBorderImageLength(*slices.Right(), state.CssToLengthConversionData()),
-      ToBorderImageLength(*slices.Bottom(), state.CssToLengthConversionData()),
-      ToBorderImageLength(*slices.Left(), state.CssToLengthConversionData()));
+  return BorderImageLengthBox(ToBorderImageLength(state, *slices.Top()),
+                              ToBorderImageLength(state, *slices.Right()),
+                              ToBorderImageLength(state, *slices.Bottom()),
+                              ToBorderImageLength(state, *slices.Left()));
 }
 
 void CSSToStyleMap::MapNinePieceImageRepeat(StyleResolverState&,
diff --git a/third_party/blink/renderer/core/css/resolver/font_builder_test.cc b/third_party/blink/renderer/core/css/resolver/font_builder_test.cc
index 84e33569..9a35a8d3f 100644
--- a/third_party/blink/renderer/core/css/resolver/font_builder_test.cc
+++ b/third_party/blink/renderer/core/css/resolver/font_builder_test.cc
@@ -190,7 +190,7 @@
   b.SetLocale(LayoutLocale::Get("se"));
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     AllFields,
     FontBuilderAdditiveTest,
     testing::Values(
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.cc b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
index 35d24a77..0c25f499 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.cc
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.cc
@@ -80,6 +80,10 @@
   FinishCommitResolver(kDetach);
   FinishAcquireResolver(kDetach);
   CancelTimeoutTask();
+  // TODO(vmpstr): At this point we might not have |element_| anymore, so the
+  // counts might leak. We need to keep track of the document object directly on
+  // the display lock and use that instead of relying on |element_| to clean up
+  // locked counts.
   state_ = kUnlocked;
 
   if (element_ && element_->GetDocument().View())
@@ -166,9 +170,9 @@
 }
 
 ScriptPromise DisplayLockContext::commit(ScriptState* script_state) {
-  // Reject if we're unlocked.
+  // Resolve if we're already unlocked.
   if (state_ == kUnlocked)
-    return GetRejectedPromise(script_state);
+    return GetResolvedPromise(script_state);
 
   // If we're already committing then return the promise.
   if (state_ == kCommitting)
@@ -193,9 +197,9 @@
 }
 
 ScriptPromise DisplayLockContext::updateAndCommit(ScriptState* script_state) {
-  // Reject if we're unlocked.
+  // Resolve if we're already unlocked.
   if (state_ == kUnlocked)
-    return GetRejectedPromise(script_state);
+    return GetResolvedPromise(script_state);
 
   // If we're in a state where a co-operative update doesn't make sense (e.g. we
   // haven't acquired the lock, or we're already sync committing), then do
@@ -396,11 +400,9 @@
     state_ = kUnlocked;
     update_budget_.reset();
     CancelTimeoutTask();
+    // Note that we reject the update, but resolve the commit.
     FinishUpdateResolver(kReject);
-    // TODO(vmpstr): Should we resolve here? What's the path to unlocking an
-    // element without connecting it (i.e. acquire the lock, then change your
-    // mind).
-    FinishCommitResolver(kReject);
+    FinishCommitResolver(kResolve);
     return;
   }
 
@@ -602,7 +604,8 @@
     update_budget_.reset();
 
     if (commit_resolver_) {
-      FinishCommitResolver(kReject);
+      // We resolve the commit if we're not connected.
+      FinishCommitResolver(kResolve);
       CancelTimeoutTask();
       state_ = kUnlocked;
     } else {
@@ -637,15 +640,6 @@
   }
 }
 
-void DisplayLockContext::ElementWasDestroyed(Document& document) {
-  // Since the element is destroyed, this is the last chance we get to adjust
-  // our lock counts.
-  if (!IsSearchable())
-    document.RemoveActivationBlockingDisplayLock();
-  if (IsLocked())
-    document.RemoveLockedDisplayLock();
-}
-
 void DisplayLockContext::ScheduleAnimation() {
   DCHECK(element_);
   DCHECK(element_->isConnected());
diff --git a/third_party/blink/renderer/core/display_lock/display_lock_context.h b/third_party/blink/renderer/core/display_lock/display_lock_context.h
index f039914..2866014 100644
--- a/third_party/blink/renderer/core/display_lock/display_lock_context.h
+++ b/third_party/blink/renderer/core/display_lock/display_lock_context.h
@@ -142,11 +142,6 @@
   void WillStartLifecycleUpdate() override;
   void DidFinishLifecycleUpdate() override;
 
-  // Called when the element associated with this lock is destroyed. Note that
-  // because the lifetime of this lock is independent of the element, this can
-  // happen.
-  void ElementWasDestroyed(Document&);
-
  private:
   friend class DisplayLockContextTest;
   friend class DisplayLockSuspendedHandle;
diff --git a/third_party/blink/renderer/core/dom/document_test.cc b/third_party/blink/renderer/core/dom/document_test.cc
index 3ce1a80..121e447f 100644
--- a/third_party/blink/renderer/core/dom/document_test.cc
+++ b/third_party/blink/renderer/core/dom/document_test.cc
@@ -1070,7 +1070,7 @@
   }
 }
 
-INSTANTIATE_TEST_CASE_P(, IsolatedWorldCSPTest, testing::Values(true, false));
+INSTANTIATE_TEST_SUITE_P(, IsolatedWorldCSPTest, testing::Values(true, false));
 
 TEST_F(DocumentTest, CanExecuteScriptsWithSandboxAndIsolatedWorld) {
   constexpr SandboxFlags kSandboxMask = kSandboxScripts;
@@ -1274,7 +1274,7 @@
   EXPECT_EQ(std::get<2>(GetParam()), GetViewportFit());
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     All,
     ParameterizedViewportFitDocumentTest,
     testing::Values(
diff --git a/third_party/blink/renderer/core/dom/dom_exception.h b/third_party/blink/renderer/core/dom/dom_exception.h
index acc876f..a104369c 100644
--- a/third_party/blink/renderer/core/dom/dom_exception.h
+++ b/third_party/blink/renderer/core/dom/dom_exception.h
@@ -36,7 +36,7 @@
 
 namespace blink {
 
-class CORE_EXPORT DOMException final : public ScriptWrappable {
+class CORE_EXPORT DOMException : public ScriptWrappable {
   DEFINE_WRAPPERTYPEINFO();
 
  public:
diff --git a/third_party/blink/renderer/core/dom/element.cc b/third_party/blink/renderer/core/dom/element.cc
index 81cf0e0..2da7d9bc8 100644
--- a/third_party/blink/renderer/core/dom/element.cc
+++ b/third_party/blink/renderer/core/dom/element.cc
@@ -223,11 +223,6 @@
                  ConstructionType type)
     : ContainerNode(document, type), tag_name_(tag_name) {}
 
-Element::~Element() {
-  if (auto* context = GetDisplayLockContext())
-    context->ElementWasDestroyed(GetDocument());
-}
-
 inline ElementRareData* Element::GetElementRareData() const {
   DCHECK(HasRareData());
   return static_cast<ElementRareData*>(RareData());
diff --git a/third_party/blink/renderer/core/dom/element.h b/third_party/blink/renderer/core/dom/element.h
index 133d569..7f0cd85 100644
--- a/third_party/blink/renderer/core/dom/element.h
+++ b/third_party/blink/renderer/core/dom/element.h
@@ -161,7 +161,6 @@
   static Element* Create(const QualifiedName&, Document*);
 
   Element(const QualifiedName& tag_name, Document*, ConstructionType);
-  ~Element() override;
 
   DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecopy, kBeforecopy);
   DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecut, kBeforecut);
diff --git a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
index 9db9334..6d4de7a 100644
--- a/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
+++ b/third_party/blink/renderer/core/editing/caret_display_item_client_test.cc
@@ -72,7 +72,7 @@
   }
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(CaretDisplayItemClientTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(CaretDisplayItemClientTest);
 
 TEST_P(CaretDisplayItemClientTest, CaretPaintInvalidation) {
   GetDocument().body()->setContentEditable("true", ASSERT_NO_EXCEPTION);
@@ -380,9 +380,9 @@
   ParameterizedComputeCaretRectTest() : ScopedLayoutNGForTest(GetParam()) {}
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        ParameterizedComputeCaretRectTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         ParameterizedComputeCaretRectTest,
+                         testing::Bool());
 
 TEST_P(ParameterizedComputeCaretRectTest, CaretRectAfterEllipsisNoCrash) {
   SetBodyInnerHTML(
diff --git a/third_party/blink/renderer/core/editing/editor.cc b/third_party/blink/renderer/core/editing/editor.cc
index 6ee4910..e104f4a 100644
--- a/third_party/blink/renderer/core/editing/editor.cc
+++ b/third_party/blink/renderer/core/editing/editor.cc
@@ -54,6 +54,7 @@
 #include "third_party/blink/renderer/core/editing/editing_tri_state.h"
 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
+#include "third_party/blink/renderer/core/editing/finder/find_buffer.h"
 #include "third_party/blink/renderer/core/editing/frame_selection.h"
 #include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
 #include "third_party/blink/renderer/core/editing/iterators/search_buffer.h"
@@ -746,15 +747,15 @@
 bool Editor::FindString(LocalFrame& frame,
                         const String& target,
                         FindOptions options) {
-  VisibleSelection selection =
-      frame.Selection().ComputeVisibleSelectionInDOMTreeDeprecated();
+  VisibleSelectionInFlatTree selection =
+      frame.Selection().ComputeVisibleSelectionInFlatTree();
 
   // TODO(yosin) We should make |findRangeOfString()| to return
   // |EphemeralRange| rather than|Range| object.
-  Range* const result_range =
-      FindRangeOfString(*frame.GetDocument(), target,
-                        EphemeralRange(selection.Start(), selection.End()),
-                        static_cast<FindOptions>(options | kFindAPICall));
+  Range* const result_range = FindRangeOfString(
+      *frame.GetDocument(), target,
+      EphemeralRangeInFlatTree(selection.Start(), selection.End()),
+      static_cast<FindOptions>(options | kFindAPICall));
 
   if (!result_range)
     return false;
@@ -770,18 +771,17 @@
 // TODO(yosin) We should return |EphemeralRange| rather than |Range|. We use
 // |Range| object for checking whether start and end position crossing shadow
 // boundaries, however we can do it without |Range| object.
-template <typename Strategy>
 static Range* FindStringBetweenPositions(
     const String& target,
-    const EphemeralRangeTemplate<Strategy>& reference_range,
+    const EphemeralRangeInFlatTree& reference_range,
     FindOptions options) {
-  EphemeralRangeTemplate<Strategy> search_range(reference_range);
+  EphemeralRangeInFlatTree search_range(reference_range);
 
   bool forward = !(options & kBackwards);
 
   while (true) {
-    EphemeralRangeTemplate<Strategy> result_range =
-        FindPlainText(search_range, target, options);
+    EphemeralRangeInFlatTree result_range =
+        FindBuffer::FindMatchInRange(search_range, target, options);
     if (result_range.IsCollapsed())
       return nullptr;
 
@@ -797,12 +797,12 @@
     // next occurrence.
     // TODO(yosin) Handle this case.
     if (forward) {
-      search_range = EphemeralRangeTemplate<Strategy>(
+      search_range = EphemeralRangeInFlatTree(
           NextPositionOf(result_range.StartPosition(),
                          PositionMoveType::kGraphemeCluster),
           search_range.EndPosition());
     } else {
-      search_range = EphemeralRangeTemplate<Strategy>(
+      search_range = EphemeralRangeInFlatTree(
           search_range.StartPosition(),
           PreviousPositionOf(result_range.EndPosition(),
                              PositionMoveType::kGraphemeCluster));
@@ -813,11 +813,10 @@
   return nullptr;
 }
 
-template <typename Strategy>
-static Range* FindRangeOfStringAlgorithm(
+Range* Editor::FindRangeOfString(
     Document& document,
     const String& target,
-    const EphemeralRangeTemplate<Strategy>& reference_range,
+    const EphemeralRangeInFlatTree& reference_range,
     FindOptions options) {
   if (target.IsEmpty())
     return nullptr;
@@ -825,26 +824,27 @@
   // Start from an edge of the reference range. Which edge is used depends on
   // whether we're searching forward or backward, and whether startInSelection
   // is set.
-  EphemeralRangeTemplate<Strategy> document_range =
-      EphemeralRangeTemplate<Strategy>::RangeOfContents(document);
-  EphemeralRangeTemplate<Strategy> search_range(document_range);
+  EphemeralRangeInFlatTree document_range =
+      EphemeralRangeInFlatTree::RangeOfContents(document);
+  EphemeralRangeInFlatTree search_range(document_range);
 
   bool forward = !(options & kBackwards);
   bool start_in_reference_range = false;
   if (reference_range.IsNotNull()) {
     start_in_reference_range = options & kStartInSelection;
-    if (forward && start_in_reference_range)
-      search_range = EphemeralRangeTemplate<Strategy>(
-          reference_range.StartPosition(), document_range.EndPosition());
-    else if (forward)
-      search_range = EphemeralRangeTemplate<Strategy>(
-          reference_range.EndPosition(), document_range.EndPosition());
-    else if (start_in_reference_range)
-      search_range = EphemeralRangeTemplate<Strategy>(
-          document_range.StartPosition(), reference_range.EndPosition());
-    else
-      search_range = EphemeralRangeTemplate<Strategy>(
-          document_range.StartPosition(), reference_range.StartPosition());
+    if (forward && start_in_reference_range) {
+      search_range = EphemeralRangeInFlatTree(reference_range.StartPosition(),
+                                              document_range.EndPosition());
+    } else if (forward) {
+      search_range = EphemeralRangeInFlatTree(reference_range.EndPosition(),
+                                              document_range.EndPosition());
+    } else if (start_in_reference_range) {
+      search_range = EphemeralRangeInFlatTree(document_range.StartPosition(),
+                                              reference_range.EndPosition());
+    } else {
+      search_range = EphemeralRangeInFlatTree(document_range.StartPosition(),
+                                              reference_range.StartPosition());
+    }
   }
 
   Range* result_range =
@@ -855,16 +855,16 @@
   // to remove collapsed whitespace. Compare ranges instead of selection
   // objects to ignore the way that the current selection was made.
   if (result_range && start_in_reference_range &&
-      NormalizeRange(EphemeralRangeTemplate<Strategy>(result_range)) ==
+      NormalizeRange(EphemeralRangeInFlatTree(result_range)) ==
           reference_range) {
     if (forward)
-      search_range = EphemeralRangeTemplate<Strategy>(
-          FromPositionInDOMTree<Strategy>(result_range->EndPosition()),
+      search_range = EphemeralRangeInFlatTree(
+          ToPositionInFlatTree(result_range->EndPosition()),
           search_range.EndPosition());
     else
-      search_range = EphemeralRangeTemplate<Strategy>(
+      search_range = EphemeralRangeInFlatTree(
           search_range.StartPosition(),
-          FromPositionInDOMTree<Strategy>(result_range->StartPosition()));
+          ToPositionInFlatTree(result_range->StartPosition()));
     result_range = FindStringBetweenPositions(target, search_range, options);
   }
 
@@ -874,22 +874,6 @@
   return result_range;
 }
 
-Range* Editor::FindRangeOfString(Document& document,
-                                 const String& target,
-                                 const EphemeralRange& reference,
-                                 FindOptions options) {
-  return FindRangeOfStringAlgorithm<EditingStrategy>(document, target,
-                                                     reference, options);
-}
-
-Range* Editor::FindRangeOfString(Document& document,
-                                 const String& target,
-                                 const EphemeralRangeInFlatTree& reference,
-                                 FindOptions options) {
-  return FindRangeOfStringAlgorithm<EditingInFlatTreeStrategy>(
-      document, target, reference, options);
-}
-
 void Editor::SetMarkedTextMatchesAreHighlighted(bool flag) {
   if (flag == are_marked_text_matches_highlighted_)
     return;
diff --git a/third_party/blink/renderer/core/editing/editor.h b/third_party/blink/renderer/core/editing/editor.h
index b6f3c97..7780440 100644
--- a/third_party/blink/renderer/core/editing/editor.h
+++ b/third_party/blink/renderer/core/editing/editor.h
@@ -162,10 +162,6 @@
 
   static bool FindString(LocalFrame&, const String&, FindOptions);
 
-  static Range* FindRangeOfString(Document&,
-                                  const String& target,
-                                  const EphemeralRange& reference_range,
-                                  FindOptions);
   static Range* FindRangeOfString(
       Document&,
       const String& target,
diff --git a/third_party/blink/renderer/core/editing/element_inner_text_test.cc b/third_party/blink/renderer/core/editing/element_inner_text_test.cc
index 1d98598a..296fed3 100644
--- a/third_party/blink/renderer/core/editing/element_inner_text_test.cc
+++ b/third_party/blink/renderer/core/editing/element_inner_text_test.cc
@@ -18,7 +18,7 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All, ElementInnerTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, ElementInnerTest, testing::Bool());
 
 // http://crbug.com/877498
 TEST_P(ElementInnerTest, ListItemWithLeadingWhiteSpace) {
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 3208ecbf..be91e7f 100644
--- a/third_party/blink/renderer/core/editing/finder/find_buffer.cc
+++ b/third_party/blink/renderer/core/editing/finder/find_buffer.cc
@@ -24,7 +24,7 @@
 namespace blink {
 
 FindBuffer::FindBuffer(const EphemeralRangeInFlatTree& range) {
-  DCHECK(range.IsNotNull());
+  DCHECK(range.IsNotNull() && !range.IsCollapsed()) << range;
   CollectTextUntilBlockBoundary(range);
 }
 
@@ -71,6 +71,22 @@
   return Iterator();
 }
 
+bool FindBuffer::Results::IsEmpty() {
+  return begin() == end();
+}
+
+FindBuffer::BufferMatchResult FindBuffer::Results::front() {
+  return *begin();
+}
+
+FindBuffer::BufferMatchResult FindBuffer::Results::back() {
+  Iterator last_result;
+  for (Iterator it = begin(); it != end(); ++it) {
+    last_result = it;
+  }
+  return *last_result;
+}
+
 unsigned FindBuffer::Results::CountForTesting() {
   unsigned result = 0;
   for (Iterator it = begin(); it != end(); ++it) {
@@ -125,10 +141,11 @@
           !element.GetDisplayLockContext()->IsSearchable());
 }
 
-Node* GetDisplayNoneAncestor(const Node& node) {
+Node* GetNonSearchableAncestor(const Node& node) {
   for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(node)) {
     const ComputedStyle* style = ancestor.EnsureComputedStyle();
-    if (style && style->Display() == EDisplay::kNone)
+    if ((style && style->Display() == EDisplay::kNone) ||
+        ShouldIgnoreContents(node))
       return &ancestor;
     if (ancestor.IsDocumentNode())
       return nullptr;
@@ -145,7 +162,7 @@
 Node* GetVisibleTextNode(Node& start_node) {
   Node* node = &start_node;
   // Move to outside display none subtree if we're inside one.
-  while (Node* ancestor = GetDisplayNoneAncestor(*node)) {
+  while (Node* ancestor = GetNonSearchableAncestor(*node)) {
     if (ancestor->IsDocumentNode())
       return nullptr;
     node = FlatTreeTraversal::NextSkippingChildren(*ancestor);
@@ -185,6 +202,53 @@
   return *start_node.GetDocument().documentElement();
 }
 
+EphemeralRangeInFlatTree FindBuffer::FindMatchInRange(
+    const EphemeralRangeInFlatTree& range,
+    String search_text,
+    FindOptions options) {
+  if (!range.StartPosition().IsConnected())
+    return EphemeralRangeInFlatTree();
+
+  EphemeralRangeInFlatTree last_match_range;
+  Node* first_node = range.StartPosition().NodeAsRangeFirstNode();
+  Node* past_last_node = range.EndPosition().NodeAsRangePastLastNode();
+  Node* node = first_node;
+  while (node && node != past_last_node) {
+    if (GetNonSearchableAncestor(*node)) {
+      node = FlatTreeTraversal::NextSkippingChildren(*node);
+      continue;
+    }
+    if (!node->IsTextNode()) {
+      node = FlatTreeTraversal::Next(*node);
+      continue;
+    }
+    // If we're in the same node as the start position, start from the start
+    // position instead of the start of this node.
+    PositionInFlatTree start_position =
+        node == first_node ? range.StartPosition()
+                           : PositionInFlatTree::FirstPositionInNode(*node);
+    if (start_position >= range.EndPosition())
+      break;
+
+    FindBuffer buffer(
+        EphemeralRangeInFlatTree(start_position, range.EndPosition()));
+    std::unique_ptr<Results> match_results =
+        buffer.FindMatches(search_text, options);
+    if (!match_results->IsEmpty()) {
+      if (!(options & kBackwards)) {
+        BufferMatchResult match = match_results->front();
+        return buffer.RangeFromBufferIndex(match.start,
+                                           match.start + match.length);
+      }
+      BufferMatchResult match = match_results->back();
+      last_match_range =
+          buffer.RangeFromBufferIndex(match.start, match.start + match.length);
+    }
+    node = buffer.PositionAfterBlock().ComputeContainerNode();
+  }
+  return last_match_range;
+}
+
 std::unique_ptr<FindBuffer::Results> FindBuffer::FindMatches(
     const WebString& search_text,
     const blink::FindOptions options) const {
@@ -201,6 +265,7 @@
 // |node_after_block_|.
 void FindBuffer::CollectTextUntilBlockBoundary(
     const EphemeralRangeInFlatTree& range) {
+  DCHECK(range.IsNotNull() && !range.IsCollapsed()) << range;
   // Get first visible text node from |start_position|.
   Node* node =
       GetVisibleTextNode(*range.StartPosition().NodeAsRangeFirstNode());
@@ -230,7 +295,8 @@
     if (ShouldIgnoreContents(*node)) {
       if (end_node && (end_node == node ||
                        FlatTreeTraversal::IsDescendantOf(*end_node, *node))) {
-        node = nullptr;
+        // For setting |node_after_block| later.
+        node = FlatTreeTraversal::NextSkippingChildren(*node);
         break;
       }
       // Move the node so we wouldn't encounter this node or its descendants
@@ -259,7 +325,8 @@
       // we guarantee |block_ancestor| is visible.
       if (end_node && (end_node == node ||
                        FlatTreeTraversal::IsDescendantOf(*end_node, *node))) {
-        node = nullptr;
+        // For setting |node_after_block| later.
+        node = FlatTreeTraversal::NextSkippingChildren(*node);
         break;
       }
       node = FlatTreeTraversal::NextSkippingChildren(*node);
@@ -290,7 +357,7 @@
       AddTextToBuffer(text_node, block_flow, range);
     }
     if (node == end_node) {
-      node = nullptr;
+      node = FlatTreeTraversal::Next(*node);
       break;
     }
     node = FlatTreeTraversal::Next(*node);
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer.h b/third_party/blink/renderer/core/editing/finder/find_buffer.h
index c2c184e..1d6ed9a 100644
--- a/third_party/blink/renderer/core/editing/finder/find_buffer.h
+++ b/third_party/blink/renderer/core/editing/finder/find_buffer.h
@@ -5,7 +5,6 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_FIND_BUFFER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_EDITING_FINDER_FIND_BUFFER_H_
 
-#include "third_party/blink/public/mojom/frame/find_in_page.mojom-blink.h"
 #include "third_party/blink/renderer/core/editing/finder/find_options.h"
 #include "third_party/blink/renderer/core/editing/iterators/text_searcher_icu.h"
 #include "third_party/blink/renderer/core/layout/ng/inline/ng_inline_node.h"
@@ -23,7 +22,12 @@
   STACK_ALLOCATED();
 
  public:
-  FindBuffer(const EphemeralRangeInFlatTree& range);
+  explicit FindBuffer(const EphemeralRangeInFlatTree& range);
+
+  static EphemeralRangeInFlatTree FindMatchInRange(
+      const EphemeralRangeInFlatTree& range,
+      String search_text,
+      const FindOptions);
 
   // A match result, containing the starting position of the match and
   // the length of the match.
@@ -70,6 +74,12 @@
 
     Iterator end() const;
 
+    bool IsEmpty();
+
+    BufferMatchResult front();
+
+    BufferMatchResult back();
+
     unsigned CountForTesting();
 
    private:
diff --git a/third_party/blink/renderer/core/editing/finder/find_buffer_test.cc b/third_party/blink/renderer/core/editing/finder/find_buffer_test.cc
index 4776890..6c5798aa 100644
--- a/third_party/blink/renderer/core/editing/finder/find_buffer_test.cc
+++ b/third_party/blink/renderer/core/editing/finder/find_buffer_test.cc
@@ -264,17 +264,93 @@
   EXPECT_EQ(0u, buffer.FindMatches("f", kCaseInsensitive)->CountForTesting());
 }
 
+TEST_F(FindBufferTest, FindMatchInRange) {
+  SetBodyContent(
+      "<div id='div' invisible>foo<a id='a'>foof</a><b id='b'>oo</b></div>");
+  Element* div = GetElementById("div");
+  Element* a = GetElementById("a");
+  Element* b = GetElementById("b");
+  EphemeralRangeInFlatTree foo1 = EphemeralRangeInFlatTree(
+      PositionInFlatTree::FirstPositionInNode(*div->firstChild()),
+      PositionInFlatTree::LastPositionInNode(*div->firstChild()));
+  EphemeralRangeInFlatTree foo2 = EphemeralRangeInFlatTree(
+      PositionInFlatTree::FirstPositionInNode(*a->firstChild()),
+      PositionInFlatTree(*a->firstChild(), 3));
+  EphemeralRangeInFlatTree foo3 = EphemeralRangeInFlatTree(
+      PositionInFlatTree(*a->firstChild(), 3),
+      PositionInFlatTree::LastPositionInNode(*b->firstChild()));
+
+  // <div>^foo<a>foof</a><b>oo|</b></div>, forwards
+  EphemeralRangeInFlatTree match = FindBuffer::FindMatchInRange(
+      WholeDocumentRange(), "foo", kCaseInsensitive);
+  EXPECT_EQ(foo1, match);
+  // <div>f^oo<a>foof</a><b>oo|</b></div>, forwards
+  match = FindBuffer::FindMatchInRange(
+      EphemeralRangeInFlatTree(PositionInFlatTree(*div->firstChild(), 1),
+                               LastPositionInDocument()),
+      "foo", kCaseInsensitive);
+  EXPECT_EQ(foo2, match);
+  // <div>foo<a>^foo|f</a><b>oo</b></div>, forwards
+  match = FindBuffer::FindMatchInRange(foo2, "foo", kCaseInsensitive);
+  EXPECT_EQ(foo2, match);
+  // <div>foo<a>f^oof|</a><b>oo</b></div>, forwards
+  match = FindBuffer::FindMatchInRange(
+      EphemeralRangeInFlatTree(
+          PositionInFlatTree(*a->firstChild(), 1),
+          PositionInFlatTree::LastPositionInNode(*a->firstChild())),
+      "foo", kCaseInsensitive);
+  EXPECT_TRUE(match.IsNull());
+  // <div>foo<a>f^oof</a><b>oo|</b></div>, forwards
+  match = FindBuffer::FindMatchInRange(
+      EphemeralRangeInFlatTree(PositionInFlatTree(*a->firstChild(), 1),
+                               LastPositionInDocument()),
+      "foo", kCaseInsensitive);
+  EXPECT_EQ(foo3, match);
+
+  // <div>^foo<a>foof</a><b>oo|</b></div>, backwards
+  match = FindBuffer::FindMatchInRange(WholeDocumentRange(), "foo",
+                                       kCaseInsensitive | kBackwards);
+  EXPECT_EQ(foo3, match);
+  // <div>^foo<a>foof</a><b>o|o</b></div>, backwards
+  match = FindBuffer::FindMatchInRange(
+      EphemeralRangeInFlatTree(
+          PositionInFlatTree::FirstPositionInNode(*div->firstChild()),
+          PositionInFlatTree(*b->firstChild(), 1)),
+      "foo", kCaseInsensitive | kBackwards);
+  EXPECT_EQ(foo2, match);
+  // <div>foo<a>^foof</a><b>o|o</b></div>, backwards
+  match = FindBuffer::FindMatchInRange(
+      EphemeralRangeInFlatTree(
+          PositionInFlatTree::FirstPositionInNode(*a->firstChild()),
+          PositionInFlatTree(*b->firstChild(), 1)),
+      "foo", kCaseInsensitive | kBackwards);
+  EXPECT_EQ(foo2, match);
+  // <div>foo<a>foo^f</a><b>o|o</b></div>, backwards
+  match = FindBuffer::FindMatchInRange(
+      EphemeralRangeInFlatTree(PositionInFlatTree(*a->firstChild(), 3),
+                               PositionInFlatTree(*b->firstChild(), 1)),
+      "foo", kCaseInsensitive | kBackwards);
+  EXPECT_TRUE(match.IsNull());
+  // <div>^foo<a>fo|of</a><b>oo</b></div>, backwards
+  match = FindBuffer::FindMatchInRange(
+      EphemeralRangeInFlatTree(
+          PositionInFlatTree::FirstPositionInNode(*div->firstChild()),
+          PositionInFlatTree(*a->firstChild(), 2)),
+      "foo", kCaseInsensitive | kBackwards);
+  EXPECT_EQ(foo1, match);
+}
+
 class FindBufferBlockTest : public FindBufferTest,
                             public testing::WithParamInterface<std::string> {};
 
-INSTANTIATE_TEST_CASE_P(Blocks,
-                        FindBufferBlockTest,
-                        testing::Values("block",
-                                        "table",
-                                        "flow-root",
-                                        "grid",
-                                        "flex",
-                                        "list-item"));
+INSTANTIATE_TEST_SUITE_P(Blocks,
+                         FindBufferBlockTest,
+                         testing::Values("block",
+                                         "table",
+                                         "flow-root",
+                                         "grid",
+                                         "flex",
+                                         "list-item"));
 
 TEST_P(FindBufferBlockTest, FindBlock) {
   SetBodyContent("text<div id='block' style='display: " + GetParam() +
@@ -320,16 +396,16 @@
     : public FindBufferTest,
       public testing::WithParamInterface<std::string> {};
 
-INSTANTIATE_TEST_CASE_P(Separators,
-                        FindBufferSeparatorTest,
-                        testing::Values("br",
-                                        "hr",
-                                        "legend",
-                                        "meter",
-                                        "object",
-                                        "progress",
-                                        "select",
-                                        "video"));
+INSTANTIATE_TEST_SUITE_P(Separators,
+                         FindBufferSeparatorTest,
+                         testing::Values("br",
+                                         "hr",
+                                         "legend",
+                                         "meter",
+                                         "object",
+                                         "progress",
+                                         "select",
+                                         "video"));
 
 TEST_P(FindBufferSeparatorTest, FindSeparatedElements) {
   SetBodyContent("a<" + GetParam() + ">a</" + GetParam() + ">a");
diff --git a/third_party/blink/renderer/core/editing/finder/find_in_page_coordinates.cc b/third_party/blink/renderer/core/editing/finder/find_in_page_coordinates.cc
index 76ecb45..c334cd8 100644
--- a/third_party/blink/renderer/core/editing/finder/find_in_page_coordinates.cc
+++ b/third_party/blink/renderer/core/editing/finder/find_in_page_coordinates.cc
@@ -33,6 +33,7 @@
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/dom/range.h"
 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
+#include "third_party/blink/renderer/core/editing/visible_units.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/layout/layout_block.h"
@@ -147,8 +148,8 @@
   if (!baseLayoutObject)
     return FloatRect();
 
-  return FindInPageRectFromAbsoluteRect(
-      LayoutObject::AbsoluteBoundingBoxRectForRange(range), baseLayoutObject);
+  return FindInPageRectFromAbsoluteRect(ComputeTextFloatRect(range),
+                                        baseLayoutObject);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/finder/find_task_controller.cc b/third_party/blink/renderer/core/editing/finder/find_task_controller.cc
index 81e1f95..9d66cc1 100644
--- a/third_party/blink/renderer/core/editing/finder/find_task_controller.cc
+++ b/third_party/blink/renderer/core/editing/finder/find_task_controller.cc
@@ -119,7 +119,7 @@
         (options_->match_case ? 0 : kCaseInsensitive) |
         (options_->find_next ? 0 : kStartInSelection);
 
-    do {
+    while (search_start != search_end) {
       // Find in the whole block.
       FindBuffer buffer(EphemeralRangeInFlatTree(search_start, search_end));
       std::unique_ptr<FindBuffer::Results> match_results =
@@ -150,7 +150,9 @@
         break;
       }
       next_task_start_position = search_start;
-    } while (deadline->timeRemaining() > 0);
+      if (deadline->timeRemaining() <= 0)
+        break;
+    }
 
     const TimeDelta time_spent = CurrentTimeTicks() - start_time;
     UMA_HISTOGRAM_TIMES("WebCore.FindInPage.ScopingTime",
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 1f9438e7..2f29bab9 100644
--- a/third_party/blink/renderer/core/editing/finder/text_finder.cc
+++ b/third_party/blink/renderer/core/editing/finder/text_finder.cc
@@ -54,6 +54,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
+#include "third_party/blink/renderer/core/invisible_dom/invisible_dom.h"
 #include "third_party/blink/renderer/core/layout/layout_object.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
 #include "third_party/blink/renderer/core/layout/text_autosizer.h"
@@ -73,6 +74,8 @@
 
 static void ScrollToVisible(Range* match) {
   const Node& first_node = *match->FirstNode();
+  if (InvisibleDOM::ActivateRangeIfNeeded(EphemeralRangeInFlatTree(match)))
+    first_node.GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheets();
   Settings* settings = first_node.GetDocument().GetSettings();
   bool smooth_find_enabled =
       settings ? settings->GetSmoothScrollForFindEnabled() : false;
@@ -156,8 +159,7 @@
           ->PageNeedsAutosizing()) {
     OwnerFrame().LocalRoot()->FrameWidget()->ZoomToFindInPageRect(
         OwnerFrame().GetFrameView()->ConvertToRootFrame(
-            EnclosingIntRect(LayoutObject::AbsoluteBoundingBoxRectForRange(
-                EphemeralRange(active_match_.Get())))));
+            ComputeTextRect(EphemeralRange(active_match_.Get()))));
   }
 
   bool was_active_frame = current_active_match_frame_;
@@ -607,8 +609,7 @@
 
   IntRect active_match_rect;
   IntRect active_match_bounding_box =
-      EnclosingIntRect(LayoutObject::AbsoluteBoundingBoxRectForRange(
-          EphemeralRange(active_match_.Get())));
+      ComputeTextRect(EphemeralRange(active_match_.Get()));
 
   if (!active_match_bounding_box.IsEmpty()) {
     if (active_match_->FirstNode() &&
@@ -628,8 +629,7 @@
       // that needs to be merged to a release branch.
       // https://crbug.com/823365.
       active_match_bounding_box =
-          EnclosingIntRect(LayoutObject::AbsoluteBoundingBoxRectForRange(
-              EphemeralRange(active_match_.Get())));
+          ComputeTextRect(EphemeralRange(active_match_.Get()));
     }
 
     // Zoom to the active match.
diff --git a/third_party/blink/renderer/core/editing/frame_selection.cc b/third_party/blink/renderer/core/editing/frame_selection.cc
index 7db9e58..3bb14d7 100644
--- a/third_party/blink/renderer/core/editing/frame_selection.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection.cc
@@ -953,6 +953,7 @@
                      frame_->GetSettings() &&
                      frame_->GetSettings()->GetSelectionIncludesAltImageText())
                  .SetSkipsUnselectableContent(true)
+                 .SetEntersTextControls(true)
                  .Build());
 }
 
diff --git a/third_party/blink/renderer/core/editing/frame_selection_test.cc b/third_party/blink/renderer/core/editing/frame_selection_test.cc
index c1a709ca..1f80860 100644
--- a/third_party/blink/renderer/core/editing/frame_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/frame_selection_test.cc
@@ -1091,4 +1091,14 @@
   EXPECT_TRUE(Selection().Contains(f_left));
 }
 
+// This is a regression test for https://crbug.com/927394 where 'copy' operation
+// stopped copying content from inside text controls.
+// Note that this is a non-standard behavior.
+TEST_F(FrameSelectionTest, SelectedTextForClipboardEntersTextControls) {
+  Selection().SetSelection(
+      SetSelectionTextToBody("^foo<input value=\"bar\">baz|"),
+      SetSelectionOptions());
+  EXPECT_EQ("foo\nbar\nbaz", Selection().SelectedTextForClipboard());
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/hit_testing_bidi_test.cc b/third_party/blink/renderer/core/editing/hit_testing_bidi_test.cc
index c4b75c44..73e3e9a 100644
--- a/third_party/blink/renderer/core/editing/hit_testing_bidi_test.cc
+++ b/third_party/blink/renderer/core/editing/hit_testing_bidi_test.cc
@@ -26,7 +26,7 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All, ParameterizedHitTestingBidiTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, ParameterizedHitTestingBidiTest, testing::Bool());
 
 // This file contains script-generated tests for PositionForPoint()
 // that are related to bidirectional text. The test cases are only for
diff --git a/third_party/blink/renderer/core/editing/iterators/character_iterator_test.cc b/third_party/blink/renderer/core/editing/iterators/character_iterator_test.cc
index e02baf9..897fc6d 100644
--- a/third_party/blink/renderer/core/editing/iterators/character_iterator_test.cc
+++ b/third_party/blink/renderer/core/editing/iterators/character_iterator_test.cc
@@ -50,9 +50,9 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        ParameterizedCharacterIteratorTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         ParameterizedCharacterIteratorTest,
+                         testing::Bool());
 
 TEST_P(ParameterizedCharacterIteratorTest, SubrangeWithReplacedElements) {
   static const char* body_content =
diff --git a/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc b/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc
index e000dcc..f204ca6 100644
--- a/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc
+++ b/third_party/blink/renderer/core/editing/iterators/text_iterator_test.cc
@@ -152,7 +152,7 @@
   return range;
 }
 
-INSTANTIATE_TEST_CASE_P(All, TextIteratorTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, TextIteratorTest, testing::Bool());
 
 TEST_P(TextIteratorTest, BitStackOverflow) {
   const unsigned kBitsInWord = sizeof(unsigned) * 8;
diff --git a/third_party/blink/renderer/core/editing/layout_selection_test.cc b/third_party/blink/renderer/core/editing/layout_selection_test.cc
index 8b058d1..fb78d31 100644
--- a/third_party/blink/renderer/core/editing/layout_selection_test.cc
+++ b/third_party/blink/renderer/core/editing/layout_selection_test.cc
@@ -152,7 +152,7 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All, LayoutSelectionTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, LayoutSelectionTest, ::testing::Bool());
 
 TEST_P(LayoutSelectionTest, TraverseLayoutObject) {
   SetBodyContent("foo<br>bar");
diff --git a/third_party/blink/renderer/core/editing/local_caret_rect_bidi_test.cc b/third_party/blink/renderer/core/editing/local_caret_rect_bidi_test.cc
index 546e78d..026300a 100644
--- a/third_party/blink/renderer/core/editing/local_caret_rect_bidi_test.cc
+++ b/third_party/blink/renderer/core/editing/local_caret_rect_bidi_test.cc
@@ -25,9 +25,9 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        ParameterizedLocalCaretRectBidiTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         ParameterizedLocalCaretRectBidiTest,
+                         testing::Bool());
 
 // This file contains script-generated tests for LocalCaretRectOfPosition()
 // that are related to Bidirectional text. The test cases are only for
diff --git a/third_party/blink/renderer/core/editing/local_caret_rect_test.cc b/third_party/blink/renderer/core/editing/local_caret_rect_test.cc
index efe0a2a..547e2e8 100644
--- a/third_party/blink/renderer/core/editing/local_caret_rect_test.cc
+++ b/third_party/blink/renderer/core/editing/local_caret_rect_test.cc
@@ -38,7 +38,7 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All, ParameterizedLocalCaretRectTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, ParameterizedLocalCaretRectTest, testing::Bool());
 
 TEST_P(ParameterizedLocalCaretRectTest, DOMAndFlatTrees) {
   const char* body_content =
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
index 1923757..20500643 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
+++ b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.cc
@@ -56,47 +56,34 @@
   markup_.Append(string);
 }
 
-void MarkupAccumulator::AppendStartTag(Node& node, Namespaces* namespaces) {
-  AppendStartMarkup(markup_, node, namespaces);
-}
-
 void MarkupAccumulator::AppendEndTag(const Element& element) {
-  AppendEndMarkup(markup_, element);
+  AppendEndMarkup(element);
 }
 
-void MarkupAccumulator::AppendStartMarkup(StringBuilder& result,
-                                          Node& node,
-                                          Namespaces* namespaces) {
+void MarkupAccumulator::AppendStartMarkup(Node& node, Namespaces& namespaces) {
   switch (node.getNodeType()) {
     case Node::kTextNode:
-      AppendText(result, ToText(node));
+      formatter_.AppendText(markup_, ToText(node));
       break;
     case Node::kElementNode:
-      AppendElement(result, ToElement(node), namespaces);
+      AppendElement(ToElement(node), namespaces);
       break;
     case Node::kAttributeNode:
       // Only XMLSerializer can pass an Attr.  So, |documentIsHTML| flag is
       // false.
-      formatter_.AppendAttributeValue(result, ToAttr(node).value(), false);
+      formatter_.AppendAttributeValue(markup_, ToAttr(node).value(), false);
       break;
     default:
-      formatter_.AppendStartMarkup(result, node);
+      formatter_.AppendStartMarkup(markup_, node);
       break;
   }
 }
 
-void MarkupAccumulator::AppendEndMarkup(StringBuilder& result,
-                                        const Element& element) {
-  formatter_.AppendEndMarkup(result, element);
+void MarkupAccumulator::AppendEndMarkup(const Element& element) {
+  formatter_.AppendEndMarkup(markup_, element);
 }
 
-void MarkupAccumulator::AppendCustomAttributes(StringBuilder&,
-                                               const Element&,
-                                               Namespaces*) {}
-
-void MarkupAccumulator::AppendText(StringBuilder& result, Text& text) {
-  formatter_.AppendText(result, text);
-}
+void MarkupAccumulator::AppendCustomAttributes(const Element&, Namespaces&) {}
 
 bool MarkupAccumulator::ShouldIgnoreAttribute(
     const Element& element,
@@ -108,11 +95,10 @@
   return false;
 }
 
-void MarkupAccumulator::AppendElement(StringBuilder& result,
-                                      const Element& element,
-                                      Namespaces* namespaces) {
+void MarkupAccumulator::AppendElement(const Element& element,
+                                      Namespaces& namespaces) {
   // https://html.spec.whatwg.org/multipage/parsing.html#html-fragment-serialisation-algorithm
-  AppendOpenTag(result, element, namespaces);
+  AppendStartTagOpen(element, namespaces);
 
   AttributeCollection attributes = element.Attributes();
   if (SerializeAsHTMLDocument(element)) {
@@ -120,42 +106,148 @@
     // element does not have an is attribute in its attribute list, ...
     const AtomicString& is_value = element.IsValue();
     if (!is_value.IsNull() && !attributes.Find(html_names::kIsAttr)) {
-      AppendAttribute(result, element, Attribute(html_names::kIsAttr, is_value),
+      AppendAttribute(element, Attribute(html_names::kIsAttr, is_value),
                       namespaces);
     }
   }
   for (const auto& attribute : attributes) {
     if (!ShouldIgnoreAttribute(element, attribute))
-      AppendAttribute(result, element, attribute, namespaces);
+      AppendAttribute(element, attribute, namespaces);
   }
 
   // Give an opportunity to subclasses to add their own attributes.
-  AppendCustomAttributes(result, element, namespaces);
+  AppendCustomAttributes(element, namespaces);
 
-  AppendCloseTag(result, element);
+  AppendStartTagClose(element);
 }
 
-void MarkupAccumulator::AppendOpenTag(StringBuilder& result,
-                                      const Element& element,
-                                      Namespaces* namespaces) {
-  formatter_.AppendOpenTag(result, element);
-  if (!SerializeAsHTMLDocument(element) && namespaces &&
-      ShouldAddNamespaceElement(element, *namespaces)) {
-    MarkupFormatter::AppendNamespace(result, element.prefix(),
-                                     element.namespaceURI(), *namespaces);
+void MarkupAccumulator::AppendStartTagOpen(const Element& element,
+                                           Namespaces& namespaces) {
+  formatter_.AppendStartTagOpen(markup_, element);
+  if (!SerializeAsHTMLDocument(element) &&
+      ShouldAddNamespaceElement(element, namespaces)) {
+    AppendNamespace(element.prefix(), element.namespaceURI(), namespaces);
   }
 }
 
-void MarkupAccumulator::AppendCloseTag(StringBuilder& result,
-                                       const Element& element) {
-  formatter_.AppendCloseTag(result, element);
+void MarkupAccumulator::AppendStartTagClose(const Element& element) {
+  formatter_.AppendStartTagClose(markup_, element);
 }
 
-void MarkupAccumulator::AppendAttribute(StringBuilder& result,
-                                        const Element& element,
+void MarkupAccumulator::AppendAttribute(const Element& element,
                                         const Attribute& attribute,
-                                        Namespaces* namespaces) {
-  formatter_.AppendAttribute(result, element, attribute, namespaces);
+                                        Namespaces& namespaces) {
+  String value = formatter_.ResolveURLIfNeeded(element, attribute);
+  if (SerializeAsHTMLDocument(element)) {
+    MarkupFormatter::AppendAttributeAsHTML(markup_, attribute, value);
+  } else {
+    AppendAttributeAsXMLWithNamespace(element, attribute, value, namespaces);
+  }
+}
+
+void MarkupAccumulator::AppendAttributeAsXMLWithNamespace(
+    const Element& element,
+    const Attribute& attribute,
+    const String& value,
+    Namespaces& namespaces) {
+  // https://w3c.github.io/DOM-Parsing/#serializing-an-element-s-attributes
+
+  // 3.3. Let attribute namespace be the value of attr's namespaceURI value.
+  const AtomicString& attribute_namespace = attribute.NamespaceURI();
+
+  // 3.4. Let candidate prefix be null.
+  AtomicString candidate_prefix;
+
+  // 3.5. If attribute namespace is not null, then run these sub-steps:
+
+  // 3.5.1. Let candidate prefix be the result of retrieving a preferred
+  // prefix string from map given namespace attribute namespace with preferred
+  // prefix being attr's prefix value.
+  // TODO(tkent): Implement it. crbug.com/906807
+  candidate_prefix = attribute.Prefix();
+
+  // 3.5.2. If the value of attribute namespace is the XMLNS namespace, then
+  // run these steps:
+  if (attribute_namespace == xmlns_names::kNamespaceURI) {
+    if (!attribute.Prefix() && attribute.LocalName() != g_xmlns_atom)
+      candidate_prefix = g_xmlns_atom;
+    // Account for the namespace attribute we're about to append.
+    const AtomicString& lookup_key =
+        (!attribute.Prefix()) ? g_empty_atom : attribute.LocalName();
+    namespaces.Set(lookup_key, attribute.Value());
+  } else if (attribute_namespace == xml_names::kNamespaceURI) {
+    // TODO(tkent): Remove this block when we implement 'retrieving a
+    // preferred prefix string'.
+    if (!candidate_prefix)
+      candidate_prefix = g_xml_atom;
+  } else {
+    // TODO(tkent): Remove this block. The standard and Firefox don't
+    // have this behavior.
+    if (attribute_namespace == xlink_names::kNamespaceURI) {
+      if (!candidate_prefix)
+        candidate_prefix = g_xlink_atom;
+    }
+
+    // 3.5.3. Otherwise, the attribute namespace in not the XMLNS namespace.
+    // Run these steps:
+    if (ShouldAddNamespaceAttribute(attribute, element)) {
+      if (!candidate_prefix) {
+        // This behavior is in process of being standardized. See
+        // crbug.com/248044 and
+        // https://www.w3.org/Bugs/Public/show_bug.cgi?id=24208
+        String prefix_prefix("ns", 2u);
+        for (unsigned i = attribute_namespace.Impl()->ExistingHash();; ++i) {
+          AtomicString new_prefix(String(prefix_prefix + String::Number(i)));
+          AtomicString found_uri = namespaces.at(new_prefix);
+          if (found_uri == attribute_namespace || found_uri == g_null_atom) {
+            // We already generated a prefix for this namespace.
+            candidate_prefix = new_prefix;
+            break;
+          }
+        }
+      }
+      // 3.5.3.2. Append the following to result, in the order listed:
+      DCHECK(candidate_prefix);
+      AppendNamespace(candidate_prefix, attribute_namespace, namespaces);
+    }
+  }
+  MarkupFormatter::AppendAttribute(markup_, candidate_prefix,
+                                   attribute.LocalName(), value, false);
+}
+
+bool MarkupAccumulator::ShouldAddNamespaceAttribute(const Attribute& attribute,
+                                                    const Element& element) {
+  // xmlns and xmlns:prefix attributes should be handled by another branch in
+  // AppendAttributeAsXMLWithNamespace().
+  DCHECK_NE(attribute.NamespaceURI(), xmlns_names::kNamespaceURI);
+
+  // Attributes are in the null namespace by default.
+  if (!attribute.NamespaceURI())
+    return false;
+
+  // Attributes without a prefix will need one generated for them, and an xmlns
+  // attribute for that prefix.
+  if (!attribute.Prefix())
+    return true;
+
+  return !element.hasAttribute(WTF::g_xmlns_with_colon + attribute.Prefix());
+}
+
+void MarkupAccumulator::AppendNamespace(const AtomicString& prefix,
+                                        const AtomicString& namespace_uri,
+                                        Namespaces& namespaces) {
+  const AtomicString& lookup_key = (!prefix) ? g_empty_atom : prefix;
+  AtomicString found_uri = namespaces.at(lookup_key);
+  if (!EqualIgnoringNullity(found_uri, namespace_uri)) {
+    namespaces.Set(lookup_key, namespace_uri);
+    if (prefix.IsEmpty()) {
+      MarkupFormatter::AppendAttribute(markup_, g_null_atom, g_xmlns_atom,
+                                       namespace_uri, false);
+    } else {
+      MarkupFormatter::AppendAttribute(markup_, g_xmlns_atom, prefix,
+                                       namespace_uri, false);
+    }
+  }
 }
 
 EntityMask MarkupAccumulator::EntityMaskForText(const Text& text) const {
@@ -188,77 +280,73 @@
 }
 
 template <typename Strategy>
-static void SerializeNodesWithNamespaces(MarkupAccumulator& accumulator,
-                                         Node& target_node,
-                                         EChildrenOnly children_only,
-                                         const Namespaces* namespaces) {
+void MarkupAccumulator::SerializeNodesWithNamespaces(
+    Node& target_node,
+    EChildrenOnly children_only,
+    const Namespaces& namespaces) {
   if (target_node.IsElementNode() &&
-      accumulator.ShouldIgnoreElement(ToElement(target_node))) {
+      ShouldIgnoreElement(ToElement(target_node))) {
     return;
   }
 
-  Namespaces namespace_hash;
-  if (namespaces)
-    namespace_hash = *namespaces;
+  Namespaces namespace_hash = namespaces;
 
   if (!children_only)
-    accumulator.AppendStartTag(target_node, &namespace_hash);
+    AppendStartMarkup(target_node, namespace_hash);
 
-  if (!(accumulator.SerializeAsHTMLDocument(target_node) &&
+  if (!(SerializeAsHTMLDocument(target_node) &&
         ElementCannotHaveEndTag(target_node))) {
     Node* current = IsHTMLTemplateElement(target_node)
                         ? Strategy::FirstChild(
                               *ToHTMLTemplateElement(target_node).content())
                         : Strategy::FirstChild(target_node);
-    for (; current; current = Strategy::NextSibling(*current))
-      SerializeNodesWithNamespaces<Strategy>(accumulator, *current,
-                                             kIncludeNode, &namespace_hash);
+    for (; current; current = Strategy::NextSibling(*current)) {
+      SerializeNodesWithNamespaces<Strategy>(*current, kIncludeNode,
+                                             namespace_hash);
+    }
 
     // Traverses other DOM tree, i.e., shadow tree.
     if (target_node.IsElementNode()) {
       std::pair<Node*, Element*> auxiliary_pair =
-          accumulator.GetAuxiliaryDOMTree(ToElement(target_node));
+          GetAuxiliaryDOMTree(ToElement(target_node));
       Node* auxiliary_tree = auxiliary_pair.first;
       Element* enclosing_element = auxiliary_pair.second;
       if (auxiliary_tree) {
         if (auxiliary_pair.second)
-          accumulator.AppendStartTag(*enclosing_element);
+          AppendStartMarkup(*enclosing_element, namespace_hash);
         current = Strategy::FirstChild(*auxiliary_tree);
         for (; current; current = Strategy::NextSibling(*current)) {
-          SerializeNodesWithNamespaces<Strategy>(accumulator, *current,
-                                                 kIncludeNode, &namespace_hash);
+          SerializeNodesWithNamespaces<Strategy>(*current, kIncludeNode,
+                                                 namespace_hash);
         }
         if (enclosing_element)
-          accumulator.AppendEndTag(*enclosing_element);
+          AppendEndTag(*enclosing_element);
       }
     }
   }
 
   if ((!children_only && target_node.IsElementNode()) &&
-      !(accumulator.SerializeAsHTMLDocument(target_node) &&
+      !(SerializeAsHTMLDocument(target_node) &&
         ElementCannotHaveEndTag(target_node)))
-    accumulator.AppendEndTag(ToElement(target_node));
+    AppendEndTag(ToElement(target_node));
 }
 
 template <typename Strategy>
-String SerializeNodes(MarkupAccumulator& accumulator,
-                      Node& target_node,
-                      EChildrenOnly children_only) {
-  Namespaces* namespaces = nullptr;
+String MarkupAccumulator::SerializeNodes(Node& target_node,
+                                         EChildrenOnly children_only) {
   Namespaces namespace_hash;
-  if (!accumulator.SerializeAsHTMLDocument(target_node)) {
+  if (!SerializeAsHTMLDocument(target_node)) {
     // Add pre-bound namespaces for XML fragments.
     namespace_hash.Set(g_xml_atom, xml_names::kNamespaceURI);
-    namespaces = &namespace_hash;
   }
 
-  SerializeNodesWithNamespaces<Strategy>(accumulator, target_node,
-                                         children_only, namespaces);
-  return accumulator.ToString();
+  SerializeNodesWithNamespaces<Strategy>(target_node, children_only,
+                                         namespace_hash);
+  return ToString();
 }
 
-template String SerializeNodes<EditingStrategy>(MarkupAccumulator&,
-                                                Node&,
-                                                EChildrenOnly);
+template String MarkupAccumulator::SerializeNodes<EditingStrategy>(
+    Node&,
+    EChildrenOnly);
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.h b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.h
index fb81f82..8f2cdaef 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_accumulator.h
+++ b/third_party/blink/renderer/core/editing/serializers/markup_accumulator.h
@@ -40,6 +40,8 @@
 class Element;
 class Node;
 
+using Namespaces = HashMap<AtomicString, AtomicString>;
+
 class MarkupAccumulator {
   STACK_ALLOCATED();
 
@@ -48,32 +50,45 @@
                     SerializationType = SerializationType::kAsOwnerDocument);
   virtual ~MarkupAccumulator();
 
-  void AppendString(const String&);
-  virtual void AppendStartTag(Node&, Namespaces* = nullptr);
-  virtual void AppendEndTag(const Element&);
-  void AppendStartMarkup(StringBuilder&, Node&, Namespaces*);
-  void AppendEndMarkup(StringBuilder&, const Element&);
+  template <typename Strategy>
+  String SerializeNodes(Node&, EChildrenOnly);
 
+ protected:
+  // Serialize a Node, without its children and its end tag.
+  virtual void AppendStartMarkup(Node&, Namespaces&);
+  virtual void AppendElement(const Element&, Namespaces&);
+  virtual void AppendAttribute(const Element&, const Attribute&, Namespaces&);
+
+  MarkupFormatter formatter_;
+  StringBuilder markup_;
+
+ private:
   bool SerializeAsHTMLDocument(const Node&) const;
   String ToString() { return markup_.ToString(); }
 
-  virtual void AppendCustomAttributes(StringBuilder&,
-                                      const Element&,
-                                      Namespaces*);
+  void AppendString(const String&);
+  void AppendStartTagOpen(const Element&, Namespaces&);
+  void AppendStartTagClose(const Element&);
+  bool ShouldAddNamespaceElement(const Element&, Namespaces&) const;
+  void AppendNamespace(const AtomicString& prefix,
+                       const AtomicString& namespace_uri,
+                       Namespaces& namespaces);
+  void AppendAttributeAsXMLWithNamespace(const Element& element,
+                                         const Attribute& attribute,
+                                         const String& value,
+                                         Namespaces& namespaces);
+  static bool ShouldAddNamespaceAttribute(const Attribute& attribute,
+                                          const Element& element);
 
-  virtual void AppendText(StringBuilder&, Text&);
-  virtual bool ShouldIgnoreAttribute(const Element&, const Attribute&) const;
-  virtual bool ShouldIgnoreElement(const Element&) const;
-  virtual void AppendElement(StringBuilder&, const Element&, Namespaces*);
-  void AppendOpenTag(StringBuilder&, const Element&, Namespaces*);
-  void AppendCloseTag(StringBuilder&, const Element&);
-  virtual void AppendAttribute(StringBuilder&,
-                               const Element&,
-                               const Attribute&,
-                               Namespaces*);
+  void AppendEndTag(const Element&);
+  void AppendEndMarkup(const Element&);
 
   EntityMask EntityMaskForText(const Text&) const;
 
+  virtual void AppendCustomAttributes(const Element&, Namespaces&);
+  virtual bool ShouldIgnoreAttribute(const Element&, const Attribute&) const;
+  virtual bool ShouldIgnoreElement(const Element&) const;
+
   // Returns an auxiliary DOM tree, i.e. shadow tree, that needs also to be
   // serialized. The root of auxiliary DOM tree is returned as an 1st element
   // in the pair. It can be null if no auxiliary DOM tree exists. An additional
@@ -83,21 +98,17 @@
   // tree content.
   virtual std::pair<Node*, Element*> GetAuxiliaryDOMTree(const Element&) const;
 
- private:
-  bool ShouldAddNamespaceElement(const Element&, Namespaces&) const;
-
-  MarkupFormatter formatter_;
-  StringBuilder markup_;
+  template <typename Strategy>
+  void SerializeNodesWithNamespaces(Node& target_node,
+                                    EChildrenOnly children_only,
+                                    const Namespaces& namespaces);
 
   DISALLOW_COPY_AND_ASSIGN(MarkupAccumulator);
 };
 
-template <typename Strategy>
-String SerializeNodes(MarkupAccumulator&, Node&, EChildrenOnly);
-
-extern template String SerializeNodes<EditingStrategy>(MarkupAccumulator&,
-                                                       Node&,
-                                                       EChildrenOnly);
+extern template String MarkupAccumulator::SerializeNodes<EditingStrategy>(
+    Node&,
+    EChildrenOnly);
 
 }  // namespace blink
 
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc b/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc
index 4ab2840..f22009b 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc
+++ b/third_party/blink/renderer/core/editing/serializers/markup_formatter.cc
@@ -221,21 +221,6 @@
   result.Append('"');
 }
 
-void MarkupFormatter::AppendNamespace(StringBuilder& result,
-                                      const AtomicString& prefix,
-                                      const AtomicString& namespace_uri,
-                                      Namespaces& namespaces) {
-  const AtomicString& lookup_key = (!prefix) ? g_empty_atom : prefix;
-  AtomicString found_uri = namespaces.at(lookup_key);
-  if (!EqualIgnoringNullity(found_uri, namespace_uri)) {
-    namespaces.Set(lookup_key, namespace_uri);
-    if (prefix.IsEmpty())
-      AppendAttribute(result, g_null_atom, g_xmlns_atom, namespace_uri, false);
-    else
-      AppendAttribute(result, g_xmlns_atom, prefix, namespace_uri, false);
-  }
-}
-
 void MarkupFormatter::AppendText(StringBuilder& result, Text& text) {
   const String& str = text.data();
   AppendCharactersReplacingEntities(result, str, 0, str.length(),
@@ -310,14 +295,14 @@
   result.Append("?>");
 }
 
-void MarkupFormatter::AppendOpenTag(StringBuilder& result,
-                                    const Element& element) {
+void MarkupFormatter::AppendStartTagOpen(StringBuilder& result,
+                                         const Element& element) {
   result.Append('<');
   result.Append(element.TagQName().ToString());
 }
 
-void MarkupFormatter::AppendCloseTag(StringBuilder& result,
-                                     const Element& element) {
+void MarkupFormatter::AppendStartTagClose(StringBuilder& result,
+                                          const Element& element) {
   if (ShouldSelfClose(element)) {
     if (element.IsHTMLElement())
       result.Append(' ');  // XHTML 1.0 <-> HTML compatibility.
@@ -326,21 +311,6 @@
   result.Append('>');
 }
 
-void MarkupFormatter::AppendAttribute(StringBuilder& result,
-                                      const Element& element,
-                                      const Attribute& attribute,
-                                      Namespaces* namespaces) {
-  String value = ResolveURLIfNeeded(element, attribute);
-  if (SerializeAsHTMLDocument(element)) {
-    AppendAttributeAsHTML(result, attribute, value);
-  } else if (!namespaces) {
-    AppendAttributeAsXMLWithoutNamespace(result, attribute, value);
-  } else {
-    AppendAttributeAsXMLWithNamespace(result, element, attribute, value,
-                                      *namespaces);
-  }
-}
-
 void MarkupFormatter::AppendAttributeAsHTML(StringBuilder& result,
                                             const Attribute& attribute,
                                             const String& value) {
@@ -378,78 +348,6 @@
                   false);
 }
 
-void MarkupFormatter::AppendAttributeAsXMLWithNamespace(
-    StringBuilder& result,
-    const Element& element,
-    const Attribute& attribute,
-    const String& value,
-    Namespaces& namespaces) {
-  // https://w3c.github.io/DOM-Parsing/#serializing-an-element-s-attributes
-
-  // 3.3. Let attribute namespace be the value of attr's namespaceURI value.
-  const AtomicString& attribute_namespace = attribute.NamespaceURI();
-
-  // 3.4. Let candidate prefix be null.
-  AtomicString candidate_prefix;
-
-  // 3.5. If attribute namespace is not null, then run these sub-steps:
-
-  // 3.5.1. Let candidate prefix be the result of retrieving a preferred
-  // prefix string from map given namespace attribute namespace with preferred
-  // prefix being attr's prefix value.
-  // TODO(tkent): Implement it. crbug.com/906807
-  candidate_prefix = attribute.Prefix();
-
-  // 3.5.2. If the value of attribute namespace is the XMLNS namespace, then
-  // run these steps:
-  if (attribute_namespace == xmlns_names::kNamespaceURI) {
-    if (!attribute.Prefix() && attribute.LocalName() != g_xmlns_atom)
-      candidate_prefix = g_xmlns_atom;
-    // Account for the namespace attribute we're about to append.
-    const AtomicString& lookup_key =
-        (!attribute.Prefix()) ? g_empty_atom : attribute.LocalName();
-    namespaces.Set(lookup_key, attribute.Value());
-  } else if (attribute_namespace == xml_names::kNamespaceURI) {
-    // TODO(tkent): Remove this block when we implement 'retrieving a
-    // preferred prefix string'.
-    if (!candidate_prefix)
-      candidate_prefix = g_xml_atom;
-  } else {
-    // TODO(tkent): Remove this block. The standard and Firefox don't
-    // have this behavior.
-    if (attribute_namespace == xlink_names::kNamespaceURI) {
-      if (!candidate_prefix)
-        candidate_prefix = g_xlink_atom;
-    }
-
-    // 3.5.3. Otherwise, the attribute namespace in not the XMLNS namespace.
-    // Run these steps:
-    if (ShouldAddNamespaceAttribute(attribute, element)) {
-      if (!candidate_prefix) {
-        // This behavior is in process of being standardized. See
-        // crbug.com/248044 and
-        // https://www.w3.org/Bugs/Public/show_bug.cgi?id=24208
-        String prefix_prefix("ns", 2u);
-        for (unsigned i = attribute_namespace.Impl()->ExistingHash();; ++i) {
-          AtomicString new_prefix(String(prefix_prefix + String::Number(i)));
-          AtomicString found_uri = namespaces.at(new_prefix);
-          if (found_uri == attribute_namespace || found_uri == g_null_atom) {
-            // We already generated a prefix for this namespace.
-            candidate_prefix = new_prefix;
-            break;
-          }
-        }
-      }
-      // 3.5.3.2. Append the following to result, in the order listed:
-      DCHECK(candidate_prefix);
-      AppendNamespace(result, candidate_prefix, attribute_namespace,
-                      namespaces);
-    }
-  }
-  AppendAttribute(result, candidate_prefix, attribute.LocalName(), value,
-                  false);
-}
-
 void MarkupFormatter::AppendCDATASection(StringBuilder& result,
                                          const String& section) {
   // FIXME: CDATA content is not escaped, but XMLSerializer (and possibly other
@@ -459,24 +357,6 @@
   result.Append("]]>");
 }
 
-bool MarkupFormatter::ShouldAddNamespaceAttribute(const Attribute& attribute,
-                                                  const Element& element) {
-  // xmlns and xmlns:prefix attributes should be handled by another branch in
-  // appendAttribute.
-  DCHECK_NE(attribute.NamespaceURI(), xmlns_names::kNamespaceURI);
-
-  // Attributes are in the null namespace by default.
-  if (!attribute.NamespaceURI())
-    return false;
-
-  // Attributes without a prefix will need one generated for them, and an xmlns
-  // attribute for that prefix.
-  if (!attribute.Prefix())
-    return true;
-
-  return !element.hasAttribute(WTF::g_xmlns_with_colon + attribute.Prefix());
-}
-
 EntityMask MarkupFormatter::EntityMaskForText(const Text& text) const {
   if (!SerializeAsHTMLDocument(text))
     return kEntityMaskInPCDATA;
diff --git a/third_party/blink/renderer/core/editing/serializers/markup_formatter.h b/third_party/blink/renderer/core/editing/serializers/markup_formatter.h
index 2da2657..c902b2d 100644
--- a/third_party/blink/renderer/core/editing/serializers/markup_formatter.h
+++ b/third_party/blink/renderer/core/editing/serializers/markup_formatter.h
@@ -30,7 +30,6 @@
 #include "base/macros.h"
 #include "third_party/blink/renderer/core/editing/editing_strategy.h"
 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
-#include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
@@ -40,8 +39,6 @@
 class Element;
 class Node;
 
-typedef HashMap<AtomicString, AtomicString> Namespaces;
-
 enum EntityMask {
   kEntityAmp = 0x0001,
   kEntityLt = 0x0002,
@@ -73,6 +70,12 @@
 
  public:
   static void AppendAttributeValue(StringBuilder&, const String&, bool);
+  static void AppendAttributeAsHTML(StringBuilder& result,
+                                    const Attribute& attribute,
+                                    const String& value);
+  static void AppendAttributeAsXMLWithoutNamespace(StringBuilder& result,
+                                                   const Attribute& attribute,
+                                                   const String& value);
   static void AppendAttribute(StringBuilder& result,
                               const AtomicString& prefix,
                               const AtomicString& local_name,
@@ -86,10 +89,6 @@
                                                 EntityMask);
   static void AppendComment(StringBuilder&, const String&);
   static void AppendDocumentType(StringBuilder&, const DocumentType&);
-  static void AppendNamespace(StringBuilder&,
-                              const AtomicString& prefix,
-                              const AtomicString& namespace_uri,
-                              Namespaces&);
   static void AppendProcessingInstruction(StringBuilder&,
                                           const String& target,
                                           const String& data);
@@ -105,31 +104,16 @@
   bool SerializeAsHTMLDocument(const Node&) const;
 
   void AppendText(StringBuilder&, Text&);
-  void AppendOpenTag(StringBuilder&, const Element&);
-  void AppendCloseTag(StringBuilder&, const Element&);
-  void AppendAttribute(StringBuilder&,
-                       const Element&,
-                       const Attribute&,
-                       Namespaces*);
+  // Serialize '<' and the element name.
+  void AppendStartTagOpen(StringBuilder&, const Element&);
+  // Serialize '>' or '/>'
+  void AppendStartTagClose(StringBuilder&, const Element&);
 
-  static bool ShouldAddNamespaceAttribute(const Attribute&, const Element&);
   EntityMask EntityMaskForText(const Text&) const;
   bool ShouldSelfClose(const Element&) const;
+  String ResolveURLIfNeeded(const Element&, const Attribute& attribute) const;
 
  private:
-  String ResolveURLIfNeeded(const Element&, const Attribute& attribute) const;
-  static void AppendAttributeAsHTML(StringBuilder& result,
-                                    const Attribute& attribute,
-                                    const String& value);
-  static void AppendAttributeAsXMLWithoutNamespace(StringBuilder& result,
-                                                   const Attribute& attribute,
-                                                   const String& value);
-  static void AppendAttributeAsXMLWithNamespace(StringBuilder& result,
-                                                const Element& element,
-                                                const Attribute& attribute,
-                                                const String& value,
-                                                Namespaces& namespaces);
-
   const EAbsoluteURLs resolve_urls_method_;
   SerializationType serialization_type_;
 
diff --git a/third_party/blink/renderer/core/editing/serializers/serialization.cc b/third_party/blink/renderer/core/editing/serializers/serialization.cc
index 0ad4d70..ea80365 100644
--- a/third_party/blink/renderer/core/editing/serializers/serialization.cc
+++ b/third_party/blink/renderer/core/editing/serializers/serialization.cc
@@ -452,8 +452,8 @@
     return "";
 
   MarkupAccumulator accumulator(should_resolve_urls);
-  return SerializeNodes<EditingStrategy>(accumulator, const_cast<Node&>(*node),
-                                         children_only);
+  return accumulator.SerializeNodes<EditingStrategy>(const_cast<Node&>(*node),
+                                                     children_only);
 }
 
 static void FillContainerFromString(ContainerNode* paragraph,
diff --git a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc
index 4c105bf..ccef2db 100644
--- a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc
+++ b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.cc
@@ -139,13 +139,13 @@
     const Element& element,
     EditingStyle* style) {
   const bool document_is_html = element.GetDocument().IsHTMLDocument();
-  formatter_.AppendOpenTag(out, element);
+  formatter_.AppendStartTagOpen(out, element);
   AttributeCollection attributes = element.Attributes();
   for (const auto& attribute : attributes) {
     // We'll handle the style attribute separately, below.
     if (attribute.GetName() == kStyleAttr)
       continue;
-    formatter_.AppendAttribute(out, element, attribute, nullptr);
+    AppendAttribute(out, element, attribute);
   }
   if (style && !style->IsEmpty()) {
     out.Append(" style=\"");
@@ -153,7 +153,7 @@
                                           document_is_html);
     out.Append('\"');
   }
-  formatter_.AppendCloseTag(out, element);
+  formatter_.AppendStartTagClose(out, element);
 }
 
 void StyledMarkupAccumulator::AppendElement(const Element& element) {
@@ -162,11 +162,23 @@
 
 void StyledMarkupAccumulator::AppendElement(StringBuilder& out,
                                             const Element& element) {
-  formatter_.AppendOpenTag(out, element);
+  formatter_.AppendStartTagOpen(out, element);
   AttributeCollection attributes = element.Attributes();
   for (const auto& attribute : attributes)
-    formatter_.AppendAttribute(out, element, attribute, nullptr);
-  formatter_.AppendCloseTag(out, element);
+    AppendAttribute(out, element, attribute);
+  formatter_.AppendStartTagClose(out, element);
+}
+
+void StyledMarkupAccumulator::AppendAttribute(StringBuilder& result,
+                                              const Element& element,
+                                              const Attribute& attribute) {
+  String value = formatter_.ResolveURLIfNeeded(element, attribute);
+  if (formatter_.SerializeAsHTMLDocument(element)) {
+    MarkupFormatter::AppendAttributeAsHTML(result, attribute, value);
+  } else {
+    MarkupFormatter::AppendAttributeAsXMLWithoutNamespace(result, attribute,
+                                                          value);
+  }
 }
 
 void StyledMarkupAccumulator::WrapWithStyleNode(CSSPropertyValueSet* style) {
diff --git a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.h b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.h
index e211def..bf9d03e0 100644
--- a/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.h
+++ b/third_party/blink/renderer/core/editing/serializers/styled_markup_accumulator.h
@@ -70,6 +70,7 @@
   void AppendElementWithInlineStyle(StringBuilder&,
                                     const Element&,
                                     EditingStyle*);
+  // Serialize a Node, without its children and its end tag.
   void AppendStartMarkup(Node&);
 
   bool ShouldAnnotate() const;
@@ -82,6 +83,9 @@
   String StringValueForRange(const Text&);
 
   void AppendEndMarkup(StringBuilder&, const Element&);
+  void AppendAttribute(StringBuilder& result,
+                       const Element& element,
+                       const Attribute& attribute);
 
   MarkupFormatter formatter_;
   const TextOffset start_;
diff --git a/third_party/blink/renderer/core/editing/text_offset_mapping_test.cc b/third_party/blink/renderer/core/editing/text_offset_mapping_test.cc
index bf46362..fc7dd060 100644
--- a/third_party/blink/renderer/core/editing/text_offset_mapping_test.cc
+++ b/third_party/blink/renderer/core/editing/text_offset_mapping_test.cc
@@ -81,9 +81,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        ParameterizedTextOffsetMappingTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         ParameterizedTextOffsetMappingTest,
+                         ::testing::Bool());
 
 TEST_P(ParameterizedTextOffsetMappingTest, ComputeTextOffsetBasic) {
   EXPECT_EQ("|(1) abc def", ComputeTextOffset("<p>| (1) abc def</p>"));
diff --git a/third_party/blink/renderer/core/editing/visible_units_line_test.cc b/third_party/blink/renderer/core/editing/visible_units_line_test.cc
index 80821e9..62b6fe4 100644
--- a/third_party/blink/renderer/core/editing/visible_units_line_test.cc
+++ b/third_party/blink/renderer/core/editing/visible_units_line_test.cc
@@ -59,9 +59,9 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        ParameterizedVisibleUnitsLineTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         ParameterizedVisibleUnitsLineTest,
+                         ::testing::Bool());
 
 TEST_F(VisibleUnitsLineTest, endOfLine) {
   const char* body_content =
diff --git a/third_party/blink/renderer/core/editing/visible_units_sentence_test.cc b/third_party/blink/renderer/core/editing/visible_units_sentence_test.cc
index ec2ad96..2f172c12 100644
--- a/third_party/blink/renderer/core/editing/visible_units_sentence_test.cc
+++ b/third_party/blink/renderer/core/editing/visible_units_sentence_test.cc
@@ -59,9 +59,9 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        ParameterizedVisibleUnitsSentenceTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         ParameterizedVisibleUnitsSentenceTest,
+                         ::testing::Bool());
 
 TEST_P(ParameterizedVisibleUnitsSentenceTest, EndOfSentenceShadowDOMV0) {
   const char* body_content = "<a id=host><b id=one>1</b><b id=two>22</b></a>";
diff --git a/third_party/blink/renderer/core/editing/visible_units_word_test.cc b/third_party/blink/renderer/core/editing/visible_units_word_test.cc
index 244f469..2ba5f2d 100644
--- a/third_party/blink/renderer/core/editing/visible_units_word_test.cc
+++ b/third_party/blink/renderer/core/editing/visible_units_word_test.cc
@@ -71,9 +71,9 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        ParameterizedVisibleUnitsWordTest,
-                        ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         ParameterizedVisibleUnitsWordTest,
+                         ::testing::Bool());
 
 TEST_P(ParameterizedVisibleUnitsWordTest, StartOfWordBasic) {
   EXPECT_EQ("<p> |(1) abc def</p>", DoStartOfWord("<p>| (1) abc def</p>"));
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 60ab7e40..c111ec7a 100644
--- a/third_party/blink/renderer/core/exported/web_frame_test.cc
+++ b/third_party/blink/renderer/core/exported/web_frame_test.cc
@@ -10157,10 +10157,10 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        WebFrameOverscrollTest,
-                        testing::Values(kWebGestureDeviceTouchpad,
-                                        kWebGestureDeviceTouchscreen));
+INSTANTIATE_TEST_SUITE_P(All,
+                         WebFrameOverscrollTest,
+                         testing::Values(kWebGestureDeviceTouchpad,
+                                         kWebGestureDeviceTouchscreen));
 
 TEST_P(WebFrameOverscrollTest,
        AccumulatedRootOverscrollAndUnsedDeltaValuesOnOverscroll) {
diff --git a/third_party/blink/renderer/core/exported/web_layer_test.cc b/third_party/blink/renderer/core/exported/web_layer_test.cc
index 68930d4..fdadf04 100644
--- a/third_party/blink/renderer/core/exported/web_layer_test.cc
+++ b/third_party/blink/renderer/core/exported/web_layer_test.cc
@@ -109,7 +109,7 @@
   std::unique_ptr<frame_test_helpers::WebViewHelper> web_view_helper_;
 };
 
-INSTANTIATE_LAYER_LIST_TEST_CASE_P(WebLayerListTest);
+INSTANTIATE_LAYER_LIST_TEST_SUITE_P(WebLayerListTest);
 
 TEST_P(WebLayerListTest, DidScrollCallbackAfterScrollableAreaChanges) {
   InitializeWithHTML(*WebView()->MainFrameImpl()->GetFrame(),
@@ -267,7 +267,7 @@
   }
 };
 
-INSTANTIATE_LAYER_LIST_TEST_CASE_P(WebLayerListSimTest);
+INSTANTIATE_LAYER_LIST_TEST_SUITE_P(WebLayerListSimTest);
 
 TEST_P(WebLayerListSimTest, LayerUpdatesDoNotInvalidateEarlierLayers) {
   // TODO(crbug.com/765003): CAP may make different layerization decisions and
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
index d7167cb..02ec342 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.cc
@@ -134,9 +134,6 @@
       popup_->web_view_->WidgetClient()->ScheduleAnimation();
       return;
     }
-
-    // TODO(danakj): Why null check? Can ScheduleAnimation() happen after the
-    // call to WillCloseLayerTreeView()?
     popup_->WidgetClient()->ScheduleAnimation();
   }
 
@@ -393,15 +390,6 @@
   PageWidgetDelegate::Animate(*page_, CurrentTimeTicks());
 }
 
-void WebPagePopupImpl::WillCloseLayerTreeView() {
-  if (page_ && layer_tree_view_)
-    page_->WillCloseLayerTreeView(*layer_tree_view_, nullptr);
-
-  is_accelerated_compositing_active_ = false;
-  layer_tree_view_ = nullptr;
-  animation_host_ = nullptr;
-}
-
 void WebPagePopupImpl::UpdateLifecycle(LifecycleUpdate requested_update,
                                        LifecycleUpdateReason reason) {
   if (!page_)
@@ -539,6 +527,14 @@
 }
 
 void WebPagePopupImpl::Close() {
+  // TODO(danakj): |layer_tree_view_| should never be null here.
+  if (page_ && layer_tree_view_)
+    page_->WillCloseLayerTreeView(*layer_tree_view_, nullptr);
+
+  is_accelerated_compositing_active_ = false;
+  layer_tree_view_ = nullptr;
+  animation_host_ = nullptr;
+
   closing_ = true;
   // In case closePopup() was not called.
   if (page_)
diff --git a/third_party/blink/renderer/core/exported/web_page_popup_impl.h b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
index 0ddae22..b23e0cc 100644
--- a/third_party/blink/renderer/core/exported/web_page_popup_impl.h
+++ b/third_party/blink/renderer/core/exported/web_page_popup_impl.h
@@ -88,7 +88,6 @@
   void UpdateLifecycle(LifecycleUpdate requested_update,
                        LifecycleUpdateReason reason) override;
   void UpdateAllLifecyclePhasesAndCompositeForTesting(bool do_raster) override;
-  void WillCloseLayerTreeView() override;
   void PaintContent(cc::PaintCanvas*, const WebRect&) override;
   void Resize(const WebSize&) override;
   void Close() override;
diff --git a/third_party/blink/renderer/core/exported/web_script_controller.cc b/third_party/blink/renderer/core/exported/web_script_controller.cc
index e0fa064..4ae247e 100644
--- a/third_party/blink/renderer/core/exported/web_script_controller.cc
+++ b/third_party/blink/renderer/core/exported/web_script_controller.cc
@@ -34,8 +34,9 @@
 
 namespace blink {
 
-void WebScriptController::RegisterExtension(v8::Extension* extension) {
-  ScriptController::RegisterExtensionIfNeeded(extension);
+void WebScriptController::RegisterExtension(
+    std::unique_ptr<v8::Extension> extension) {
+  ScriptController::RegisterExtensionIfNeeded(std::move(extension));
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
index a9cbfadf..19e4331 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.cc
@@ -33,6 +33,7 @@
 #include <memory>
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "services/network/public/mojom/fetch_api.mojom-blink.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/public/mojom/devtools/devtools_agent.mojom-blink.h"
 #include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
 #include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
@@ -133,6 +134,13 @@
   DCHECK(!main_script_loader_);
   shadow_page_->DocumentLoader()->SetServiceWorkerNetworkProvider(
       client_->CreateServiceWorkerNetworkProvider());
+
+  if (features::IsOffMainThreadSharedWorkerScriptFetchEnabled()) {
+    // Bypass main script loading on the main thread.
+    ContinueStartWorkerContext();
+    return;
+  }
+
   main_script_loader_ = MakeGarbageCollected<WorkerClassicScriptLoader>();
   main_script_loader_->LoadTopLevelScriptAsynchronously(
       *shadow_page_->GetDocument(), shadow_page_->GetDocument()->Fetcher(),
@@ -296,14 +304,12 @@
   // differences between the flags just do it anyway.)
   client_->WaitForServiceWorkerControllerInfo(
       shadow_page_->DocumentLoader()->GetServiceWorkerNetworkProvider(),
-      WTF::Bind(&WebSharedWorkerImpl::ContinueOnScriptLoaderFinished,
+      WTF::Bind(&WebSharedWorkerImpl::ContinueStartWorkerContext,
                 weak_ptr_factory_.GetWeakPtr()));
 }
 
-void WebSharedWorkerImpl::ContinueOnScriptLoaderFinished() {
+void WebSharedWorkerImpl::ContinueStartWorkerContext() {
   DCHECK(IsMainThread());
-  DCHECK(main_script_loader_);
-  DCHECK(!main_script_loader_->Failed());
   if (asked_to_terminate_)
     return;
 
@@ -331,50 +337,74 @@
   web_worker_fetch_context->SetApplicationCacheHostID(
       document->Loader()->GetApplicationCacheHost()->GetHostID());
 
-  ContentSecurityPolicy* content_security_policy =
-      main_script_loader_->GetContentSecurityPolicy();
+  // TODO(nhiroki); Set |script_type| to mojom::ScriptType::kModule for module
+  // fetch (https://crbug.com/824646).
+  mojom::ScriptType script_type = mojom::ScriptType::kClassic;
+
+  if (features::IsOffMainThreadSharedWorkerScriptFetchEnabled()) {
+    // Off-the-main-thread script fetch:
+    // Some params (e.g., referrer policy, CSP) passed to
+    // GlobalScopeCreationParams are dummy values. They will be updated after
+    // worker script fetch on the worker thread.
+    // TODO(nhiroki): Currently |address_space| and |origin_trial_tokens| are
+    // not updated after worker script fetch. Update them.
+    auto creation_params = std::make_unique<GlobalScopeCreationParams>(
+        script_request_url_, script_type,
+        OffMainThreadWorkerScriptFetchOption::kEnabled, document->UserAgent(),
+        std::move(web_worker_fetch_context), Vector<CSPHeaderAndType>(),
+        network::mojom::ReferrerPolicy::kDefault,
+        outside_settings_object->GetSecurityOrigin(),
+        document->IsSecureContext(), outside_settings_object->GetHttpsState(),
+        CreateWorkerClients(), creation_address_space_,
+        nullptr /* origin_trial_tokens */, devtools_worker_token_,
+        std::make_unique<WorkerSettings>(document->GetFrame()->GetSettings()),
+        kV8CacheOptionsDefault, nullptr /* worklet_module_response_map */,
+        std::move(pending_interface_provider_));
+    StartWorkerThread(std::move(creation_params), script_request_url_,
+                      String() /* source_code */, outside_settings_object);
+    return;
+  }
+
+  // On-the-main-thread script fetch:
+  DCHECK(main_script_loader_);
+  DCHECK(!main_script_loader_->Failed());
+
+  WebURL script_response_url = main_script_loader_->ResponseURL();
+  DCHECK(script_request_url_ == script_response_url ||
+         SecurityOrigin::AreSameSchemeHostPort(script_request_url_,
+                                               script_response_url));
   auto referrer_policy = network::mojom::ReferrerPolicy::kDefault;
   if (!main_script_loader_->GetReferrerPolicy().IsNull()) {
     SecurityPolicy::ReferrerPolicyFromHeaderValue(
         main_script_loader_->GetReferrerPolicy(),
         kDoNotSupportReferrerPolicyLegacyKeywords, &referrer_policy);
   }
+  ContentSecurityPolicy* content_security_policy =
+      main_script_loader_->GetContentSecurityPolicy();
 
-  // TODO(nhiroki); Set |script_type| to mojom::ScriptType::kModule for module
-  // fetch (https://crbug.com/824646).
-  mojom::ScriptType script_type = mojom::ScriptType::kClassic;
-
-  const KURL script_response_url = main_script_loader_->ResponseURL();
-  DCHECK(static_cast<KURL>(script_request_url_) == script_response_url ||
-         SecurityOrigin::AreSameSchemeHostPort(script_request_url_,
-                                               script_response_url));
-
-  auto global_scope_creation_params =
-      std::make_unique<GlobalScopeCreationParams>(
-          script_response_url, script_type,
-          // TODO(nhiroki): Implement off-the-main-thread worker script fetch
-          // for shared workers (https://crbug.com/835717).
-          OffMainThreadWorkerScriptFetchOption::kDisabled,
-          document->UserAgent(), std::move(web_worker_fetch_context),
-          content_security_policy ? content_security_policy->Headers()
-                                  : Vector<CSPHeaderAndType>(),
-          referrer_policy, outside_settings_object->GetSecurityOrigin(),
-          document->IsSecureContext(), outside_settings_object->GetHttpsState(),
-          CreateWorkerClients(), main_script_loader_->ResponseAddressSpace(),
-          main_script_loader_->OriginTrialTokens(), devtools_worker_token_,
-          std::make_unique<WorkerSettings>(document->GetFrame()->GetSettings()),
-          kV8CacheOptionsDefault, nullptr /* worklet_module_response_map */,
-          std::move(pending_interface_provider_));
-  StartWorkerThread(std::move(global_scope_creation_params),
-                    script_response_url, main_script_loader_->SourceText());
-
+  auto creation_params = std::make_unique<GlobalScopeCreationParams>(
+      script_response_url, script_type,
+      OffMainThreadWorkerScriptFetchOption::kDisabled, document->UserAgent(),
+      std::move(web_worker_fetch_context),
+      content_security_policy ? content_security_policy->Headers()
+                              : Vector<CSPHeaderAndType>(),
+      referrer_policy, outside_settings_object->GetSecurityOrigin(),
+      document->IsSecureContext(), outside_settings_object->GetHttpsState(),
+      CreateWorkerClients(), main_script_loader_->ResponseAddressSpace(),
+      main_script_loader_->OriginTrialTokens(), devtools_worker_token_,
+      std::make_unique<WorkerSettings>(document->GetFrame()->GetSettings()),
+      kV8CacheOptionsDefault, nullptr /* worklet_module_response_map */,
+      std::move(pending_interface_provider_));
+  StartWorkerThread(std::move(creation_params), script_response_url,
+                    main_script_loader_->SourceText(), outside_settings_object);
   main_script_loader_ = nullptr;
 }
 
 void WebSharedWorkerImpl::StartWorkerThread(
     std::unique_ptr<GlobalScopeCreationParams> global_scope_creation_params,
     const KURL& script_response_url,
-    const String& source_code) {
+    const String& source_code,
+    FetchClientSettingsObjectSnapshot* outside_settings_object) {
   DCHECK(IsMainThread());
   reporting_proxy_ = MakeGarbageCollected<SharedWorkerReportingProxy>(
       this, parent_execution_context_task_runners_);
@@ -391,10 +421,19 @@
                            thread_startup_data, std::move(devtools_params),
                            parent_execution_context_task_runners_);
   // TODO(nhiroki): Support module workers (https://crbug.com/680046).
-  GetWorkerThread()->EvaluateClassicScript(script_response_url, source_code,
-                                           nullptr /* cached_meta_data */,
+  if (features::IsOffMainThreadSharedWorkerScriptFetchEnabled()) {
+    // The script has not yet been fetched. Fetch it now.
+    GetWorkerThread()->ImportClassicScript(script_request_url_,
+                                           outside_settings_object,
                                            v8_inspector::V8StackTraceId());
-  client_->WorkerScriptLoaded();
+    // We continue in WorkerGlobalScope::EvaluateClassicScript() on the worker
+    // thread.
+  } else {
+    // The script was already fetched on the main thread. Evaluate it now.
+    GetWorkerThread()->EvaluateClassicScript(script_response_url, source_code,
+                                             nullptr /* cached_meta_data */,
+                                             v8_inspector::V8StackTraceId());
+  }
 }
 
 WorkerClients* WebSharedWorkerImpl::CreateWorkerClients() {
diff --git a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
index 30036f8..1533cfb 100644
--- a/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
+++ b/third_party/blink/renderer/core/exported/web_shared_worker_impl.h
@@ -123,10 +123,12 @@
 
   void DidReceiveScriptLoaderResponse();
   void OnScriptLoaderFinished();
-  void ContinueOnScriptLoaderFinished();
-  void StartWorkerThread(std::unique_ptr<GlobalScopeCreationParams>,
-                         const KURL& script_response_url,
-                         const String& source_code);
+  void ContinueStartWorkerContext();
+  void StartWorkerThread(
+      std::unique_ptr<GlobalScopeCreationParams>,
+      const KURL& script_response_url,
+      const String& source_code,
+      FetchClientSettingsObjectSnapshot* outside_settings_object);
   WorkerClients* CreateWorkerClients();
 
   void ConnectTaskOnWorkerThread(MessagePortChannel);
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 6009bd1e..62ed1e6 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.cc
+++ b/third_party/blink/renderer/core/exported/web_view_impl.cc
@@ -1270,6 +1270,15 @@
 // WebWidget ------------------------------------------------------------------
 
 void WebViewImpl::Close() {
+  if (layer_tree_view_)
+    GetPage()->WillCloseLayerTreeView(*layer_tree_view_, nullptr);
+
+  SetRootLayer(nullptr);
+  animation_host_ = nullptr;
+
+  mutator_dispatcher_ = nullptr;
+  layer_tree_view_ = nullptr;
+
   DCHECK(AllInstances().Contains(this));
   AllInstances().erase(this);
 
@@ -1936,17 +1945,6 @@
   return !!root_layer_;
 }
 
-void WebViewImpl::WillCloseLayerTreeView() {
-  if (layer_tree_view_)
-    GetPage()->WillCloseLayerTreeView(*layer_tree_view_, nullptr);
-
-  SetRootLayer(nullptr);
-  animation_host_ = nullptr;
-
-  mutator_dispatcher_ = nullptr;
-  layer_tree_view_ = nullptr;
-}
-
 void WebViewImpl::DidAcquirePointerLock() {
   if (MainFrameImpl())
     MainFrameImpl()->FrameWidget()->DidAcquirePointerLock();
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 7f8031ee..18326e0 100644
--- a/third_party/blink/renderer/core/exported/web_view_impl.h
+++ b/third_party/blink/renderer/core/exported/web_view_impl.h
@@ -460,7 +460,6 @@
   void SetFocus(bool enable) override;
   bool SelectionBounds(WebRect& anchor, WebRect& focus) const override;
   bool IsAcceleratedCompositingActive() const override;
-  void WillCloseLayerTreeView() override;
   void DidAcquirePointerLock() override;
   void DidNotAcquirePointerLock() override;
   void DidLosePointerLock() override;
diff --git a/third_party/blink/renderer/core/fetch/BUILD.gn b/third_party/blink/renderer/core/fetch/BUILD.gn
index f13837d..aa9a18b 100644
--- a/third_party/blink/renderer/core/fetch/BUILD.gn
+++ b/third_party/blink/renderer/core/fetch/BUILD.gn
@@ -12,8 +12,6 @@
     "body.h",
     "body_stream_buffer.cc",
     "body_stream_buffer.h",
-    "buffering_bytes_consumer.cc",
-    "buffering_bytes_consumer.h",
     "bytes_consumer_tee.cc",
     "bytes_consumer_tee.h",
     "fetch_data_loader.cc",
diff --git a/third_party/blink/renderer/core/fetch/buffering_bytes_consumer.cc b/third_party/blink/renderer/core/fetch/buffering_bytes_consumer.cc
deleted file mode 100644
index d87337c36..0000000
--- a/third_party/blink/renderer/core/fetch/buffering_bytes_consumer.cc
+++ /dev/null
@@ -1,144 +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 "third_party/blink/renderer/core/fetch/buffering_bytes_consumer.h"
-
-#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
-
-namespace blink {
-
-BufferingBytesConsumer::BufferingBytesConsumer(BytesConsumer* bytes_consumer)
-    : bytes_consumer_(bytes_consumer) {
-  bytes_consumer_->SetClient(this);
-}
-
-BufferingBytesConsumer::~BufferingBytesConsumer() = default;
-
-BytesConsumer::Result BufferingBytesConsumer::BeginRead(const char** buffer,
-                                                        size_t* available) {
-  if (buffer_.IsEmpty()) {
-    if (has_seen_error_)
-      return Result::kError;
-
-    if (has_seen_end_of_data_) {
-      ClearClient();
-      return Result::kDone;
-    }
-
-    BufferData();
-
-    if (has_seen_error_)
-      return Result::kError;
-
-    if (buffer_.IsEmpty())
-      return has_seen_end_of_data_ ? Result::kDone : Result::kShouldWait;
-  }
-
-  DCHECK_LT(offset_for_first_chunk_, buffer_[0].size());
-  *buffer = buffer_[0].data() + offset_for_first_chunk_;
-  *available = buffer_[0].size() - offset_for_first_chunk_;
-  return Result::kOk;
-}
-
-BytesConsumer::Result BufferingBytesConsumer::EndRead(size_t read_size) {
-  if (buffer_.IsEmpty()) {
-    DCHECK(has_seen_error_);
-    return Result::kError;
-  }
-
-  DCHECK_LE(offset_for_first_chunk_ + read_size, buffer_[0].size());
-  offset_for_first_chunk_ += read_size;
-
-  if (offset_for_first_chunk_ == buffer_[0].size()) {
-    offset_for_first_chunk_ = 0;
-    buffer_.pop_front();
-  }
-
-  if (buffer_.IsEmpty() && has_seen_end_of_data_) {
-    ClearClient();
-    return Result::kDone;
-  }
-  return Result::kOk;
-}
-
-scoped_refptr<BlobDataHandle> BufferingBytesConsumer::DrainAsBlobDataHandle(
-    BlobSizePolicy policy) {
-  return bytes_consumer_->DrainAsBlobDataHandle(policy);
-}
-
-scoped_refptr<EncodedFormData> BufferingBytesConsumer::DrainAsFormData() {
-  return bytes_consumer_->DrainAsFormData();
-}
-
-mojo::ScopedDataPipeConsumerHandle BufferingBytesConsumer::DrainAsDataPipe() {
-  // We intentionally return an empty handle here, because returning a DataPipe
-  // may activate back pressure.
-  return {};
-}
-
-void BufferingBytesConsumer::SetClient(BytesConsumer::Client* client) {
-  client_ = client;
-}
-
-void BufferingBytesConsumer::ClearClient() {
-  client_ = nullptr;
-}
-
-void BufferingBytesConsumer::Cancel() {
-  ClearClient();
-  bytes_consumer_->Cancel();
-}
-
-BytesConsumer::PublicState BufferingBytesConsumer::GetPublicState() const {
-  if (buffer_.IsEmpty())
-    return bytes_consumer_->GetPublicState();
-  return PublicState::kReadableOrWaiting;
-}
-
-BytesConsumer::Error BufferingBytesConsumer::GetError() const {
-  return bytes_consumer_->GetError();
-}
-
-void BufferingBytesConsumer::Trace(Visitor* visitor) {
-  visitor->Trace(bytes_consumer_);
-  visitor->Trace(client_);
-  BytesConsumer::Trace(visitor);
-  BytesConsumer::Client::Trace(visitor);
-}
-
-void BufferingBytesConsumer::OnStateChange() {
-  BytesConsumer::Client* client = client_;
-  BufferData();
-  if (client)
-    client->OnStateChange();
-}
-
-void BufferingBytesConsumer::BufferData() {
-  while (true) {
-    const char* p = nullptr;
-    size_t available = 0;
-    auto result = bytes_consumer_->BeginRead(&p, &available);
-    if (result == Result::kShouldWait)
-      return;
-    if (result == Result::kOk) {
-      Vector<char> chunk;
-      chunk.Append(p, SafeCast<wtf_size_t>(available));
-      buffer_.push_back(std::move(chunk));
-      result = bytes_consumer_->EndRead(available);
-    }
-    if (result == Result::kDone) {
-      has_seen_end_of_data_ = true;
-      ClearClient();
-      return;
-    }
-    if (result != Result::kOk) {
-      buffer_.clear();
-      has_seen_error_ = true;
-      ClearClient();
-      return;
-    }
-  }
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/fetch/buffering_bytes_consumer.h b/third_party/blink/renderer/core/fetch/buffering_bytes_consumer.h
deleted file mode 100644
index d41e2a8..0000000
--- a/third_party/blink/renderer/core/fetch/buffering_bytes_consumer.h
+++ /dev/null
@@ -1,69 +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 THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BUFFERING_BYTES_CONSUMER_H_
-#define THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BUFFERING_BYTES_CONSUMER_H_
-
-#include <memory>
-
-#include "base/memory/scoped_refptr.h"
-#include "third_party/blink/renderer/core/core_export.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/deque.h"
-#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
-#include "third_party/blink/renderer/platform/wtf/vector.h"
-
-namespace blink {
-
-// BufferingBytesConsumer is a BytesConsumer. It takes a BytesConsumer
-// ("the original BytesConsumer") as a constructor parameter, and results read
-// from the BufferingBytesConsumer are as same as results which would be read
-// from the original BytesConsumer.
-// BufferingBytesConsumer buffers reads chunks from the original BytesConsumer
-// and store it until they are read, before read requests are issued from the
-// client.
-class CORE_EXPORT BufferingBytesConsumer final : public BytesConsumer,
-                                                 private BytesConsumer::Client {
-  USING_GARBAGE_COLLECTED_MIXIN(BufferingBytesConsumer);
-
- public:
-  // Creates a BufferingBytesConsumer. |bytes_consumer| is the original
-  // BytesConsumer.
-  // |bytes_consumer| must not have a client.
-  explicit BufferingBytesConsumer(BytesConsumer* bytes_consumer);
-  ~BufferingBytesConsumer() override;
-
-  // BufferingBytesConsumer
-  Result BeginRead(const char** buffer, size_t* available) override;
-  Result EndRead(size_t read_size) override;
-  scoped_refptr<BlobDataHandle> DrainAsBlobDataHandle(BlobSizePolicy) override;
-  scoped_refptr<EncodedFormData> DrainAsFormData() override;
-  mojo::ScopedDataPipeConsumerHandle DrainAsDataPipe() override;
-  void SetClient(BytesConsumer::Client*) override;
-  void ClearClient() override;
-  void Cancel() override;
-  PublicState GetPublicState() const override;
-  Error GetError() const override;
-  String DebugName() const override { return "BufferingBytesConsumer"; }
-
-  void Trace(blink::Visitor*) override;
-
- private:
-  // BufferingBytesConsumer::Client
-  void OnStateChange() override;
-  void BufferData();
-
-  const TraceWrapperMember<BytesConsumer> bytes_consumer_;
-  Deque<Vector<char>> buffer_;
-  size_t offset_for_first_chunk_ = 0;
-  bool has_seen_end_of_data_ = false;
-  bool has_seen_error_ = false;
-  Member<BytesConsumer::Client> client_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FETCH_BUFFERING_BYTES_CONSUMER_H_
diff --git a/third_party/blink/renderer/core/fetch/buffering_bytes_consumer_test.cc b/third_party/blink/renderer/core/fetch/buffering_bytes_consumer_test.cc
deleted file mode 100644
index 0195d427..0000000
--- a/third_party/blink/renderer/core/fetch/buffering_bytes_consumer_test.cc
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2018 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/core/fetch/buffering_bytes_consumer.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
-#include "third_party/blink/renderer/core/dom/document.h"
-#include "third_party/blink/renderer/core/fetch/bytes_consumer_test_util.h"
-#include "third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h"
-#include "third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h"
-
-namespace blink {
-namespace {
-
-class BufferingBytesConsumerTest : public testing::Test {
- public:
-  using Command = ReplayingBytesConsumer::Command;
-  using Result = BytesConsumer::Result;
-  using PublicState = BytesConsumer::PublicState;
-
-  static String CharVectorToString(const Vector<char>& x) {
-    return BytesConsumerTestUtil::CharVectorToString(x);
-  }
-};
-
-TEST_F(BufferingBytesConsumerTest, Read) {
-  V8TestingScope scope;
-  auto* replaying_bytes_consumer = MakeGarbageCollected<ReplayingBytesConsumer>(
-      scope.GetDocument().GetTaskRunner(TaskType::kNetworking));
-
-  replaying_bytes_consumer->Add(Command(Command::kWait));
-  replaying_bytes_consumer->Add(Command(Command::kData, "1"));
-  replaying_bytes_consumer->Add(Command(Command::kWait));
-  replaying_bytes_consumer->Add(Command(Command::kWait));
-  replaying_bytes_consumer->Add(Command(Command::kData, "23"));
-  replaying_bytes_consumer->Add(Command(Command::kData, "4"));
-  replaying_bytes_consumer->Add(Command(Command::kData, "567"));
-  replaying_bytes_consumer->Add(Command(Command::kData, "8"));
-  replaying_bytes_consumer->Add(Command(Command::kDone));
-
-  auto* bytes_consumer =
-      MakeGarbageCollected<BufferingBytesConsumer>(replaying_bytes_consumer);
-
-  EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState());
-  auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(bytes_consumer);
-  auto result = reader->Run();
-
-  EXPECT_EQ(PublicState::kClosed, bytes_consumer->GetPublicState());
-  ASSERT_EQ(result.first, Result::kDone);
-  EXPECT_EQ("12345678", CharVectorToString(result.second));
-}
-
-}  // namespace
-}  // namespace blink
diff --git a/third_party/blink/renderer/core/fetch/fetch_manager.cc b/third_party/blink/renderer/core/fetch/fetch_manager.cc
index bf2979b4..d8bb8c2 100644
--- a/third_party/blink/renderer/core/fetch/fetch_manager.cc
+++ b/third_party/blink/renderer/core/fetch/fetch_manager.cc
@@ -14,7 +14,6 @@
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/fetch/body.h"
 #include "third_party/blink/renderer/core/fetch/body_stream_buffer.h"
-#include "third_party/blink/renderer/core/fetch/buffering_bytes_consumer.h"
 #include "third_party/blink/renderer/core/fetch/fetch_request_data.h"
 #include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h"
 #include "third_party/blink/renderer/core/fetch/response.h"
@@ -36,6 +35,7 @@
 #include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/loader/cors/cors.h"
+#include "third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h"
 #include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h"
 #include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer_for_data_consumer_handle.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_initiator_type_names.h"
@@ -111,7 +111,9 @@
   return !value.IsEmpty();
 }
 
-class SRIBytesConsumer final : public BytesConsumer {
+// A BytesConsumer implementation that acts as a place holder. The actual
+// BytesConsumer can be provided later by calling "Update()".
+class PlaceHolderBytesConsumer final : public BytesConsumer {
  public:
   // BytesConsumer implementation
   Result BeginRead(const char** buffer, size_t* available) override {
@@ -165,7 +167,7 @@
     // We must not be in the errored state until we get updated.
     return underlying_->GetError();
   }
-  String DebugName() const override { return "SRIBytesConsumer"; }
+  String DebugName() const override { return "PlaceHolderBytesConsumer"; }
 
   // This function can be called at most once.
   void Update(BytesConsumer* consumer) {
@@ -238,22 +240,18 @@
   void Abort();
 
   class SRIVerifier final : public GarbageCollectedFinalized<SRIVerifier>,
-                            public WebDataConsumerHandle::Client {
+                            public BytesConsumer::Client {
+    USING_GARBAGE_COLLECTED_MIXIN(SRIVerifier);
+
    public:
-    // Promptly clear m_handle and m_reader.
-    EAGERLY_FINALIZE();
-    // SRIVerifier takes ownership of |handle| and |response|.
-    // |updater| must be garbage collected. The other arguments
-    // all must have the lifetime of the give loader.
-    SRIVerifier(std::unique_ptr<WebDataConsumerHandle> handle,
-                SRIBytesConsumer* updater,
+    SRIVerifier(BytesConsumer* body,
+                PlaceHolderBytesConsumer* updater,
                 Response* response,
                 FetchManager::Loader* loader,
                 String integrity_metadata,
                 const KURL& url,
-                FetchResponseType response_type,
-                scoped_refptr<base::SingleThreadTaskRunner> task_runner)
-        : handle_(std::move(handle)),
+                FetchResponseType response_type)
+        : body_(body),
           updater_(updater),
           response_(response),
           loader_(loader),
@@ -261,37 +259,34 @@
           url_(url),
           response_type_(response_type),
           finished_(false) {
-      reader_ = handle_->ObtainReader(this, std::move(task_runner));
+      body_->SetClient(this);
+
+      OnStateChange();
     }
 
-    void Cancel() {
-      reader_ = nullptr;
-      handle_ = nullptr;
-    }
+    void Cancel() { body_->Cancel(); }
 
-    void DidGetReadable() override {
-      DCHECK(reader_);
+    void OnStateChange() override {
+      using Result = BytesConsumer::Result;
+
       DCHECK(loader_);
       DCHECK(response_);
 
-      WebDataConsumerHandle::Result r = WebDataConsumerHandle::kOk;
-      while (r == WebDataConsumerHandle::kOk) {
-        const void* buffer;
-        size_t size;
-        r = reader_->BeginRead(&buffer, WebDataConsumerHandle::kFlagNone,
-                               &size);
-        if (r == WebDataConsumerHandle::kOk) {
-          buffer_.Append(static_cast<const char*>(buffer),
-                         SafeCast<wtf_size_t>(size));
-          reader_->EndRead(size);
+      Result result = Result::kOk;
+      while (result == Result::kOk) {
+        const char* buffer;
+        size_t available;
+        result = body_->BeginRead(&buffer, &available);
+        if (result == Result::kOk) {
+          buffer_.Append(buffer, SafeCast<wtf_size_t>(available));
+          result = body_->EndRead(available);
         }
+        if (result == Result::kShouldWait)
+          return;
       }
-      if (r == WebDataConsumerHandle::kShouldWait)
-        return;
-      String error_message =
-          "Unknown error occurred while trying to verify integrity.";
+
       finished_ = true;
-      if (r == WebDataConsumerHandle::kDone) {
+      if (result == Result::kDone) {
         SubresourceIntegrity::ReportInfo report_info;
         bool check_result = true;
         if (response_type_ != FetchResponseType::kBasic &&
@@ -328,22 +323,27 @@
           return;
         }
       }
+      String error_message =
+          "Unknown error occurred while trying to verify integrity.";
       updater_->Update(
           BytesConsumer::CreateErrored(BytesConsumer::Error(error_message)));
       loader_->PerformNetworkError(error_message);
     }
 
+    String DebugName() const override { return "SRIVerifier"; }
+
     bool IsFinished() const { return finished_; }
 
-    void Trace(blink::Visitor* visitor) {
+    void Trace(blink::Visitor* visitor) override {
+      visitor->Trace(body_);
       visitor->Trace(updater_);
       visitor->Trace(response_);
       visitor->Trace(loader_);
     }
 
    private:
-    std::unique_ptr<WebDataConsumerHandle> handle_;
-    Member<SRIBytesConsumer> updater_;
+    Member<BytesConsumer> body_;
+    Member<PlaceHolderBytesConsumer> updater_;
     // We cannot store a Response because its JS wrapper can be collected.
     // TODO(yhirano): Fix this.
     Member<Response> response_;
@@ -351,7 +351,6 @@
     String integrity_metadata_;
     KURL url_;
     const FetchResponseType response_type_;
-    std::unique_ptr<WebDataConsumerHandle::Reader> reader_;
     Vector<char> buffer_;
     bool finished_;
   };
@@ -533,7 +532,7 @@
   }
 
   FetchResponseData* response_data = nullptr;
-  SRIBytesConsumer* sri_consumer = nullptr;
+  PlaceHolderBytesConsumer* sri_consumer = nullptr;
   if (fetch_request_data_->Integrity().IsEmpty()) {
     BytesConsumer* bytes_consumer =
         MakeGarbageCollected<BytesConsumerForDataConsumerHandle>(
@@ -553,7 +552,7 @@
         MakeGarbageCollected<BodyStreamBuffer>(script_state, bytes_consumer,
                                                signal_));
   } else {
-    sri_consumer = MakeGarbageCollected<SRIBytesConsumer>();
+    sri_consumer = MakeGarbageCollected<PlaceHolderBytesConsumer>();
     response_data = FetchResponseData::CreateWithBuffer(
         MakeGarbageCollected<BodyStreamBuffer>(script_state, sri_consumer,
                                                signal_));
@@ -631,10 +630,11 @@
   } else {
     DCHECK(!integrity_verifier_);
     integrity_verifier_ = MakeGarbageCollected<SRIVerifier>(
-        std::move(handle), sri_consumer, r, this,
-        fetch_request_data_->Integrity(), response.CurrentRequestUrl(),
-        r->GetResponse()->GetType(),
-        resolver_->GetExecutionContext()->GetTaskRunner(TaskType::kNetworking));
+        MakeGarbageCollected<BytesConsumerForDataConsumerHandle>(
+            GetExecutionContext()->GetTaskRunner(TaskType::kNetworking),
+            std::move(handle)),
+        sri_consumer, r, this, fetch_request_data_->Integrity(),
+        response.CurrentRequestUrl(), r->GetResponse()->GetType());
   }
 }
 
diff --git a/third_party/blink/renderer/core/frame/BUILD.gn b/third_party/blink/renderer/core/frame/BUILD.gn
index 3c8b749f..9ba1a67 100644
--- a/third_party/blink/renderer/core/frame/BUILD.gn
+++ b/third_party/blink/renderer/core/frame/BUILD.gn
@@ -107,6 +107,8 @@
     "navigator_language.cc",
     "navigator_language.h",
     "navigator_on_line.h",
+    "navigator_scheduling.cc",
+    "navigator_scheduling.h",
     "navigator_user_activation.cc",
     "navigator_user_activation.h",
     "opened_frame_tracker.cc",
@@ -152,6 +154,8 @@
     "rotation_viewport_anchor.h",
     "sandbox_flags.cc",
     "sandbox_flags.h",
+    "scheduling.cc",
+    "scheduling.h",
     "screen.cc",
     "screen.h",
     "screen_orientation_controller.cc",
diff --git a/third_party/blink/renderer/core/frame/frame_overlay_test.cc b/third_party/blink/renderer/core/frame/frame_overlay_test.cc
index eac7cc1..d84e728e 100644
--- a/third_party/blink/renderer/core/frame/frame_overlay_test.cc
+++ b/third_party/blink/renderer/core/frame/frame_overlay_test.cc
@@ -81,7 +81,7 @@
   MOCK_METHOD2(onDrawRect, void(const SkRect&, const SkPaint&));
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(FrameOverlayTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(FrameOverlayTest);
 
 TEST_P(FrameOverlayTest, AcceleratedCompositing) {
   std::unique_ptr<FrameOverlay> frame_overlay = CreateSolidYellowOverlay();
diff --git a/third_party/blink/renderer/core/frame/frame_serializer.cc b/third_party/blink/renderer/core/frame/frame_serializer.cc
index 02eaf6c..0d52c152 100644
--- a/third_party/blink/renderer/core/frame/frame_serializer.cc
+++ b/third_party/blink/renderer/core/frame/frame_serializer.cc
@@ -89,25 +89,17 @@
   ~SerializerMarkupAccumulator() override;
 
  protected:
-  void AppendCustomAttributes(StringBuilder&,
-                              const Element&,
-                              Namespaces*) override;
-  void AppendText(StringBuilder& out, Text&) override;
+  void AppendCustomAttributes(const Element&, Namespaces&) override;
   bool ShouldIgnoreAttribute(const Element&, const Attribute&) const override;
   bool ShouldIgnoreElement(const Element&) const override;
-  void AppendElement(StringBuilder& out, const Element&, Namespaces*) override;
-  void AppendAttribute(StringBuilder& out,
-                       const Element&,
-                       const Attribute&,
-                       Namespaces*) override;
-  void AppendStartTag(Node&, Namespaces* = nullptr) override;
-  void AppendEndTag(const Element&) override;
+  void AppendElement(const Element&, Namespaces&) override;
+  void AppendAttribute(const Element&, const Attribute&, Namespaces&) override;
+  void AppendStartMarkup(Node&, Namespaces&) override;
   std::pair<Node*, Element*> GetAuxiliaryDOMTree(const Element&) const override;
 
  private:
-  void AppendAttributeValue(StringBuilder& out, const String& attribute_value);
-  void AppendRewrittenAttribute(StringBuilder& out,
-                                const Element&,
+  void AppendAttributeValue(const String& attribute_value);
+  void AppendRewrittenAttribute(const Element&,
                                 const String& attribute_name,
                                 const String& attribute_value);
 
@@ -136,17 +128,11 @@
 SerializerMarkupAccumulator::~SerializerMarkupAccumulator() = default;
 
 void SerializerMarkupAccumulator::AppendCustomAttributes(
-    StringBuilder& result,
     const Element& element,
-    Namespaces* namespaces) {
+    Namespaces& namespaces) {
   Vector<Attribute> attributes = delegate_.GetCustomAttributes(element);
   for (const auto& attribute : attributes)
-    AppendAttribute(result, element, attribute, namespaces);
-}
-
-void SerializerMarkupAccumulator::AppendText(StringBuilder& result,
-                                             Text& text) {
-  MarkupAccumulator::AppendText(result, text);
+    AppendAttribute(element, attribute, namespaces);
 }
 
 bool SerializerMarkupAccumulator::ShouldIgnoreAttribute(
@@ -168,32 +154,30 @@
   return delegate_.ShouldIgnoreElement(element);
 }
 
-void SerializerMarkupAccumulator::AppendElement(StringBuilder& result,
-                                                const Element& element,
-                                                Namespaces* namespaces) {
-  MarkupAccumulator::AppendElement(result, element, namespaces);
+void SerializerMarkupAccumulator::AppendElement(const Element& element,
+                                                Namespaces& namespaces) {
+  MarkupAccumulator::AppendElement(element, namespaces);
 
   // TODO(tiger): Refactor MarkupAccumulator so it is easier to append an
   // element like this, without special cases for XHTML
   if (IsHTMLHeadElement(element)) {
-    result.Append("<meta http-equiv=\"Content-Type\" content=\"");
-    AppendAttributeValue(result, document_->SuggestedMIMEType());
-    result.Append("; charset=");
-    AppendAttributeValue(result, document_->characterSet());
+    markup_.Append("<meta http-equiv=\"Content-Type\" content=\"");
+    AppendAttributeValue(document_->SuggestedMIMEType());
+    markup_.Append("; charset=");
+    AppendAttributeValue(document_->characterSet());
     if (document_->IsXHTMLDocument())
-      result.Append("\" />");
+      markup_.Append("\" />");
     else
-      result.Append("\">");
+      markup_.Append("\">");
   }
 
   // FIXME: For object (plugins) tags and video tag we could replace them by an
   // image of their current contents.
 }
 
-void SerializerMarkupAccumulator::AppendAttribute(StringBuilder& out,
-                                                  const Element& element,
+void SerializerMarkupAccumulator::AppendAttribute(const Element& element,
                                                   const Attribute& attribute,
-                                                  Namespaces* namespaces) {
+                                                  Namespaces& namespaces) {
   // Check if link rewriting can affect the attribute.
   bool is_link_attribute = element.HasLegalLinkAttribute(attribute.GetName());
   bool is_src_doc_attribute = IsHTMLFrameElementBase(element) &&
@@ -204,7 +188,7 @@
     if (delegate_.RewriteLink(element, new_link_for_the_element)) {
       if (is_link_attribute) {
         // Rewrite element links.
-        AppendRewrittenAttribute(out, element, attribute.GetName().ToString(),
+        AppendRewrittenAttribute(element, attribute.GetName().ToString(),
                                  new_link_for_the_element);
       } else {
         DCHECK(is_src_doc_attribute);
@@ -212,7 +196,7 @@
         // serialized subframe to use html contents from the link provided by
         // Delegate::rewriteLink rather than html contents from srcdoc
         // attribute.
-        AppendRewrittenAttribute(out, element, html_names::kSrcAttr.LocalName(),
+        AppendRewrittenAttribute(element, html_names::kSrcAttr.LocalName(),
                                  new_link_for_the_element);
       }
       return;
@@ -220,33 +204,27 @@
   }
 
   // Fallback to appending the original attribute.
-  MarkupAccumulator::AppendAttribute(out, element, attribute, namespaces);
+  MarkupAccumulator::AppendAttribute(element, attribute, namespaces);
 }
 
-void SerializerMarkupAccumulator::AppendStartTag(Node& node,
-                                                 Namespaces* namespaces) {
-  MarkupAccumulator::AppendStartTag(node, namespaces);
+void SerializerMarkupAccumulator::AppendStartMarkup(Node& node,
+                                                    Namespaces& namespaces) {
+  MarkupAccumulator::AppendStartMarkup(node, namespaces);
   nodes_.push_back(&node);
 }
 
-void SerializerMarkupAccumulator::AppendEndTag(const Element& element) {
-  MarkupAccumulator::AppendEndTag(element);
-}
-
 std::pair<Node*, Element*> SerializerMarkupAccumulator::GetAuxiliaryDOMTree(
     const Element& element) const {
   return delegate_.GetAuxiliaryDOMTree(element);
 }
 
 void SerializerMarkupAccumulator::AppendAttributeValue(
-    StringBuilder& out,
     const String& attribute_value) {
-  MarkupFormatter::AppendAttributeValue(out, attribute_value,
+  MarkupFormatter::AppendAttributeValue(markup_, attribute_value,
                                         document_->IsHTMLDocument());
 }
 
 void SerializerMarkupAccumulator::AppendRewrittenAttribute(
-    StringBuilder& out,
     const Element& element,
     const String& attribute_name,
     const String& attribute_value) {
@@ -257,11 +235,11 @@
   // Append the rewritten attribute.
   // TODO(tiger): Refactor MarkupAccumulator so it is easier to append an
   // attribute like this.
-  out.Append(' ');
-  out.Append(attribute_name);
-  out.Append("=\"");
-  AppendAttributeValue(out, attribute_value);
-  out.Append("\"");
+  markup_.Append(' ');
+  markup_.Append(attribute_name);
+  markup_.Append("=\"");
+  AppendAttributeValue(attribute_value);
+  markup_.Append("\"");
 }
 
 // TODO(tiger): Right now there is no support for rewriting URLs inside CSS
@@ -303,7 +281,7 @@
     SerializerMarkupAccumulator accumulator(delegate_, document,
                                             serialized_nodes);
     String text =
-        SerializeNodes<EditingStrategy>(accumulator, document, kIncludeNode);
+        accumulator.SerializeNodes<EditingStrategy>(document, kIncludeNode);
 
     CString frame_html =
         document.Encoding().Encode(text, WTF::kEntitiesForUnencodables);
diff --git a/third_party/blink/renderer/core/frame/frame_test_helpers.cc b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
index b0ef5f68..415fa71 100644
--- a/third_party/blink/renderer/core/frame/frame_test_helpers.cc
+++ b/third_party/blink/renderer/core/frame/frame_test_helpers.cc
@@ -492,14 +492,8 @@
 }
 
 void TestWebFrameClient::FrameDetached(DetachType type) {
-  if (frame_->FrameWidget()) {
-    // TODO(dcheng): This shouldn't exclude the main frame, but it is currently
-    // unsafe to close the LTV for the main frame: that's handled separately by
-    // closing the entire WebView itself.
-    if (frame_->Parent())
-      frame_->FrameWidget()->WillCloseLayerTreeView();
+  if (frame_->FrameWidget())
     frame_->FrameWidget()->Close();
-  }
 
   owned_widget_client_.reset();
   frame_->Close();
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 fecf84b9..f077f51 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -1335,11 +1335,6 @@
       }
     }
 
-    TRACE_EVENT_INSTANT1(
-        TRACE_DISABLED_BY_DEFAULT("devtools.timeline.invalidationTracking"),
-        "ScrollInvalidationTracking", TRACE_EVENT_SCOPE_THREAD, "data",
-        inspector_scroll_invalidation_tracking_event::Data(*layout_object));
-
     // If the fixed layer has a blur/drop-shadow filter applied on at least one
     // of its parents, we cannot scroll using the fast path, otherwise the
     // outsets of the filter will be moved around the page.
@@ -2633,13 +2628,8 @@
     // Devtools overlays query the inspected page's paint data so this update
     // needs to be after other paintings. Because devtools overlays can add
     // layers, this needs to be before layers are collected.
-    auto* web_local_frame_impl = WebLocalFrameImpl::FromFrame(frame_);
-    if (web_local_frame_impl && web_local_frame_impl->HasDevToolsOverlays()) {
+    if (auto* web_local_frame_impl = WebLocalFrameImpl::FromFrame(frame_))
       web_local_frame_impl->UpdateDevToolsOverlays();
-      // Devtools overlays can change cc::Layer property tree nodes and we need
-      // to ensure these updated values are pushed to the compositor.
-      SetPaintArtifactCompositorNeedsUpdate();
-    }
   }
 
   ForAllNonThrottledLocalFrameViews([](LocalFrameView& frame_view) {
diff --git a/third_party/blink/renderer/core/frame/navigator.cc b/third_party/blink/renderer/core/frame/navigator.cc
index 2d22c0be..3e5a25e3 100644
--- a/third_party/blink/renderer/core/frame/navigator.cc
+++ b/third_party/blink/renderer/core/frame/navigator.cc
@@ -33,7 +33,7 @@
 #include "third_party/blink/renderer/core/page/chrome_client.h"
 #include "third_party/blink/renderer/core/page/page.h"
 #include "third_party/blink/renderer/platform/language.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 
 namespace blink {
 
diff --git a/third_party/blink/renderer/core/frame/navigator_language.cc b/third_party/blink/renderer/core/frame/navigator_language.cc
index d2f3e4c..10e8d1cf 100644
--- a/third_party/blink/renderer/core/frame/navigator_language.cc
+++ b/third_party/blink/renderer/core/frame/navigator_language.cc
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/renderer/core/probe/core_probes.h"
 #include "third_party/blink/renderer/platform/language.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
 
 namespace blink {
 
@@ -37,6 +38,38 @@
 }
 
 const Vector<String>& NavigatorLanguage::languages() {
+  EnsureUpdatedLanguage();
+  return languages_;
+}
+
+AtomicString NavigatorLanguage::SerializeLanguagesForClientHintHeader() {
+  EnsureUpdatedLanguage();
+
+  StringBuilder builder;
+  for (size_t i = 0; i < languages_.size(); i++) {
+    if (i)
+      builder.Append(", ");
+    builder.Append('"');
+    builder.Append(languages_[i]);
+    builder.Append('"');
+  }
+  return builder.ToAtomicString();
+}
+
+bool NavigatorLanguage::IsLanguagesDirty() const {
+  return languages_dirty_;
+}
+
+void NavigatorLanguage::SetLanguagesDirty() {
+  languages_dirty_ = true;
+  languages_.clear();
+}
+
+void NavigatorLanguage::SetLanguagesForTesting(const String& languages) {
+  languages_ = ParseAndSanitize(languages);
+}
+
+void NavigatorLanguage::EnsureUpdatedLanguage() {
   if (languages_dirty_) {
     String accept_languages_override;
     probe::applyAcceptLanguageOverride(context_, &accept_languages_override);
@@ -49,16 +82,6 @@
 
     languages_dirty_ = false;
   }
-  return languages_;
-}
-
-bool NavigatorLanguage::IsLanguagesDirty() const {
-  return languages_dirty_;
-}
-
-void NavigatorLanguage::SetLanguagesDirty() {
-  languages_dirty_ = true;
-  languages_.clear();
 }
 
 void NavigatorLanguage::Trace(blink::Visitor* visitor) {
diff --git a/third_party/blink/renderer/core/frame/navigator_language.h b/third_party/blink/renderer/core/frame/navigator_language.h
index b6507f9..b18dec3 100644
--- a/third_party/blink/renderer/core/frame/navigator_language.h
+++ b/third_party/blink/renderer/core/frame/navigator_language.h
@@ -19,6 +19,10 @@
   const Vector<String>& languages();
   bool IsLanguagesDirty() const;
   void SetLanguagesDirty();
+  AtomicString SerializeLanguagesForClientHintHeader();
+
+  // Accepts a comma-separated list of languages.
+  void SetLanguagesForTesting(const String& languages);
 
   void Trace(blink::Visitor*) override;
 
@@ -28,6 +32,8 @@
   virtual String GetAcceptLanguages() = 0;
 
  private:
+  void EnsureUpdatedLanguage();
+
   Vector<String> languages_;
 };
 
diff --git a/third_party/blink/renderer/core/frame/navigator_scheduling.cc b/third_party/blink/renderer/core/frame/navigator_scheduling.cc
new file mode 100644
index 0000000..3ca2c27
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/navigator_scheduling.cc
@@ -0,0 +1,42 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/core/frame/navigator_scheduling.h"
+
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/scheduling.h"
+
+namespace blink {
+
+const char NavigatorScheduling::kSupplementName[] = "NavigatorScheduling";
+
+NavigatorScheduling& NavigatorScheduling::From(Navigator& navigator) {
+  NavigatorScheduling* supplement =
+      Supplement<Navigator>::From<NavigatorScheduling>(navigator);
+  if (!supplement) {
+    supplement = MakeGarbageCollected<NavigatorScheduling>(navigator);
+    ProvideTo(navigator, supplement);
+  }
+  return *supplement;
+}
+
+Scheduling* NavigatorScheduling::scheduling(Navigator& navigator) {
+  return From(navigator).scheduling();
+}
+
+Scheduling* NavigatorScheduling::scheduling() {
+  return scheduling_;
+}
+
+void NavigatorScheduling::Trace(blink::Visitor* visitor) {
+  visitor->Trace(scheduling_);
+  Supplement<Navigator>::Trace(visitor);
+}
+
+NavigatorScheduling::NavigatorScheduling(Navigator& navigator)
+    : Supplement<Navigator>(navigator) {
+  scheduling_ = MakeGarbageCollected<Scheduling>();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/navigator_scheduling.h b/third_party/blink/renderer/core/frame/navigator_scheduling.h
new file mode 100644
index 0000000..90e39bc8
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/navigator_scheduling.h
@@ -0,0 +1,39 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_NAVIGATOR_SCHEDULING_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_NAVIGATOR_SCHEDULING_H_
+
+#include "third_party/blink/renderer/core/frame/navigator.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/supplementable.h"
+
+namespace blink {
+
+class Scheduling;
+
+class CORE_EXPORT NavigatorScheduling final
+    : public GarbageCollected<NavigatorScheduling>,
+      public Supplement<Navigator> {
+  USING_GARBAGE_COLLECTED_MIXIN(NavigatorScheduling);
+
+ public:
+  static const char kSupplementName[];
+
+  static Scheduling* scheduling(Navigator& navigator);
+  Scheduling* scheduling();
+
+  explicit NavigatorScheduling(Navigator&);
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  static NavigatorScheduling& From(Navigator&);
+
+  Member<Scheduling> scheduling_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_NAVIGATOR_SCHEDULING_H_
diff --git a/third_party/blink/renderer/core/frame/navigator_scheduling.idl b/third_party/blink/renderer/core/frame/navigator_scheduling.idl
new file mode 100644
index 0000000..5fa17284
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/navigator_scheduling.idl
@@ -0,0 +1,12 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://github.com/tdresser/is-input-pending
+[
+    Exposed=Window,
+    ImplementedAs=NavigatorScheduling,
+    OriginTrialEnabled=ExperimentalIsInputPending
+] partial interface Navigator {
+  readonly attribute Scheduling scheduling;
+};
diff --git a/third_party/blink/renderer/core/frame/scheduling.cc b/third_party/blink/renderer/core/frame/scheduling.cc
new file mode 100644
index 0000000..a05ab63
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/scheduling.cc
@@ -0,0 +1,62 @@
+// 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/renderer/core/frame/scheduling.h"
+#include "third_party/blink/public/platform/platform.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/core/origin_trials/origin_trials.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h"
+#include "third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
+
+namespace blink {
+
+bool Scheduling::isInputPending(ScriptState* script_state,
+                                const Vector<String>& input_types) const {
+  DCHECK(origin_trials::ExperimentalIsInputPendingEnabled(
+      ExecutionContext::From(script_state)));
+
+  if (!Platform::Current()->IsLockedToSite()) {
+    // As we're interested in checking pending events for as many frames as we
+    // can on the main thread, restrict the API to the case where all frames in
+    // a process are part of the same site to avoid leaking cross-site inputs.
+    ExecutionContext::From(script_state)
+        ->AddConsoleMessage(ConsoleMessage::Create(
+            kJSMessageSource, kWarningMessageLevel,
+            "isInputPending requires site-per-process (crbug.com/910421)."));
+    return false;
+  }
+
+  auto* scheduler = ThreadScheduler::Current();
+  auto input_info = scheduler->GetPendingUserInputInfo();
+  if (input_types.size() == 0) {
+    // If unspecified, return true if any input type is pending.
+    return input_info.HasPendingInputType(
+        scheduler::PendingUserInputType::kAny);
+  }
+
+  bool has_pending_input = false;
+  for (const String& input_type_string : input_types) {
+    const auto pending_input_type = scheduler::PendingUserInput::TypeFromString(
+        AtomicString(input_type_string));
+    if (pending_input_type == scheduler::PendingUserInputType::kNone) {
+      StringBuilder message;
+      message.Append("Unknown input event type \"");
+      message.Append(input_type_string);
+      message.Append("\". Skipping.");
+      ExecutionContext::From(script_state)
+          ->AddConsoleMessage(ConsoleMessage::Create(
+              kJSMessageSource, kWarningMessageLevel, message.ToString()));
+    }
+
+    if (!has_pending_input)
+      has_pending_input |= input_info.HasPendingInputType(pending_input_type);
+  }
+  return has_pending_input;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/scheduling.h b/third_party/blink/renderer/core/frame/scheduling.h
new file mode 100644
index 0000000..ebd1564b
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/scheduling.h
@@ -0,0 +1,25 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_SCHEDULING_H_
+#define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_SCHEDULING_H_
+
+#include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// Low-level scheduling primitives for JS scheduler implementations.
+class Scheduling : public ScriptWrappable {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  bool isInputPending(ScriptState*, const Vector<String>& input_types) const;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_SCHEDULING_H_
diff --git a/third_party/blink/renderer/core/frame/scheduling.idl b/third_party/blink/renderer/core/frame/scheduling.idl
new file mode 100644
index 0000000..c701deba
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/scheduling.idl
@@ -0,0 +1,9 @@
+// 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.
+
+// https://github.com/tdresser/is-input-pending
+[OriginTrialEnabled=ExperimentalIsInputPending]
+interface Scheduling {
+    [CallWith=ScriptState, OriginTrialEnabled=ExperimentalIsInputPending] boolean isInputPending(optional sequence<DOMString> inputTypes = []);
+};
diff --git a/third_party/blink/renderer/core/frame/visual_viewport_test.cc b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
index 078b245..775a8f8 100644
--- a/third_party/blink/renderer/core/frame/visual_viewport_test.cc
+++ b/third_party/blink/renderer/core/frame/visual_viewport_test.cc
@@ -209,7 +209,7 @@
   frame_test_helpers::WebViewHelper helper_;
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(VisualViewportTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(VisualViewportTest);
 
 // Test that resizing the VisualViewport works as expected and that resizing the
 // WebView resizes the VisualViewport.
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
index 14e6768..f96299a4 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc
@@ -159,7 +159,6 @@
       root_layer_(nullptr),
       root_graphics_layer_(nullptr),
       is_accelerated_compositing_active_(false),
-      layer_tree_view_closed_(false),
       suppress_next_keypress_event_(false),
       ime_accept_events_(true),
       self_keep_alive_(this) {}
@@ -174,13 +173,18 @@
 // WebWidget ------------------------------------------------------------------
 
 void WebFrameWidgetImpl::Close() {
+  if (layer_tree_view_) {
+    GetPage()->WillCloseLayerTreeView(*layer_tree_view_,
+                                      LocalRootImpl()->GetFrame()->View());
+  }
+
   WebFrameWidgetBase::Close();
 
   mutator_dispatcher_ = nullptr;
   layer_tree_view_ = nullptr;
+  animation_host_ = nullptr;
   root_layer_ = nullptr;
   root_graphics_layer_ = nullptr;
-  animation_host_ = nullptr;
 
   self_keep_alive_.Clear();
 }
@@ -634,19 +638,6 @@
   return is_accelerated_compositing_active_;
 }
 
-void WebFrameWidgetImpl::WillCloseLayerTreeView() {
-  if (layer_tree_view_) {
-    GetPage()->WillCloseLayerTreeView(*layer_tree_view_,
-                                      LocalRootImpl()->GetFrame()->View());
-  }
-
-  SetIsAcceleratedCompositingActive(false);
-  mutator_dispatcher_ = nullptr;
-  layer_tree_view_ = nullptr;
-  animation_host_ = nullptr;
-  layer_tree_view_closed_ = true;
-}
-
 void WebFrameWidgetImpl::SetRemoteViewportIntersection(
     const WebRect& viewport_intersection,
     bool occluded_or_obscured) {
@@ -1029,24 +1020,17 @@
 }
 
 void WebFrameWidgetImpl::SetIsAcceleratedCompositingActive(bool active) {
-  // In the middle of shutting down; don't try to spin back up a compositor.
-  // FIXME: compositing startup/shutdown should be refactored so that it
-  // turns on explicitly rather than lazily, which causes this awkwardness.
-  if (layer_tree_view_closed_)
+  if (!active)
     return;
-
-  DCHECK(!active || layer_tree_view_);
-
-  if (is_accelerated_compositing_active_ == active)
+  if (is_accelerated_compositing_active_)
     return;
+  DCHECK(layer_tree_view_);
 
-  if (active) {
-    TRACE_EVENT0("blink",
-                 "WebViewImpl::setIsAcceleratedCompositingActive(true)");
-    layer_tree_view_->SetRootLayer(root_layer_);
-    UpdateLayerTreeViewport();
-    is_accelerated_compositing_active_ = true;
-  }
+  TRACE_EVENT0("blink",
+               "WebFrameWidgetImpl::SetIsAcceleratedCompositingActive(true)");
+  layer_tree_view_->SetRootLayer(root_layer_);
+  UpdateLayerTreeViewport();
+  is_accelerated_compositing_active_ = true;
 }
 
 PaintLayerCompositor* WebFrameWidgetImpl::Compositor() const {
diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
index d8967c7..074bbaf 100644
--- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
+++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.h
@@ -107,7 +107,6 @@
   void SetFocus(bool enable) override;
   bool SelectionBounds(WebRect& anchor, WebRect& focus) const override;
   bool IsAcceleratedCompositingActive() const override;
-  void WillCloseLayerTreeView() override;
   void SetRemoteViewportIntersection(const WebRect&, bool) override;
   void SetIsInert(bool) override;
   void SetInheritedEffectiveTouchAction(TouchAction) override;
@@ -211,7 +210,6 @@
   std::unique_ptr<CompositorAnimationHost> animation_host_;
   base::TimeTicks raf_aligned_input_start_time_;
   bool is_accelerated_compositing_active_;
-  bool layer_tree_view_closed_;
 
   bool suppress_next_keypress_event_;
 
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 327d0f1c..517e357 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
@@ -914,7 +914,11 @@
 }
 
 void WebLocalFrameImpl::ReloadImage(const WebNode& web_node) {
-  const Node* node = web_node.ConstUnwrap<Node>();
+  Node* node = web_node;  // Use implicit WebNode->Node* cast.
+  HitTestResult hit_test_result;
+  hit_test_result.SetInnerNode(node);
+  hit_test_result.SetToShadowHostIfInRestrictedShadowRoot();
+  node = hit_test_result.InnerNodeOrImageMapImage();
   if (auto* image_element = ToHTMLImageElementOrNull(*node))
     image_element->ForceReload();
 }
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
index 4e358507..730f819 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.cc
@@ -149,10 +149,6 @@
   return web_view_->IsAcceleratedCompositingActive();
 }
 
-void WebViewFrameWidget::WillCloseLayerTreeView() {
-  web_view_->WillCloseLayerTreeView();
-}
-
 WebURL WebViewFrameWidget::GetURLForDebugTrace() {
   return web_view_->GetURLForDebugTrace();
 }
diff --git a/third_party/blink/renderer/core/frame/web_view_frame_widget.h b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
index 6c8832ed..71ecab44 100644
--- a/third_party/blink/renderer/core/frame/web_view_frame_widget.h
+++ b/third_party/blink/renderer/core/frame/web_view_frame_widget.h
@@ -75,7 +75,6 @@
   void SetFocus(bool) override;
   bool SelectionBounds(WebRect& anchor, WebRect& focus) const override;
   bool IsAcceleratedCompositingActive() const override;
-  void WillCloseLayerTreeView() override;
   WebURL GetURLForDebugTrace() override;
 
   // WebFrameWidget overrides:
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_font_cache.cc b/third_party/blink/renderer/core/html/canvas/canvas_font_cache.cc
index f9de29a..0a32e5f3 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_font_cache.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_font_cache.cc
@@ -10,7 +10,7 @@
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/style/computed_style.h"
 #include "third_party/blink/renderer/platform/fonts/font_cache.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 
 namespace {
 
@@ -43,13 +43,14 @@
 }
 
 unsigned CanvasFontCache::MaxFonts() {
-  return MemoryCoordinator::IsLowEndDevice() ? CanvasFontCacheMaxFontsLowEnd
-                                             : CanvasFontCacheMaxFonts;
+  return MemoryPressureListenerRegistry::IsLowEndDevice()
+             ? CanvasFontCacheMaxFontsLowEnd
+             : CanvasFontCacheMaxFonts;
 }
 
 unsigned CanvasFontCache::HardMaxFonts() {
   return document_->hidden() ? CanvasFontCacheHiddenMaxFonts
-                             : (MemoryCoordinator::IsLowEndDevice()
+                             : (MemoryPressureListenerRegistry::IsLowEndDevice()
                                     ? CanvasFontCacheHardMaxFontsLowEnd
                                     : CanvasFontCacheHardMaxFonts);
 }
diff --git a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
index bcd91590..3e42e9ee 100644
--- a/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
+++ b/third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
@@ -1049,7 +1049,7 @@
   if (!surface->IsValid())
     return nullptr;
 
-  if (MemoryCoordinator::IsLowEndDevice())
+  if (MemoryPressureListenerRegistry::IsLowEndDevice())
     surface->DisableDeferral(kDisableDeferralReasonLowEndDevice);
 
   return surface;
diff --git a/third_party/blink/renderer/core/html/lazy_load_frame_observer_test.cc b/third_party/blink/renderer/core/html/lazy_load_frame_observer_test.cc
index 9bfeb4e..411eb0d 100644
--- a/third_party/blink/renderer/core/html/lazy_load_frame_observer_test.cc
+++ b/third_party/blink/renderer/core/html/lazy_load_frame_observer_test.cc
@@ -1085,7 +1085,7 @@
   EXPECT_TRUE(ConsoleMessages().Contains("child frame element onload"));
 }
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     LazyFrameLoading,
     LazyLoadFramesParamsTest,
     ::testing::Combine(
diff --git a/third_party/blink/renderer/core/html/parser/html_document_parser_loading_test.cc b/third_party/blink/renderer/core/html/parser/html_document_parser_loading_test.cc
index 586cfc4..290b8ed 100644
--- a/third_party/blink/renderer/core/html/parser/html_document_parser_loading_test.cc
+++ b/third_party/blink/renderer/core/html/parser/html_document_parser_loading_test.cc
@@ -29,12 +29,12 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(Threaded,
-                        HTMLDocumentParserLoadingTest,
-                        testing::Values(true));
-INSTANTIATE_TEST_CASE_P(NotThreaded,
-                        HTMLDocumentParserLoadingTest,
-                        testing::Values(false));
+INSTANTIATE_TEST_SUITE_P(Threaded,
+                         HTMLDocumentParserLoadingTest,
+                         testing::Values(true));
+INSTANTIATE_TEST_SUITE_P(NotThreaded,
+                         HTMLDocumentParserLoadingTest,
+                         testing::Values(false));
 
 TEST_P(HTMLDocumentParserLoadingTest,
        ShouldNotPauseParsingForExternalStylesheetsInHead) {
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.cc b/third_party/blink/renderer/core/input/pointer_event_manager.cc
index 0d527c2..6a02e6a 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.cc
@@ -86,9 +86,6 @@
 
 void PointerEventManager::Trace(blink::Visitor* visitor) {
   visitor->Trace(frame_);
-  visitor->Trace(element_under_pointer_);
-  visitor->Trace(pointer_capture_target_);
-  visitor->Trace(pending_pointer_capture_target_);
   visitor->Trace(touch_event_manager_);
   visitor->Trace(mouse_event_manager_);
 }
@@ -230,20 +227,20 @@
 
 void PointerEventManager::SetElementUnderPointer(PointerEvent* pointer_event,
                                                  Element* target) {
-  if (element_under_pointer_.Contains(pointer_event->pointerId())) {
-    EventTargetAttributes node =
-        element_under_pointer_.at(pointer_event->pointerId());
+  const auto& element_iterator =
+      element_under_pointer_.find(pointer_event->pointerId());
+  if (element_iterator != element_under_pointer_.end()) {
+    Element* old_target = element_iterator->second.target;
     if (!target) {
-      element_under_pointer_.erase(pointer_event->pointerId());
-    } else if (target !=
-               element_under_pointer_.at(pointer_event->pointerId()).target) {
-      element_under_pointer_.Set(pointer_event->pointerId(),
-                                 EventTargetAttributes(target));
+      element_under_pointer_.erase(element_iterator);
+    } else if (target != old_target) {
+      element_under_pointer_[pointer_event->pointerId()] =
+          EventTargetAttributes(target);
     }
-    SendBoundaryEvents(node.target, target, pointer_event);
+    SendBoundaryEvents(old_target, target, pointer_event);
   } else if (target) {
-    element_under_pointer_.insert(pointer_event->pointerId(),
-                                  EventTargetAttributes(target));
+    element_under_pointer_[pointer_event->pointerId()] =
+        EventTargetAttributes(target);
     SendBoundaryEvents(nullptr, target, pointer_event);
   }
 }
@@ -282,9 +279,10 @@
   for (auto pointer_event : canceled_pointer_events) {
     // If we are sending a pointercancel we have sent the pointerevent to some
     // target before.
-    DCHECK(element_under_pointer_.Contains(pointer_event->pointerId()));
-    Element* target =
-        element_under_pointer_.at(pointer_event->pointerId()).target;
+    const auto& element_iterator =
+        element_under_pointer_.find(pointer_event->pointerId());
+    DCHECK(element_iterator != element_under_pointer_.end());
+    Element* target = element_iterator->second.target;
 
     DispatchPointerEvent(
         GetEffectiveTargetForPointerEvent(target, pointer_event->pointerId()),
@@ -366,8 +364,10 @@
   // that will be capturing this event. |m_pointerCaptureTarget| may not
   // have this target yet since the processing of that will be done right
   // before firing the event.
+  const auto& capture_iterator =
+      pending_pointer_capture_target_.find(pointer_id);
   if (web_pointer_event.GetType() == WebInputEvent::kPointerDown ||
-      !pending_pointer_capture_target_.Contains(pointer_id)) {
+      capture_iterator == pending_pointer_capture_target_.end()) {
     HitTestRequest::HitTestRequestType hit_type = HitTestRequest::kTouchEvent |
                                                   HitTestRequest::kReadOnly |
                                                   HitTestRequest::kActive;
@@ -392,8 +392,7 @@
     // Set the target of pointer event to the captured element as this
     // pointer is captured otherwise it would have gone to the if block
     // and perform a hit-test.
-    pointer_event_target.target_element =
-        pending_pointer_capture_target_.at(pointer_id);
+    pointer_event_target.target_element = capture_iterator->second;
     pointer_event_target.target_frame =
         pointer_event_target.target_element->GetDocument().GetFrame();
   }
@@ -772,14 +771,12 @@
     PointerId pointer_id,
     Element** pointer_capture_target,
     Element** pending_pointer_capture_target) {
-  PointerCapturingMap::const_iterator it;
-
-  it = pointer_capture_target_.find(pointer_id);
+  auto it = pointer_capture_target_.find(pointer_id);
   Element* pointer_capture_target_temp =
-      (it != pointer_capture_target_.end()) ? it->value : nullptr;
+      (it != pointer_capture_target_.end()) ? it->second : nullptr;
   it = pending_pointer_capture_target_.find(pointer_id);
   Element* pending_pointercapture_target_temp =
-      (it != pending_pointer_capture_target_.end()) ? it->value : nullptr;
+      (it != pending_pointer_capture_target_.end()) ? it->second : nullptr;
 
   if (pointer_capture_target)
     *pointer_capture_target = pointer_capture_target_temp;
@@ -796,10 +793,9 @@
     const WebMouseEvent* mouse_event) {
   ProcessPendingPointerCapture(pointer_event);
 
-  PointerCapturingMap::const_iterator it =
-      pointer_capture_target_.find(pointer_event->pointerId());
+  const auto& it = pointer_capture_target_.find(pointer_event->pointerId());
   if (Element* pointercapture_target =
-          (it != pointer_capture_target_.end()) ? it->value : nullptr)
+          (it != pointer_capture_target_.end()) ? it->second : nullptr)
     hit_test_target = pointercapture_target;
 
   SetElementUnderPointer(pointer_event, hit_test_target);
@@ -841,7 +837,7 @@
         pending_pointer_capture_target,
         pointer_event_factory_.CreatePointerCaptureEvent(
             pointer_event, event_type_names::kGotpointercapture));
-    pointer_capture_target_.Set(pointer_id, pending_pointer_capture_target);
+    pointer_capture_target_[pointer_id] = pending_pointer_capture_target;
   } else {
     pointer_capture_target_.erase(pointer_id);
   }
@@ -864,15 +860,16 @@
   // the performance improvement considering there might not be a lot of
   // active pointer or pointer captures at the same time.
   PointerCapturingMap tmp = map;
-  for (PointerCapturingMap::iterator it = tmp.begin(); it != tmp.end(); ++it) {
-    if (it->value == target)
-      map.erase(it->key);
+  for (auto it = tmp.begin(); it != tmp.end(); ++it) {
+    if (it->second == target)
+      map.erase(it->first);
   }
 }
 
 Element* PointerEventManager::GetCapturingElement(PointerId pointer_id) {
-  if (pointer_capture_target_.Contains(pointer_id))
-    return pointer_capture_target_.at(pointer_id);
+  const auto& capture_iterator = pointer_capture_target_.find(pointer_id);
+  if (capture_iterator != pointer_capture_target_.end())
+    return capture_iterator->second;
   return nullptr;
 }
 
@@ -898,7 +895,7 @@
       UseCounter::Count(frame_,
                         WebFeature::kPointerEventSetCaptureOutsideDispatch);
     }
-    pending_pointer_capture_target_.Set(pointer_id, target);
+    pending_pointer_capture_target_[pointer_id] = target;
   }
 }
 
@@ -911,7 +908,7 @@
   // but |m_pendingPointerCaptureTarget| indicated the element that gets the
   // very next pointer event. They will be the same if there was no change in
   // capturing of a particular |pointerId|. See crbug.com/614481.
-  if (pending_pointer_capture_target_.at(pointer_id) == target)
+  if (HasPointerCapture(pointer_id, target))
     ReleasePointerCapture(pointer_id);
 }
 
@@ -921,7 +918,10 @@
 
 bool PointerEventManager::HasPointerCapture(PointerId pointer_id,
                                             const Element* target) const {
-  return pending_pointer_capture_target_.at(pointer_id) == target;
+  const auto& capture_iterator =
+      pending_pointer_capture_target_.find(pointer_id);
+  return capture_iterator != pending_pointer_capture_target_.end() &&
+         capture_iterator->second == target;
 }
 
 void PointerEventManager::ReleasePointerCapture(PointerId pointer_id) {
@@ -942,9 +942,10 @@
   if (pointer_event_factory_.GetPointerType(pointer_id) !=
       WebPointerProperties::PointerType::kTouch)
     return false;
+  const auto& element_iterator = element_under_pointer_.find(pointer_id);
   Element* last_element_receiving_event =
-      element_under_pointer_.Contains(pointer_id)
-          ? element_under_pointer_.at(pointer_id).target
+      element_iterator != element_under_pointer_.end()
+          ? element_iterator->second.target
           : nullptr;
   return last_element_receiving_event &&
          last_element_receiving_event->GetDocument().GetFrame() == frame;
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager.h b/third_party/blink/renderer/core/input/pointer_event_manager.h
index d6d1bf0..4fecffcd 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager.h
+++ b/third_party/blink/renderer/core/input/pointer_event_manager.h
@@ -5,6 +5,8 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_POINTER_EVENT_MANAGER_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INPUT_POINTER_EVENT_MANAGER_H_
 
+#include <unordered_map>
+
 #include "base/macros.h"
 #include "third_party/blink/public/platform/web_input_event_result.h"
 #include "third_party/blink/public/platform/web_pointer_properties.h"
@@ -108,11 +110,10 @@
   WebInputEventResult FlushEvents();
 
  private:
-  typedef HeapHashMap<PointerId,
-                      Member<Element>,
-                      WTF::IntHash<PointerId>,
-                      WTF::UnsignedWithZeroKeyHashTraits<PointerId>>
-      PointerCapturingMap;
+  // We are using unordered_map instead of WTF::HashMap to accommodate
+  // for all the possible keys in the given rage as WTF::HashTraits have at
+  // least one unsupported key like 0 or max value in their range.
+  typedef std::unordered_map<PointerId, Member<Element>> PointerCapturingMap;
   class EventTargetAttributes {
     DISALLOW_NEW();
 
@@ -244,10 +245,7 @@
   // keeps track of any compatibility mouse event positions but this map for
   // the pointer with id=1 is only taking care of true mouse related events.
   using ElementUnderPointerMap =
-      HeapHashMap<PointerId,
-                  EventTargetAttributes,
-                  WTF::IntHash<PointerId>,
-                  WTF::UnsignedWithZeroKeyHashTraits<PointerId>>;
+      std::unordered_map<PointerId, EventTargetAttributes>;
   ElementUnderPointerMap element_under_pointer_;
 
   PointerCapturingMap pointer_capture_target_;
diff --git a/third_party/blink/renderer/core/input/pointer_event_manager_test.cc b/third_party/blink/renderer/core/input/pointer_event_manager_test.cc
index 925a18f..9dc7f4d 100644
--- a/third_party/blink/renderer/core/input/pointer_event_manager_test.cc
+++ b/third_party/blink/renderer/core/input/pointer_event_manager_test.cc
@@ -4,6 +4,8 @@
 
 #include "third_party/blink/renderer/core/input/pointer_event_manager.h"
 
+#include <limits>
+
 #include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/native_event_listener.h"
 #include "third_party/blink/renderer/core/html/html_element.h"
@@ -100,13 +102,55 @@
   WebMouseEvent CreateTestMouseEvent(WebInputEvent::Type type,
                                      const WebFloatPoint& coordinates) {
     WebMouseEvent event(type, coordinates, coordinates,
-                        WebPointerProperties::Button::kLeft, 0, 0,
+                        WebPointerProperties::Button::kLeft, 0,
+                        WebInputEvent::kLeftButtonDown,
                         WebInputEvent::GetStaticTimeStampForTests());
     event.SetFrameScale(1);
     return event;
   }
 };
 
+TEST_F(PointerEventManagerTest, HasPointerCapture) {
+  WebView().MainFrameWidget()->Resize(WebSize(400, 400));
+  SimRequest request("https://example.com/test.html", "text/html");
+  LoadURL("https://example.com/test.html");
+  request.Complete(
+      "<body style='padding: 0px; width: 400px; height: 400px;'>"
+      "</body>");
+  ASSERT_FALSE(GetDocument().body()->hasPointerCapture(4));
+  ASSERT_FALSE(GetDocument().body()->hasPointerCapture(
+      std::numeric_limits<PointerId>::max()));
+  ASSERT_FALSE(GetDocument().body()->hasPointerCapture(0));
+  ASSERT_FALSE(GetDocument().body()->hasPointerCapture(-1));
+  ASSERT_FALSE(
+      GetDocument().body()->hasPointerCapture(PointerEventFactory::kMouseId));
+
+  ExceptionState exception(nullptr, ExceptionState::kExecutionContext, "", "");
+
+  GetEventHandler().HandleMousePressEvent(
+      CreateTestMouseEvent(WebInputEvent::kMouseDown, WebFloatPoint(100, 100)));
+
+  ASSERT_FALSE(
+      GetDocument().body()->hasPointerCapture(PointerEventFactory::kMouseId));
+
+  GetDocument().body()->setPointerCapture(PointerEventFactory::kMouseId,
+                                          exception);
+  ASSERT_TRUE(
+      GetDocument().body()->hasPointerCapture(PointerEventFactory::kMouseId));
+
+  GetEventHandler().HandleMouseMoveEvent(
+      CreateTestMouseEvent(WebInputEvent::kMouseMove, WebFloatPoint(200, 200)),
+      Vector<WebMouseEvent>(), Vector<WebMouseEvent>());
+
+  ASSERT_TRUE(
+      GetDocument().body()->hasPointerCapture(PointerEventFactory::kMouseId));
+
+  GetDocument().body()->releasePointerCapture(PointerEventFactory::kMouseId,
+                                              exception);
+  ASSERT_FALSE(
+      GetDocument().body()->hasPointerCapture(PointerEventFactory::kMouseId));
+}
+
 TEST_F(PointerEventManagerTest, PointerCancelsOfAllTypes) {
   WebView().MainFrameWidget()->Resize(WebSize(400, 400));
   SimRequest request("https://example.com/test.html", "text/html");
diff --git a/third_party/blink/renderer/core/inspector/browser_protocol.pdl b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
index 58696dba..77d37b6 100644
--- a/third_party/blink/renderer/core/inspector/browser_protocol.pdl
+++ b/third_party/blink/renderer/core/inspector/browser_protocol.pdl
@@ -2705,23 +2705,9 @@
       # Frame height (DIP).
       integer height
 
-  # Notification sent after the virtual time has advanced.
-  experimental event virtualTimeAdvanced
-    parameters
-      # The amount of virtual time that has elapsed in milliseconds since virtual time was first
-      # enabled.
-      number virtualTimeElapsed
-
   # Notification sent after the virtual time budget for the current VirtualTimePolicy has run out.
   experimental event virtualTimeBudgetExpired
 
-  # Notification sent after the virtual time has paused.
-  experimental event virtualTimePaused
-    parameters
-      # The amount of virtual time that has elapsed in milliseconds since virtual time was first
-      # enabled.
-      number virtualTimeElapsed
-
   # Allows overriding user agent with the given string.
   command setUserAgentOverride
     parameters
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
index 8dae62c8..95ce457 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.cc
@@ -141,11 +141,6 @@
   setCPUThrottlingRate(1);
   setFocusEmulationEnabled(false);
   setDefaultBackgroundColorOverride(Maybe<protocol::DOM::RGBA>());
-  if (virtual_time_setup_) {
-    DCHECK(web_local_frame_);
-    web_local_frame_->View()->Scheduler()->RemoveVirtualTimeObserver(this);
-    virtual_time_setup_ = false;
-  }
   return Response::OK();
 }
 
@@ -299,10 +294,6 @@
   }
 
   InnerEnable();
-  if (!virtual_time_setup_) {
-    web_local_frame_->View()->Scheduler()->AddVirtualTimeObserver(this);
-    virtual_time_setup_ = true;
-  }
 
   // This needs to happen before we apply virtual time.
   if (initial_virtual_time.isJust()) {
@@ -390,28 +381,15 @@
 void InspectorEmulationAgent::VirtualTimeBudgetExpired() {
   TRACE_EVENT_ASYNC_END0("renderer.scheduler", "VirtualTimeBudget", this);
   WebView* view = web_local_frame_->View();
-  if (!view) {
-    DCHECK_EQ(false, virtual_time_setup_);
+  if (!view)
     return;
-  }
+
   view->Scheduler()->SetVirtualTimePolicy(
       PageScheduler::VirtualTimePolicy::kPause);
   virtual_time_policy_.Set(protocol::Emulation::VirtualTimePolicyEnum::Pause);
   GetFrontend()->virtualTimeBudgetExpired();
 }
 
-void InspectorEmulationAgent::OnVirtualTimeAdvanced(
-    WTF::TimeDelta virtual_time_offset) {
-  virtual_time_offset_.Set(virtual_time_offset.InMillisecondsF());
-  GetFrontend()->virtualTimeAdvanced(virtual_time_offset.InMillisecondsF());
-}
-
-void InspectorEmulationAgent::OnVirtualTimePaused(
-    WTF::TimeDelta virtual_time_offset) {
-  virtual_time_offset_.Set(virtual_time_offset.InMillisecondsF());
-  GetFrontend()->virtualTimePaused(virtual_time_offset.InMillisecondsF());
-}
-
 Response InspectorEmulationAgent::setDefaultBackgroundColorOverride(
     Maybe<protocol::DOM::RGBA> color) {
   Response response = AssertPage();
diff --git a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
index d8119b5a..baa03bd3 100644
--- a/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
+++ b/third_party/blink/renderer/core/inspector/inspector_emulation_agent.h
@@ -32,8 +32,7 @@
 }  // namespace protocol
 
 class CORE_EXPORT InspectorEmulationAgent final
-    : public InspectorBaseAgent<protocol::Emulation::Metainfo>,
-      public PageScheduler::VirtualTimeObserver {
+    : public InspectorBaseAgent<protocol::Emulation::Metainfo> {
  public:
   explicit InspectorEmulationAgent(WebLocalFrameImpl*);
   ~InspectorEmulationAgent() override;
@@ -95,10 +94,6 @@
   protocol::Response disable() override;
   void Restore() override;
 
-  // scheduler::PageScheduler::VirtualTimeObserver implementation.
-  void OnVirtualTimeAdvanced(WTF::TimeDelta virtual_time_offset) override;
-  void OnVirtualTimePaused(WTF::TimeDelta virtual_time_offset) override;
-
   void Trace(blink::Visitor*) override;
 
  private:
@@ -115,7 +110,6 @@
   void ApplyVirtualTimePolicy(const PendingVirtualTimePolicy& new_policy);
 
   Member<WebLocalFrameImpl> web_local_frame_;
-  bool virtual_time_setup_ = false;
   WTF::TimeTicks virtual_time_base_ticks_;
 
   // Supports a virtual time policy change scheduled to occur after any
diff --git a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
index 3712f51..c99ff2c 100644
--- a/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_overlay_agent.cc
@@ -167,9 +167,6 @@
 
     if (RuntimeEnabledFeatures::CompositeAfterPaintEnabled()) {
       layer_->SetBounds(gfx::Size(frame_overlay.Size()));
-      overlay_->OverlayMainFrame()
-          ->View()
-          ->SetPaintArtifactCompositorNeedsUpdate();
       RecordForeignLayer(graphics_context,
                          DisplayItem::kForeignLayerDevToolsOverlay, layer_,
                          PropertyTreeState::Root());
@@ -583,6 +580,8 @@
   }
 
   frame_overlay_->Update();
+  if (auto* frame_view = frame_impl_->GetFrameView())
+    frame_view->SetPaintArtifactCompositorNeedsUpdate();
 }
 
 void InspectorOverlayAgent::UpdateAllOverlayLifecyclePhases() {
@@ -812,6 +811,8 @@
       frame_overlay_.reset();
       client.SetCursorOverridden(false);
       client.SetCursor(PointerCursor(), frame_impl_->GetFrame());
+      if (auto* frame_view = frame_impl_->GetFrameView())
+        frame_view->SetPaintArtifactCompositorNeedsUpdate();
     }
     return;
   }
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
index 11cc66c..82a88ad 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.cc
@@ -726,20 +726,6 @@
   return value;
 }
 
-std::unique_ptr<TracedValue> inspector_scroll_invalidation_tracking_event::Data(
-    const LayoutObject& layout_object) {
-  static const char kScrollInvalidationReason[] =
-      "Scroll with viewport-constrained element";
-
-  std::unique_ptr<TracedValue> value = TracedValue::Create();
-  value->SetString("frame",
-                   IdentifiersFactory::FrameId(layout_object.GetFrame()));
-  value->SetString("reason", kScrollInvalidationReason);
-  SetGeneratingNodeInfo(value.get(), &layout_object, "nodeId", "nodeName");
-  SourceLocation::Capture()->ToTracedValue(value.get(), "stackTrace");
-  return value;
-}
-
 std::unique_ptr<TracedValue> inspector_change_resource_priority_event::Data(
     DocumentLoader* loader,
     unsigned long identifier,
diff --git a/third_party/blink/renderer/core/inspector/inspector_trace_events.h b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
index 691c2a5..726ebad 100644
--- a/third_party/blink/renderer/core/inspector/inspector_trace_events.h
+++ b/third_party/blink/renderer/core/inspector/inspector_trace_events.h
@@ -265,10 +265,6 @@
 std::unique_ptr<TracedValue> Data(const LayoutObject&);
 }
 
-namespace inspector_scroll_invalidation_tracking_event {
-std::unique_ptr<TracedValue> Data(const LayoutObject&);
-}
-
 namespace inspector_change_resource_priority_event {
 std::unique_ptr<TracedValue> Data(DocumentLoader*,
                                   unsigned long identifier,
diff --git a/third_party/blink/renderer/core/invisible_dom/find_invisible_test.cc b/third_party/blink/renderer/core/invisible_dom/find_invisible_test.cc
index 56771c2..74dd77a6 100644
--- a/third_party/blink/renderer/core/invisible_dom/find_invisible_test.cc
+++ b/third_party/blink/renderer/core/invisible_dom/find_invisible_test.cc
@@ -109,7 +109,7 @@
   frame_test_helpers::WebViewHelper web_view_helper_;
 };
 
-INSTANTIATE_TEST_CASE_P(All, FindInvisibleTest, ::testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, FindInvisibleTest, ::testing::Bool());
 
 TEST_P(FindInvisibleTest, FindingInvisibleTextHasCorrectCount) {
   ResizeAndFocus();
@@ -138,11 +138,10 @@
   test::RunPendingTasks();
   EXPECT_TRUE(client.FindResultsAreReady());
   EXPECT_EQ(4, client.Count());
-  // Active match is not supported yet for invisible elements.
-  EXPECT_EQ(-1, client.ActiveIndex());
+  EXPECT_EQ(1, client.ActiveIndex());
 }
 
-TEST_P(FindInvisibleTest, FindingInvisibleTextHasIncorrectActiveMatch) {
+TEST_P(FindInvisibleTest, FindingInvisibleTextHasCorrectActiveMatch) {
   ResizeAndFocus();
   SetInnerHTML(
       "<div id='div'>1foo<div invisible>22foo</div>333foo</div>"
@@ -167,6 +166,7 @@
   // Finding next focuses on "1foo".
   WebRange range = LocalMainFrame()->SelectionRange();
   ASSERT_FALSE(range.IsNull());
+  EXPECT_EQ(1, client.ActiveIndex());
   EXPECT_EQ(1, range.StartOffset());
   EXPECT_EQ(4, range.EndOffset());
 
@@ -175,9 +175,22 @@
   test::RunPendingTasks();
   find_in_page->StopFinding(
       mojom::blink::StopFindAction::kStopFindActionKeepSelection);
-  // Finding next focuses on "333foo", skipping "22foo".
+  // Finding next focuses on "22foo".
   range = LocalMainFrame()->SelectionRange();
   ASSERT_FALSE(range.IsNull());
+  EXPECT_EQ(2, client.ActiveIndex());
+  EXPECT_EQ(2, range.StartOffset());
+  EXPECT_EQ(5, range.EndOffset());
+
+  find_options->find_next = true;
+  find_in_page->Find(current_id++, "foo", find_options->Clone());
+  test::RunPendingTasks();
+  find_in_page->StopFinding(
+      mojom::blink::StopFindAction::kStopFindActionKeepSelection);
+  // Finding next focuses on "333foo".
+  range = LocalMainFrame()->SelectionRange();
+  ASSERT_FALSE(range.IsNull());
+  EXPECT_EQ(3, client.ActiveIndex());
   EXPECT_EQ(3, range.StartOffset());
   EXPECT_EQ(6, range.EndOffset());
 
@@ -186,9 +199,22 @@
   test::RunPendingTasks();
   find_in_page->StopFinding(
       mojom::blink::StopFindAction::kStopFindActionKeepSelection);
-  // Finding next focuses on "55555foo", skipping "4444foo".
+  // Finding next focuses on "4444foo".
   range = LocalMainFrame()->SelectionRange();
   ASSERT_FALSE(range.IsNull());
+  EXPECT_EQ(4, client.ActiveIndex());
+  EXPECT_EQ(4, range.StartOffset());
+  EXPECT_EQ(7, range.EndOffset());
+
+  find_options->find_next = true;
+  find_in_page->Find(current_id++, "foo", find_options->Clone());
+  test::RunPendingTasks();
+  find_in_page->StopFinding(
+      mojom::blink::StopFindAction::kStopFindActionKeepSelection);
+  // Finding next focuses on "55555foo".
+  range = LocalMainFrame()->SelectionRange();
+  ASSERT_FALSE(range.IsNull());
+  EXPECT_EQ(5, client.ActiveIndex());
   EXPECT_EQ(5, range.StartOffset());
   EXPECT_EQ(8, range.EndOffset());
 }
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 920e4fc..58f6e93 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 (!node.CanParticipateInFlatTree())
+    return false;
   for (Node& ancestor : FlatTreeTraversal::InclusiveAncestorsOf(node)) {
     if (ancestor.IsElementNode() &&
         ToElement(ancestor).HasInvisibleAttribute()) {
@@ -30,4 +32,25 @@
   return root;
 }
 
+bool InvisibleDOM::ActivateRangeIfNeeded(
+    const EphemeralRangeInFlatTree& range) {
+  if (range.IsNull() || range.IsCollapsed())
+    return false;
+  HeapVector<Member<Element>> elements_to_activate;
+  for (Node& node : range.Nodes()) {
+    if (!InvisibleDOM::IsInsideInvisibleSubtree(node))
+      continue;
+    for (Node& node : FlatTreeTraversal::AncestorsOf(node)) {
+      if (node.IsElementNode()) {
+        elements_to_activate.push_back(ToElement(node));
+        break;
+      }
+    }
+  }
+  for (Element* element : elements_to_activate) {
+    element->DispatchActivateInvisibleEventIfNeeded();
+  }
+  return !elements_to_activate.IsEmpty();
+}
+
 }  // namespace blink
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 b75f2b4..adf323c8 100644
--- a/third_party/blink/renderer/core/invisible_dom/invisible_dom.h
+++ b/third_party/blink/renderer/core/invisible_dom/invisible_dom.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_INVISIBLE_DOM_INVISIBLE_DOM_H_
 
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/editing/ephemeral_range.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 
 namespace blink {
@@ -21,6 +22,10 @@
 
   // Highest inclusive ancestor that has the invisible attribute.
   static Element* InvisibleRoot(const Node&);
+
+  // Activates all the nodes within |range|. Returns true if at least one
+  // node gets activated.
+  static bool ActivateRangeIfNeeded(const EphemeralRangeInFlatTree& range);
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
index 914174c..d9919e8 100644
--- a/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/flexible_box_algorithm.cc
@@ -476,7 +476,17 @@
   // css-flexbox section 4.5
   const Length& min = IsHorizontalFlow() ? child.StyleRef().MinWidth()
                                          : child.StyleRef().MinHeight();
-  return min.IsAuto() && !child.ShouldApplySizeContainment() &&
+  if (!min.IsAuto())
+    return false;
+
+  // TODO(crbug.com/927066): We calculate an incorrect intrinsic logical height
+  // when percentages are involved, so for now don't apply min-height: auto
+  // in such cases.
+  if (IsColumnFlow() && child.IsFlexibleBox() &&
+      ToLayoutBlock(child).HasPercentHeightDescendants())
+    return false;
+
+  return !child.ShouldApplySizeContainment() &&
          MainAxisOverflowForChild(child) == EOverflow::kVisible;
 }
 
diff --git a/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc b/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc
index 52095c7..8ca78288 100644
--- a/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc
+++ b/third_party/blink/renderer/core/layout/grid_track_sizing_algorithm.cc
@@ -854,8 +854,12 @@
 
   const GridTrackSize& track_size =
       RawGridTrackSize(direction, translated_index);
-  if (track_size.IsFitContent())
-    return track_size;
+  if (track_size.IsFitContent()) {
+    return IsRelativeGridLengthAsAuto(track_size.FitContentTrackBreadth(),
+                                      direction)
+               ? GridTrackSize(Length(kAuto), Length(kMaxContent))
+               : track_size;
+  }
 
   GridLength min_track_breadth = track_size.MinTrackBreadth();
   GridLength max_track_breadth = track_size.MaxTrackBreadth();
@@ -925,7 +929,6 @@
   DCHECK(auto_sized_tracks_for_stretch_index_.IsEmpty());
   DCHECK(!has_percent_sized_rows_indefinite_height_);
   Vector<GridTrack>& track_list = Tracks(direction_);
-  bool has_definite_free_space = !!AvailableSpace();
   bool indefinite_height =
       direction_ == kForRows && !layout_grid_->CachedHasDefiniteLogicalHeight();
   size_t num_tracks = track_list.size();
@@ -937,11 +940,9 @@
     track.SetInfinitelyGrowable(false);
 
     if (track_size.IsFitContent()) {
-      GridLength grid_length = track_size.FitContentTrackBreadth();
-      if (!grid_length.HasPercentage() || has_definite_free_space) {
-        track.SetGrowthLimitCap(ValueForLength(
-            grid_length.length(), AvailableSpace().value_or(LayoutUnit())));
-      }
+      track.SetGrowthLimitCap(
+          ValueForLength(track_size.FitContentTrackBreadth().length(),
+                         AvailableSpace().value_or(LayoutUnit())));
     }
 
     if (track_size.IsContentSized())
diff --git a/third_party/blink/renderer/core/layout/layout_box_test.cc b/third_party/blink/renderer/core/layout/layout_box_test.cc
index f9cc5897..50dff4c2 100644
--- a/third_party/blink/renderer/core/layout/layout_box_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_box_test.cc
@@ -27,7 +27,7 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(All, LayoutBoxTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, LayoutBoxTest, testing::Bool());
 
 TEST_P(LayoutBoxTest, BackgroundObscuredInRect) {
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc b/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc
index b93f599..2f24387 100644
--- a/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_flexible_box_test.cc
@@ -28,7 +28,7 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(All, LayoutFlexibleBoxTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, LayoutFlexibleBoxTest, testing::Bool());
 
 static String CommonStyle() {
   return R"HTML(
diff --git a/third_party/blink/renderer/core/layout/layout_inline_test.cc b/third_party/blink/renderer/core/layout/layout_inline_test.cc
index a106d63..f04b615 100644
--- a/third_party/blink/renderer/core/layout/layout_inline_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_inline_test.cc
@@ -28,7 +28,7 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All, ParameterizedLayoutInlineTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, ParameterizedLayoutInlineTest, testing::Bool());
 
 TEST_P(ParameterizedLayoutInlineTest, LinesBoundingBox) {
   LoadAhem();
diff --git a/third_party/blink/renderer/core/layout/layout_object.cc b/third_party/blink/renderer/core/layout/layout_object.cc
index a377c5e5..24d534d 100644
--- a/third_party/blink/renderer/core/layout/layout_object.cc
+++ b/third_party/blink/renderer/core/layout/layout_object.cc
@@ -3267,6 +3267,12 @@
       layer->DirtyVisibleContentStatus();
   }
 
+  if (IsInLayoutNGInlineFormattingContext()) {
+    // In case of |this| layout object is moved, to avoid paint fragments in old
+    // tree live longer than |this|, we reset associated paint fragment list.
+    SetFirstInlineFragment(nullptr);
+  }
+
   if (Parent()->ChildrenInline())
     Parent()->DirtyLinesFromChangedChild(this);
 
diff --git a/third_party/blink/renderer/core/layout/layout_text_fragment.cc b/third_party/blink/renderer/core/layout/layout_text_fragment.cc
index 54ece45..563fe6f 100644
--- a/third_party/blink/renderer/core/layout/layout_text_fragment.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_fragment.cc
@@ -202,12 +202,18 @@
 }
 
 Position LayoutTextFragment::PositionForCaretOffset(unsigned offset) const {
-  DCHECK_LE(offset, FragmentLength());
+  // TODO(layout-dev): Make the following DCHECK always enabled after we
+  // properly support 'text-transform' changing text length.
+#if DCHECK_IS_ON()
+  if (StyleRef().TextTransform() == ETextTransform::kNone)
+    DCHECK_LE(offset, FragmentLength());
+#endif
   const Text* node = AssociatedTextNode();
   if (!node)
     return Position();
-  // TODO(layout-dev): Support offset change due to text-transform.
-  return Position(node, Start() + offset);
+  // TODO(layout-dev): Properly support offset change due to text-transform.
+  const unsigned clamped_offset = std::min(offset, FragmentLength());
+  return Position(node, Start() + clamped_offset);
 }
 
 base::Optional<unsigned> LayoutTextFragment::CaretOffsetForPosition(
diff --git a/third_party/blink/renderer/core/layout/layout_text_fragment_test.cc b/third_party/blink/renderer/core/layout/layout_text_fragment_test.cc
index a29e6db7..c0230fb 100644
--- a/third_party/blink/renderer/core/layout/layout_text_fragment_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_fragment_test.cc
@@ -53,9 +53,9 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        ParameterizedLayoutTextFragmentTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         ParameterizedLayoutTextFragmentTest,
+                         testing::Bool());
 
 TEST_P(ParameterizedLayoutTextFragmentTest, Basics) {
   SetBasicBody("foo");
diff --git a/third_party/blink/renderer/core/layout/layout_text_test.cc b/third_party/blink/renderer/core/layout/layout_text_test.cc
index 0296a5e..7362aa9 100644
--- a/third_party/blink/renderer/core/layout/layout_text_test.cc
+++ b/third_party/blink/renderer/core/layout/layout_text_test.cc
@@ -80,7 +80,7 @@
   bool LayoutNGEnabled() const { return GetParam(); }
 };
 
-INSTANTIATE_TEST_CASE_P(All, ParameterizedLayoutTextTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, ParameterizedLayoutTextTest, testing::Bool());
 
 }  // namespace
 
@@ -212,9 +212,9 @@
   MapDOMOffsetToTextContentOffset() : ScopedLayoutNGForTest(true) {}
 };
 
-INSTANTIATE_TEST_CASE_P(LayoutTextTest,
-                        MapDOMOffsetToTextContentOffset,
-                        testing::ValuesIn(offset_mapping_test_data));
+INSTANTIATE_TEST_SUITE_P(LayoutTextTest,
+                         MapDOMOffsetToTextContentOffset,
+                         testing::ValuesIn(offset_mapping_test_data));
 
 TEST_P(MapDOMOffsetToTextContentOffset, Basic) {
   const auto data = GetParam();
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect_test.cc b/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect_test.cc
index 1b4e290..3f98942c 100644
--- a/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_logical_rect_test.cc
@@ -41,9 +41,9 @@
     : public testing::Test,
       public testing::WithParamInterface<LogicalRectUniteTestData> {};
 
-INSTANTIATE_TEST_CASE_P(NGGeometryUnitsTest,
-                        NGLogicalRectUniteTest,
-                        testing::ValuesIn(logical_rect_unite_test_data));
+INSTANTIATE_TEST_SUITE_P(NGGeometryUnitsTest,
+                         NGLogicalRectUniteTest,
+                         testing::ValuesIn(logical_rect_unite_test_data));
 
 TEST_P(NGLogicalRectUniteTest, Data) {
   const auto& data = GetParam();
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h b/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h
index 4295353b..d08b3aa2 100644
--- a/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h
+++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset.h
@@ -57,6 +57,10 @@
     return other.left == left && other.top == top;
   }
 
+  bool operator!=(const NGPhysicalOffset& other) const {
+    return !(*this == other);
+  }
+
   // Conversions from/to existing code. New code prefers type safety for
   // logical/physical distinctions.
   explicit NGPhysicalOffset(const LayoutPoint& point) {
diff --git a/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect_test.cc b/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect_test.cc
index 9ec7a2d..85f15f3 100644
--- a/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/geometry/ng_physical_offset_rect_test.cc
@@ -41,7 +41,7 @@
     : public testing::Test,
       public testing::WithParamInterface<PhysicalOffsetRectUniteTestData> {};
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     NGGeometryUnitsTest,
     NGPhysicalOffsetRectUniteTest,
     testing::ValuesIn(physical_offset_rect_unite_test_data));
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_baseline_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_baseline_test.cc
index 4e311e71..5278637 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_baseline_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_baseline_test.cc
@@ -40,9 +40,9 @@
     : public NGBaselineTest,
       public testing::WithParamInterface<NGBaselineRequestListTestData> {};
 
-INSTANTIATE_TEST_CASE_P(NGBaselineTest,
-                        NGBaselineRequestListDataTest,
-                        testing::ValuesIn(baseline_request_list_test_data));
+INSTANTIATE_TEST_SUITE_P(NGBaselineTest,
+                         NGBaselineRequestListDataTest,
+                         testing::ValuesIn(baseline_request_list_test_data));
 
 TEST_P(NGBaselineRequestListDataTest, Data) {
   const auto& data = GetParam();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc
index 0e56a7f0..bba4bd3 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_items_builder_test.cc
@@ -367,11 +367,11 @@
 class CollapsibleSpaceTest : public NGInlineItemsBuilderTest,
                              public testing::WithParamInterface<UChar> {};
 
-INSTANTIATE_TEST_CASE_P(NGInlineItemsBuilderTest,
-                        CollapsibleSpaceTest,
-                        testing::Values(kSpaceCharacter,
-                                        kTabulationCharacter,
-                                        kNewlineCharacter));
+INSTANTIATE_TEST_SUITE_P(NGInlineItemsBuilderTest,
+                         CollapsibleSpaceTest,
+                         testing::Values(kSpaceCharacter,
+                                         kTabulationCharacter,
+                                         kNewlineCharacter));
 
 TEST_P(CollapsibleSpaceTest, CollapsedSpaceAfterNoWrap) {
   UChar space = GetParam();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
index 82e45844..73d52b96 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_inline_node_test.cc
@@ -183,7 +183,7 @@
 class NodeParameterTest : public NGInlineNodeTest,
                           public testing::WithParamInterface<const char*> {};
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     NGInlineNodeTest,
     NodeParameterTest,
     testing::Values("text",
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
index 1e04be79..4a5a1783 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_line_breaker_test.cc
@@ -411,9 +411,9 @@
     : public NGLineBreakerTest,
       public testing::WithParamInterface<WhitespaceStateTestData> {};
 
-INSTANTIATE_TEST_CASE_P(NGLineBreakerTest,
-                        NGWhitespaceStateTest,
-                        testing::ValuesIn(whitespace_state_test_data));
+INSTANTIATE_TEST_SUITE_P(NGLineBreakerTest,
+                         NGWhitespaceStateTest,
+                         testing::ValuesIn(whitespace_state_test_data));
 
 TEST_P(NGWhitespaceStateTest, WhitespaceState) {
   const auto& data = GetParam();
@@ -471,9 +471,9 @@
     : public NGLineBreakerTest,
       public testing::WithParamInterface<TrailingSpaceWidthTestData> {};
 
-INSTANTIATE_TEST_CASE_P(NGLineBreakerTest,
-                        NGTrailingSpaceWidthTest,
-                        testing::ValuesIn(trailing_space_width_test_data));
+INSTANTIATE_TEST_SUITE_P(NGLineBreakerTest,
+                         NGTrailingSpaceWidthTest,
+                         testing::ValuesIn(trailing_space_width_test_data));
 
 TEST_P(NGTrailingSpaceWidthTest, TrailingSpaceWidth) {
   const auto& data = GetParam();
diff --git a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc
index 6d1fc17..6d39095b 100644
--- a/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc
+++ b/third_party/blink/renderer/core/layout/ng/inline/ng_offset_mapping_test.cc
@@ -1162,9 +1162,9 @@
   NGOffsetMappingGetterTest() : ScopedLayoutNGForTest(GetParam()) {}
 };
 
-INSTANTIATE_TEST_CASE_P(NGOffsetMappingTest,
-                        NGOffsetMappingGetterTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(NGOffsetMappingTest,
+                         NGOffsetMappingGetterTest,
+                         testing::Bool());
 
 TEST_P(NGOffsetMappingGetterTest, Get) {
   SetBodyInnerHTML(R"HTML(
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 18ffec3..44923ef 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
@@ -222,14 +222,6 @@
       // !constraint_space.IsIntermediateLayout() block below.
       UpdateShapeOutsideInfoIfNeeded(
           *layout_result, constraint_space.PercentageResolutionInlineSize());
-      // We may need paint invalidation even if we can reuse layout, as our
-      // paint offset/visual rect may have changed due to relative
-      // positioning changes. Otherwise we fail fast/css/
-      // fast/css/relative-positioned-block-with-inline-ancestor-and-parent
-      // -dynamic.html
-      // TODO(layoutng): See if we can optimize this. When we natively
-      // support relative positioning in NG we can probably remove this,
-      box_->SetSubtreeShouldCheckForPaintInvalidation();
 
       // We have to re-set the cached result here, because it is used for
       // LayoutNGMixin::CurrentFragment and therefore has to be up-to-date.
@@ -349,6 +341,13 @@
     }
   }
 
+  // If we have an overflow clip present, we need to check for paint
+  // invalidation for the subtree in case that the overflow clip changes.
+  // TODO(crbug.com/927903): Probably PrePaintTreeWalk is the better place to
+  // do this by invalidating descendant PaintLayers only.
+  if (box_->HasOverflowClip())
+    box_->SetSubtreeShouldCheckForPaintInvalidation();
+
   CopyFragmentDataToLayoutBox(constraint_space, *layout_result);
 }
 
diff --git a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
index d99bdff..5e6f59fb 100644
--- a/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
+++ b/third_party/blink/renderer/core/layout/ng/ng_box_fragment_builder.cc
@@ -31,8 +31,8 @@
     if (!descendant.fragment->IsBox())
       continue;
     LayoutObject* key = descendant.fragment->GetLayoutObject();
-    // TODO(atotic) Is traversing continuations the right thing to do?
-    if (key->IsLayoutInline())  // key for inlines is continuation root.
+    // Key for inline is continuation root if it exists.
+    if (key->IsLayoutInline() && key->GetNode())
       key = key->GetNode()->GetLayoutObject();
     auto it = inline_containing_block_map->find(key);
     if (it == inline_containing_block_map->end()) {
diff --git a/third_party/blink/renderer/core/layout/scroll_anchor_test.cc b/third_party/blink/renderer/core/layout/scroll_anchor_test.cc
index 546e97c..5f7b133 100644
--- a/third_party/blink/renderer/core/layout/scroll_anchor_test.cc
+++ b/third_party/blink/renderer/core/layout/scroll_anchor_test.cc
@@ -80,7 +80,7 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(All, ScrollAnchorTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, ScrollAnchorTest, testing::Bool());
 
 // TODO(ymalik): Currently, this should be the first test in the file to avoid
 // failure when running with other tests. Dig into this more and fix.
diff --git a/third_party/blink/renderer/core/layout/scrollbars_test.cc b/third_party/blink/renderer/core/layout/scrollbars_test.cc
index a58b560..21f3a385f 100644
--- a/third_party/blink/renderer/core/layout/scrollbars_test.cc
+++ b/third_party/blink/renderer/core/layout/scrollbars_test.cc
@@ -1422,7 +1422,7 @@
 };
 
 // Test both overlay and non-overlay scrollbars.
-INSTANTIATE_TEST_CASE_P(All, ScrollbarAppearanceTest, testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All, ScrollbarAppearanceTest, testing::Bool());
 
 // Make sure native scrollbar can change by Emulator.
 // Disable on Android since Android always enable OverlayScrollbar.
diff --git a/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc b/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc
index 3c1515378..7034679e 100644
--- a/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc
+++ b/third_party/blink/renderer/core/layout/visual_rect_mapping_test.cc
@@ -114,7 +114,7 @@
   }
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(VisualRectMappingTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(VisualRectMappingTest);
 
 TEST_P(VisualRectMappingTest, LayoutText) {
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/loader/document_loader.cc b/third_party/blink/renderer/core/loader/document_loader.cc
index 8e890f5f6a..183dfaa 100644
--- a/third_party/blink/renderer/core/loader/document_loader.cc
+++ b/third_party/blink/renderer/core/loader/document_loader.cc
@@ -1452,19 +1452,6 @@
   if (reason == InstallNewDocumentReason::kNavigation)
     DidCommitNavigation(global_object_reuse_policy);
 
-  // TODO(yoichio): This is temporary switch to support chrome internal
-  // components migration from the old web APIs.
-  // After completion of the migration, we should remove this.
-  // See crbug.com/911943 for detail.
-  if (url.Protocol() == "chrome") {
-    OriginTrialContext::FromOrCreate(document)->AddFeature("WebComponentsV0");
-    // CrElementsProfileAvatarSelectorFocusTest, or some Web UI tests need this
-    // RuntimeEnabledFeatures on in addition to the above OriginTrialContext
-    // flag.
-    RuntimeEnabledFeatures::SetShadowDOMV0Enabled(true);
-    RuntimeEnabledFeatures::SetCustomElementsV0Enabled(true);
-    RuntimeEnabledFeatures::SetHTMLImportsEnabled(true);
-  }
   // Initializing origin trials might force window proxy initialization,
   // which later triggers CHECK when swapping in via WebFrame::Swap().
   // We can safely omit installing original trials on initial empty document
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context.cc b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
index 1eca168..32c9ecf 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context.cc
@@ -62,6 +62,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/frame/navigator.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/html_frame_owner_element.h"
@@ -538,9 +539,10 @@
       NetworkHintsInterfaceImpl(), resource_loading_policy,
       PreloadHelper::kLoadAll, nullptr);
 
-  if (response.HasMajorCertificateErrors()) {
+  if (response.HasMajorCertificateErrors() &&
+      request.GetFrameType() !=
+          network::mojom::RequestContextFrameType::kTopLevel) {
     MixedContentChecker::HandleCertificateError(GetFrame(), response,
-                                                request.GetFrameType(),
                                                 request.GetRequestContext());
   }
 
@@ -858,6 +860,17 @@
         AtomicString(NetworkStateNotifier::EffectiveConnectionTypeToString(
             holdback_ect.value())));
   }
+
+  if (ShouldSendClientHint(mojom::WebClientHintsType::kLang, hints_preferences,
+                           enabled_hints)) {
+    request.AddHTTPHeaderField(
+        blink::kClientHintsHeaderMapping[static_cast<size_t>(
+            mojom::WebClientHintsType::kLang)],
+        GetFrame()
+            ->DomWindow()
+            ->navigator()
+            ->SerializeLanguagesForClientHintHeader());
+  }
 }
 
 void FrameFetchContext::PopulateResourceRequest(
diff --git a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
index b82a96a4..7ec17b1 100644
--- a/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
+++ b/third_party/blink/renderer/core/loader/frame_fetch_context_test.cc
@@ -50,7 +50,9 @@
 #include "third_party/blink/renderer/core/frame/ad_tracker.h"
 #include "third_party/blink/renderer/core/frame/frame_owner.h"
 #include "third_party/blink/renderer/core/frame/frame_types.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
+#include "third_party/blink/renderer/core/frame/navigator.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/html_iframe_element.h"
@@ -791,6 +793,31 @@
   ExpectHeader("https://www.example.com/1.gif", "DPR", false, "");
 }
 
+TEST_F(FrameFetchContextHintsTest, MonitorLangHint) {
+  ExpectHeader("https://www.example.com/1.gif", "Sec-CH-Lang", false, "");
+  ExpectHeader("http://www.example.com/1.gif", "Sec-CH-Lang", false, "");
+
+  ClientHintsPreferences preferences;
+  preferences.SetShouldSendForTesting(mojom::WebClientHintsType::kLang);
+  document->GetFrame()->GetClientHintsPreferences().UpdateFrom(preferences);
+
+  document->domWindow()->navigator()->SetLanguagesForTesting("en-US");
+  ExpectHeader("https://www.example.com/1.gif", "Sec-CH-Lang", true,
+               "\"en-US\"");
+  ExpectHeader("http://www.example.com/1.gif", "Sec-CH-Lang", false, "");
+
+  document->domWindow()->navigator()->SetLanguagesForTesting("en,de,fr");
+  ExpectHeader("https://www.example.com/1.gif", "Sec-CH-Lang", true,
+               "\"en\", \"de\", \"fr\"");
+  ExpectHeader("http://www.example.com/1.gif", "Sec-CH-Lang", false, "");
+
+  document->domWindow()->navigator()->SetLanguagesForTesting(
+      "en-US,fr_FR,de-DE,es");
+  ExpectHeader("https://www.example.com/1.gif", "Sec-CH-Lang", true,
+               "\"en-US\", \"fr-FR\", \"de-DE\", \"es\"");
+  ExpectHeader("http://www.example.com/1.gif", "Sec-CH-Lang", false, "");
+}
+
 TEST_F(FrameFetchContextHintsTest, MonitorAllHints) {
   ExpectHeader("https://www.example.com/1.gif", "Device-Memory", false, "");
   ExpectHeader("https://www.example.com/1.gif", "DPR", false, "");
@@ -799,6 +826,7 @@
   ExpectHeader("https://www.example.com/1.gif", "rtt", false, "");
   ExpectHeader("https://www.example.com/1.gif", "downlink", false, "");
   ExpectHeader("https://www.example.com/1.gif", "ect", false, "");
+  ExpectHeader("https://www.example.com/1.gif", "Sec-CH-Lang", false, "");
 
   ClientHintsPreferences preferences;
   preferences.SetShouldSendForTesting(mojom::WebClientHintsType::kDeviceMemory);
@@ -810,6 +838,7 @@
   preferences.SetShouldSendForTesting(mojom::WebClientHintsType::kRtt);
   preferences.SetShouldSendForTesting(mojom::WebClientHintsType::kDownlink);
   preferences.SetShouldSendForTesting(mojom::WebClientHintsType::kEct);
+  preferences.SetShouldSendForTesting(mojom::WebClientHintsType::kLang);
   ApproximatedDeviceMemory::SetPhysicalMemoryMBForTesting(4096);
   document->GetFrame()->GetClientHintsPreferences().UpdateFrom(preferences);
   ExpectHeader("https://www.example.com/1.gif", "Device-Memory", true, "4");
@@ -817,6 +846,10 @@
   ExpectHeader("https://www.example.com/1.gif", "Width", true, "400", 400);
   ExpectHeader("https://www.example.com/1.gif", "Viewport-Width", true, "500");
 
+  document->domWindow()->navigator()->SetLanguagesForTesting("en,de,fr");
+  ExpectHeader("https://www.example.com/1.gif", "Sec-CH-Lang", true,
+               "\"en\", \"de\", \"fr\"");
+
   // Value of network quality client hints may vary, so only check if the
   // header is present and the values are non-negative/non-empty.
   bool conversion_ok = false;
diff --git a/third_party/blink/renderer/core/loader/link_loader_test.cc b/third_party/blink/renderer/core/loader/link_loader_test.cc
index 7fe2097..24b34478 100644
--- a/third_party/blink/renderer/core/loader/link_loader_test.cc
+++ b/third_party/blink/renderer/core/loader/link_loader_test.cc
@@ -204,9 +204,9 @@
   TestPreload(params, expectations);
 }
 
-INSTANTIATE_TEST_CASE_P(LinkLoaderPreloadTest,
-                        LinkLoaderPreloadTest,
-                        testing::ValuesIn(kPreloadTestParams));
+INSTANTIATE_TEST_SUITE_P(LinkLoaderPreloadTest,
+                         LinkLoaderPreloadTest,
+                         testing::ValuesIn(kPreloadTestParams));
 
 struct PreloadMimeTypeTestParams {
   const char* href;
@@ -284,9 +284,9 @@
   TestPreload(params, expectations);
 }
 
-INSTANTIATE_TEST_CASE_P(LinkLoaderPreloadMimeTypeTest,
-                        LinkLoaderPreloadMimeTypeTest,
-                        testing::ValuesIn(kPreloadMimeTypeTestParams));
+INSTANTIATE_TEST_SUITE_P(LinkLoaderPreloadMimeTypeTest,
+                         LinkLoaderPreloadMimeTypeTest,
+                         testing::ValuesIn(kPreloadMimeTypeTestParams));
 
 struct PreloadMediaTestParams {
   const char* media;
@@ -319,9 +319,9 @@
   TestPreload(params, expectations);
 }
 
-INSTANTIATE_TEST_CASE_P(LinkLoaderPreloadMediaTest,
-                        LinkLoaderPreloadMediaTest,
-                        testing::ValuesIn(kPreloadMediaTestParams));
+INSTANTIATE_TEST_SUITE_P(LinkLoaderPreloadMediaTest,
+                         LinkLoaderPreloadMediaTest,
+                         testing::ValuesIn(kPreloadMediaTestParams));
 
 constexpr network::mojom::ReferrerPolicy kPreloadReferrerPolicyTestParams[] = {
     network::mojom::ReferrerPolicy::kOrigin,
@@ -348,9 +348,9 @@
   TestPreload(params, expectations);
 }
 
-INSTANTIATE_TEST_CASE_P(LinkLoaderPreloadReferrerPolicyTest,
-                        LinkLoaderPreloadReferrerPolicyTest,
-                        testing::ValuesIn(kPreloadReferrerPolicyTestParams));
+INSTANTIATE_TEST_SUITE_P(LinkLoaderPreloadReferrerPolicyTest,
+                         LinkLoaderPreloadReferrerPolicyTest,
+                         testing::ValuesIn(kPreloadReferrerPolicyTestParams));
 
 struct PreloadNonceTestParams {
   const char* nonce;
@@ -388,9 +388,9 @@
   TestPreload(params, expectations);
 }
 
-INSTANTIATE_TEST_CASE_P(LinkLoaderPreloadNonceTest,
-                        LinkLoaderPreloadNonceTest,
-                        testing::ValuesIn(kPreloadNonceTestParams));
+INSTANTIATE_TEST_SUITE_P(LinkLoaderPreloadNonceTest,
+                         LinkLoaderPreloadNonceTest,
+                         testing::ValuesIn(kPreloadNonceTestParams));
 
 struct PreloadImageSrcsetTestParams {
   const char* href;
@@ -440,9 +440,9 @@
   TestPreload(params, expectations);
 }
 
-INSTANTIATE_TEST_CASE_P(LinkLoaderPreloadImageSrcsetTest,
-                        LinkLoaderPreloadImageSrcsetTest,
-                        testing::ValuesIn(kPreloadImageSrcsetTestParams));
+INSTANTIATE_TEST_SUITE_P(LinkLoaderPreloadImageSrcsetTest,
+                         LinkLoaderPreloadImageSrcsetTest,
+                         testing::ValuesIn(kPreloadImageSrcsetTestParams));
 
 struct ModulePreloadTestParams {
   const char* href;
@@ -528,9 +528,9 @@
   ASSERT_EQ(test_case.expecting_load, modulator->fetched());
 }
 
-INSTANTIATE_TEST_CASE_P(LinkLoaderModulePreloadTest,
-                        LinkLoaderModulePreloadTest,
-                        testing::ValuesIn(kModulePreloadTestParams));
+INSTANTIATE_TEST_SUITE_P(LinkLoaderModulePreloadTest,
+                         LinkLoaderModulePreloadTest,
+                         testing::ValuesIn(kModulePreloadTestParams));
 
 TEST(LinkLoaderTest, Prefetch) {
   struct TestCase {
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker.cc b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
index 068ee603..4f015e1 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
@@ -724,19 +724,13 @@
 void MixedContentChecker::HandleCertificateError(
     LocalFrame* frame,
     const ResourceResponse& response,
-    network::mojom::RequestContextFrameType frame_type,
     mojom::RequestContextType request_context) {
-  Frame* effective_frame = EffectiveFrameForFrameType(frame, frame_type);
-  if (frame_type == network::mojom::RequestContextFrameType::kTopLevel ||
-      !effective_frame)
-    return;
-
   // Use the current local frame's client; the embedder doesn't distinguish
   // mixed content signals from different frames on the same page.
   LocalFrameClient* client = frame->Client();
   bool strict_mixed_content_checking_for_plugin =
-      effective_frame->GetSettings() &&
-      effective_frame->GetSettings()->GetStrictMixedContentCheckingForPlugin();
+      frame->GetSettings() &&
+      frame->GetSettings()->GetStrictMixedContentCheckingForPlugin();
   WebMixedContentContextType context_type =
       WebMixedContent::ContextTypeFromRequestContext(
           request_context, strict_mixed_content_checking_for_plugin);
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker.h b/third_party/blink/renderer/core/loader/mixed_content_checker.h
index 4ee3393..4e0cad7 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.h
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.h
@@ -112,7 +112,6 @@
 
   static void HandleCertificateError(LocalFrame*,
                                      const ResourceResponse&,
-                                     network::mojom::RequestContextFrameType,
                                      mojom::RequestContextType);
 
   // Receive information about mixed content found externally.
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc b/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
index 2a15a0e..b04012d 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker_test.cc
@@ -134,7 +134,6 @@
   EXPECT_CALL(*client, DidRunContentWithCertificateErrors());
   MixedContentChecker::HandleCertificateError(
       &dummy_page_holder->GetFrame(), response1,
-      network::mojom::RequestContextFrameType::kNone,
       mojom::RequestContextType::SCRIPT);
 
   ResourceResponse response2(displayed_url);
@@ -146,9 +145,8 @@
                                .GetSettings()
                                ->GetStrictMixedContentCheckingForPlugin()));
   EXPECT_CALL(*client, DidDisplayContentWithCertificateErrors());
-  MixedContentChecker::HandleCertificateError(
-      &dummy_page_holder->GetFrame(), response2,
-      network::mojom::RequestContextFrameType::kNone, request_context);
+  MixedContentChecker::HandleCertificateError(&dummy_page_holder->GetFrame(),
+                                              response2, request_context);
 }
 
 TEST(MixedContentCheckerTest, DetectMixedForm) {
diff --git a/third_party/blink/renderer/core/loader/preload_helper.cc b/third_party/blink/renderer/core/loader/preload_helper.cc
index 890c1bf..98d6dec 100644
--- a/third_party/blink/renderer/core/loader/preload_helper.cc
+++ b/third_party/blink/renderer/core/loader/preload_helper.cc
@@ -209,6 +209,7 @@
   KURL url;
   if (resource_type == ResourceType::kImage && !params.image_srcset.IsEmpty() &&
       RuntimeEnabledFeatures::PreloadImageSrcSetEnabled()) {
+    UseCounter::Count(document, WebFeature::kLinkRelPreloadImageSrcset);
     media_values = CreateMediaValues(document, viewport_description);
     float source_size =
         SizesAttributeParser(media_values, params.image_sizes).length();
diff --git a/third_party/blink/renderer/core/loader/private/frame_client_hints_preferences_context.cc b/third_party/blink/renderer/core/loader/private/frame_client_hints_preferences_context.cc
index aa29d3b..480d0344 100644
--- a/third_party/blink/renderer/core/loader/private/frame_client_hints_preferences_context.cc
+++ b/third_party/blink/renderer/core/loader/private/frame_client_hints_preferences_context.cc
@@ -21,6 +21,7 @@
     WebFeature::kClientHintsRtt,
     WebFeature::kClientHintsDownlink,
     WebFeature::kClientHintsEct,
+    WebFeature::kClientHintsLang,
 };
 
 static_assert(static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1 ==
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 b602826..45758c8 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
@@ -979,9 +979,9 @@
   EXPECT_FALSE(full_image_resource->ShouldShowPlaceholder());
 }
 
-INSTANTIATE_TEST_CASE_P(/* no prefix */,
-                        ImageResourceReloadTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(/* no prefix */,
+                         ImageResourceReloadTest,
+                         testing::Bool());
 
 TEST(ImageResourceTest, SVGImage) {
   KURL url("http://127.0.0.1:8000/foo");
diff --git a/third_party/blink/renderer/core/page/print_context_test.cc b/third_party/blink/renderer/core/page/print_context_test.cc
index 2072557..1c6bbfa 100644
--- a/third_party/blink/renderer/core/page/print_context_test.cc
+++ b/third_party/blink/renderer/core/page/print_context_test.cc
@@ -151,7 +151,7 @@
   EXPECT_EQ(expectedWidth, actualRect.width());                               \
   EXPECT_EQ(expectedHeight, actualRect.height());
 
-INSTANTIATE_PAINT_TEST_CASE_P(PrintContextTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PrintContextTest);
 
 TEST_P(PrintContextTest, LinkTarget) {
   MockPageContextCanvas canvas;
@@ -312,7 +312,7 @@
   EXPECT_SKRECT_EQ(50, 60, 200, 100, operations[0].rect);
 }
 
-INSTANTIATE_PAINT_TEST_CASE_P(PrintContextFrameTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PrintContextFrameTest);
 
 TEST_P(PrintContextFrameTest, WithSubframe) {
   GetDocument().SetBaseURLOverride(KURL("http://a.com/"));
diff --git a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
index 0ad8b4b..b4b7af9 100644
--- a/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/main_thread_scrolling_reasons_test.cc
@@ -105,8 +105,7 @@
   frame_test_helpers::WebViewHelper helper_;
 };
 
-INSTANTIATE_TEST_CASE_P(All, MainThreadScrollingReasonsTest, testing::Bool());
-
+INSTANTIATE_TEST_SUITE_P(All, MainThreadScrollingReasonsTest, testing::Bool());
 
 TEST_P(MainThreadScrollingReasonsTest,
        CustomScrollbarShouldTriggerMainThreadScroll) {
@@ -412,9 +411,9 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        NonCompositedMainThreadScrollingReasonsTest,
-                        testing::Bool());
+INSTANTIATE_TEST_SUITE_P(All,
+                         NonCompositedMainThreadScrollingReasonsTest,
+                         testing::Bool());
 
 TEST_P(NonCompositedMainThreadScrollingReasonsTest, TransparentTest) {
   TestNonCompositedReasons("transparent",
diff --git a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc
index 1d0e45c..220aa7e1 100644
--- a/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc
+++ b/third_party/blink/renderer/core/page/scrolling/scrolling_coordinator_test.cc
@@ -157,7 +157,7 @@
   frame_test_helpers::WebViewHelper helper_;
 };
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     All,
     ScrollingCoordinatorTest,
     ::testing::Values(kScrollingCoordinatorTestNoFlags,
@@ -1426,7 +1426,7 @@
   FakeGLES2Interface gl_;
 };
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     All,
     ScrollingCoordinatorTestWithAcceleratedContext,
     ::testing::Values(kScrollingCoordinatorTestNoFlags,
diff --git a/third_party/blink/renderer/core/paint/block_painter_test.cc b/third_party/blink/renderer/core/paint/block_painter_test.cc
index a4b5a3a9..528709a5 100644
--- a/third_party/blink/renderer/core/paint/block_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/block_painter_test.cc
@@ -20,7 +20,7 @@
 
 using BlockPainterTest = PaintControllerPaintTest;
 
-INSTANTIATE_PAINT_TEST_CASE_P(BlockPainterTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(BlockPainterTest);
 
 TEST_P(BlockPainterTest, ScrollHitTestProperties) {
   if (!RuntimeEnabledFeatures::CompositeAfterPaintEnabled())
diff --git a/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc b/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc
index 1278853..9b48a50c1 100644
--- a/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc
+++ b/third_party/blink/renderer/core/paint/box_paint_invalidator_test.cc
@@ -96,7 +96,7 @@
   FragmentData fragment_data_;
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(BoxPaintInvalidatorTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(BoxPaintInvalidatorTest);
 
 TEST_P(BoxPaintInvalidatorTest, ComputePaintInvalidationReasonPaintingNothing) {
   SetUpHTML();
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
index 3fa2735..8216ab0 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.cc
@@ -75,6 +75,7 @@
 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
 #include "third_party/blink/renderer/platform/graphics/paint/cull_rect.h"
 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
+#include "third_party/blink/renderer/platform/graphics/paint/geometry_mapper.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_controller.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/transforms/transform_state.h"
@@ -1020,7 +1021,6 @@
     const PaintLayer* compositing_container,
     const IntPoint& snapped_offset_from_composited_ancestor,
     Vector<GraphicsLayerPaintInfo>& layers,
-    LayoutPoint* offset_from_transformed_ancestor,
     Vector<PaintLayer*>& layers_needing_paint_invalidation) {
   if (!squashing_layer_)
     return;
@@ -1142,11 +1142,6 @@
     GetLayoutObject().SetNeedsPaintPropertyUpdate();
   }
 
-  *offset_from_transformed_ancestor =
-      compositing_container_offset_from_transformed_ancestor;
-  offset_from_transformed_ancestor->Move(
-      squash_layer_origin_in_compositing_container_space);
-
   for (wtf_size_t i = 0; i < layers.size(); ++i) {
     LocalClipRectForSquashedLayer(owning_layer_, layers, layers[i]);
   }
@@ -1209,7 +1204,6 @@
   UpdateSquashingLayerGeometry(
       graphics_layer_parent_location, compositing_container,
       snapped_offset_from_composited_ancestor, squashed_layers_,
-      &squashing_layer_offset_from_transformed_ancestor_,
       layers_needing_paint_invalidation);
 
   UpdateChildTransformLayerGeometry();
@@ -3179,87 +3173,50 @@
     const GraphicsLayer* graphics_layer) const {
   IntRect graphics_layer_bounds(IntPoint(), IntSize(graphics_layer->Size()));
 
-  FloatSize offset_from_anchor_layout_object;
-  const LayoutBoxModelObject* anchor_layout_object;
-  bool should_apply_anchor_overflow_clip = false;
-  if (graphics_layer == squashing_layer_.get()) {
-    // All squashed layers have the same clip and transform space, so we can use
-    // the first squashed layer's layoutObject to map the squashing layer's
-    // bounds into viewport space, with offsetFromAnchorLayoutObject to
-    // translate squashing layer's bounds into the first squashed layer's space.
-    anchor_layout_object =
-        &owning_layer_.TransformAncestorOrRoot().GetLayoutObject();
-    offset_from_anchor_layout_object =
-        ToFloatSize(FloatPoint(SquashingOffsetFromTransformedAncestor()));
+  FloatClipRect mapping_rect((FloatRect(graphics_layer_bounds)));
 
-    // SquashingOffsetFromTransformedAncestor does not include scroll
-    // offset.
-    if (anchor_layout_object->UsesCompositedScrolling()) {
-      offset_from_anchor_layout_object -=
-          FloatSize(ToLayoutBox(anchor_layout_object)->ScrolledContentOffset());
-    }
-  } else {
-    DCHECK(graphics_layer == graphics_layer_.get() ||
-           graphics_layer == scrolling_contents_layer_.get());
-    anchor_layout_object = &owning_layer_.GetLayoutObject();
-    IntSize offset = graphics_layer->OffsetFromLayoutObject();
-    should_apply_anchor_overflow_clip =
-        AdjustForCompositedScrolling(graphics_layer, offset);
-    offset_from_anchor_layout_object = FloatSize(offset);
-  }
+  PropertyTreeState source_state = graphics_layer->GetPropertyTreeState();
 
-  LayoutView* root_view = anchor_layout_object->View();
+  LayoutView* root_view = owning_layer_.GetLayoutObject().View();
   while (root_view->GetFrame()->OwnerLayoutObject())
     root_view = root_view->GetFrame()->OwnerLayoutObject()->View();
 
-  // Start with the bounds of the graphics layer in the space of the anchor
-  // LayoutObject.
-  FloatRect graphics_layer_bounds_in_object_space(graphics_layer_bounds);
-  graphics_layer_bounds_in_object_space.Move(offset_from_anchor_layout_object);
-  if (should_apply_anchor_overflow_clip && anchor_layout_object != root_view) {
-    FloatRect clip_rect(
-        ToLayoutBox(anchor_layout_object)->OverflowClipRect(LayoutPoint()));
-    graphics_layer_bounds_in_object_space.Intersect(clip_rect);
-  }
+  PropertyTreeState root_view_contents_state =
+      root_view->FirstFragment().ContentsProperties();
+  PropertyTreeState root_view_border_box_state =
+      root_view->FirstFragment().LocalBorderBoxProperties();
 
-  // Now map the bounds to its visible content rect in root view space,
-  // including applying clips along the way.
-  LayoutRect graphics_layer_bounds_in_root_view_space(
-      graphics_layer_bounds_in_object_space);
+  // 1. Move into local transform space.
+  mapping_rect.MoveBy(FloatPoint(graphics_layer->GetOffsetFromTransformNode()));
+  // 2. Map into contents space of the root LayoutView.
+  GeometryMapper::LocalToAncestorVisualRect(
+      source_state, root_view_contents_state, mapping_rect);
 
-  anchor_layout_object->MapToVisualRectInAncestorSpace(
-      root_view, graphics_layer_bounds_in_root_view_space, kUseGeometryMapper);
+  FloatRect visible_content_rect = mapping_rect.Rect();
 
-  // MapToVisualRectInAncestorSpace doesn't account for the root scroll because
-  // it earlies out as soon as we reach this ancestor. That is, it only maps to
-  // the space of the root_view, not accounting for the fact that the root_view
-  // itself can be scrolled. If the root_view is our anchor_layout_object, then
-  // this extra offset is counted in offset_from_anchor_layout_object. In other
-  // cases, we need to account for it here. Otherwise, the paint clip below
-  // might clip the whole (visible) rect out.
-  if (root_view != anchor_layout_object) {
-    if (auto* scrollable_area = root_view->GetScrollableArea()) {
-      graphics_layer_bounds_in_root_view_space.MoveBy(
-          -scrollable_area->VisibleContentRect().Location());
-    }
-  }
+  // 3. Move into local border box transform space of the root LayoutView.
+  // Note that the overflow clip has *not* been applied.
+  GeometryMapper::SourceToDestinationRect(
+      root_view_contents_state.Transform(),
+      root_view_border_box_state.Transform(), visible_content_rect);
 
-  FloatRect visible_content_rect(graphics_layer_bounds_in_root_view_space);
+  // 4. Apply overflow clip, or adjusted version if necessary.
   root_view->GetFrameView()->ClipPaintRect(&visible_content_rect);
 
-  // Map the visible content rect from root view space to local graphics layer
-  // space.
   FloatRect local_interest_rect;
   // If the visible content rect is empty, then it makes no sense to map it back
   // since there is nothing to map.
   if (!visible_content_rect.IsEmpty()) {
-    local_interest_rect = FloatRect(
-        anchor_layout_object
-            ->AbsoluteToLocalQuad(visible_content_rect,
-                                  kUseTransforms | kTraverseDocumentBoundaries)
-            .EnclosingBoundingBox());
-    local_interest_rect.Move(-offset_from_anchor_layout_object);
-    // TODO(chrishtr): the code below is a heuristic, instead we should detect
+    local_interest_rect = visible_content_rect;
+    // 5. Map the visible content rect from root view space to local graphics
+    // layer space.
+    GeometryMapper::SourceToDestinationRect(
+        root_view_border_box_state.Transform(), source_state.Transform(),
+        local_interest_rect);
+    local_interest_rect.MoveBy(
+        -FloatPoint(graphics_layer->GetOffsetFromTransformNode()));
+
+    // TODO(chrishtr): the code below is a heuristic. Instead we should detect
     // and return whether the mapping failed.  In some cases,
     // absoluteToLocalQuad can fail to map back to the local space, due to
     // passing through non-invertible transforms or floating-point accuracy
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
index 9deb283..b08ba01 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping.h
@@ -278,12 +278,6 @@
 
   LayoutSize ContentOffsetInCompositingLayer() const;
 
-  // Returned value does not include any composited scroll offset of
-  // the transform ancestor.
-  LayoutPoint SquashingOffsetFromTransformedAncestor() const {
-    return squashing_layer_offset_from_transformed_ancestor_;
-  }
-
   // If there is a squashed layer painting into this CLM that is an ancestor of
   // the given LayoutObject, return it. Otherwise return nullptr.
   const GraphicsLayerPaintInfo* ContainingSquashedLayer(
@@ -355,7 +349,6 @@
       const PaintLayer* compositing_container,
       const IntPoint& snapped_offset_from_composited_ancestor,
       Vector<GraphicsLayerPaintInfo>& layers,
-      LayoutPoint* offset_from_transformed_ancestor,
       Vector<PaintLayer*>& layers_needing_paint_invalidation);
   void UpdateMainGraphicsLayerGeometry(
       const IntRect& relative_compositing_bounds,
@@ -666,7 +659,6 @@
   // layers paint into.
   std::unique_ptr<GraphicsLayer> squashing_layer_;
   Vector<GraphicsLayerPaintInfo> squashed_layers_;
-  LayoutPoint squashing_layer_offset_from_transformed_ancestor_;
   IntSize squashing_layer_offset_from_layout_object_;
 
   LayoutRect composited_bounds_;
diff --git a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
index 662a396..913c060 100644
--- a/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
+++ b/third_party/blink/renderer/core/paint/compositing/composited_layer_mapping_test.cc
@@ -2632,17 +2632,10 @@
   auto* squashed =
       ToLayoutBoxModelObject(GetLayoutObjectByElementId("squashed"))->Layer();
   EXPECT_EQ(kPaintsIntoGroupedBacking, squashed->GetCompositingState());
-  EXPECT_EQ(
-      LayoutPoint(),
-      squashed->GroupedMapping()->SquashingOffsetFromTransformedAncestor());
 
   GetDocument().View()->LayoutViewport()->ScrollBy(ScrollOffset(0, 25),
                                                    kUserScroll);
   UpdateAllLifecyclePhasesForTest();
-
-  EXPECT_EQ(
-      LayoutPoint(),
-      squashed->GroupedMapping()->SquashingOffsetFromTransformedAncestor());
 }
 
 TEST_F(CompositedLayerMappingTest, SquashingScrollInterestRect) {
diff --git a/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc b/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc
index f32026e..982458f 100644
--- a/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/html_canvas_painter_test.cc
@@ -76,7 +76,7 @@
   FakeGLES2Interface gl_;
 };
 
-INSTANTIATE_CAP_TEST_CASE_P(HTMLCanvasPainterTestForCAP);
+INSTANTIATE_CAP_TEST_SUITE_P(HTMLCanvasPainterTestForCAP);
 
 TEST_P(HTMLCanvasPainterTestForCAP, Canvas2DLayerAppearsInLayerTree) {
   // Insert a <canvas> and force it into accelerated mode.
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 d3da4806..e27e22c 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
@@ -115,7 +115,7 @@
   frame_test_helpers::WebViewHelper web_view_helper_;
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(LinkHighlightImplTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(LinkHighlightImplTest);
 
 TEST_P(LinkHighlightImplTest, verifyWebViewImplIntegration) {
   WebViewImpl* web_view_impl = web_view_helper_.GetWebView();
diff --git a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
index 7e156a3c..37e8a91 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_box_fragment_painter.cc
@@ -1155,11 +1155,15 @@
     const LayoutPoint& physical_offset,
     HitTestAction action) {
   const NGPhysicalFragment& fragment = paint_fragment.PhysicalFragment();
+
+  // Note: Floats should only be hit tested in the |kHitTestFloat| phase, so we
+  // shouldn't enter a float when |action| doesn't match. However, as floats may
+  // scatter around in the entire inline formatting context, we should always
+  // enter non-floating inline child boxes to search for floats in the
+  // |kHitTestFloat| phase, unless the child box forms another context.
+
   if (fragment.IsFloating() && action != kHitTestFloat)
     return false;
-  // Lines and inlines are hit tested only in the foreground phase.
-  if (fragment.IsInline() && action != kHitTestForeground)
-    return false;
 
   if (!FragmentRequiresLegacyFallback(fragment)) {
     // TODO(layout-dev): Implement HitTestAllPhases in NG after we stop
@@ -1170,6 +1174,9 @@
         .NodeAtPoint(result, location_in_container, physical_offset, action);
   }
 
+  if (fragment.IsInline() && action != kHitTestForeground)
+    return false;
+
   LayoutBox* const layout_box = ToLayoutBox(fragment.GetLayoutObject());
 
   // To be passed as |accumulated_offset| to legacy hit test functions of
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 242d4a66..aaf9553 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
@@ -269,13 +269,20 @@
     // If the LayoutObject are the same, the new paint fragment should have the
     // same DisplayItemClient identity as the previous instance.
     if (previous_instance->GetLayoutObject() == fragment->GetLayoutObject()) {
+      // If our fragment has changed size or location, mark ourselves as
+      // requiring a full paint invalidation. We always require a full paint
+      // invalidation if we aren't a box, e.g. a text fragment.
+      if (previous_instance->physical_fragment_->Size() != fragment->Size() ||
+          previous_instance->offset_ != offset ||
+          fragment->Type() != NGPhysicalFragment::kFragmentBox)
+        previous_instance->SetShouldDoFullPaintInvalidation();
+
       previous_instance->physical_fragment_ = std::move(fragment);
       previous_instance->offset_ = offset;
       previous_instance->next_for_same_layout_object_ = nullptr;
       previous_instance->is_dirty_inline_ = false;
       if (!*populate_children)
         previous_instance->first_child_ = nullptr;
-      previous_instance->SetShouldDoFullPaintInvalidation();
       return previous_instance;
     }
   }
@@ -443,7 +450,7 @@
                       std::move(previous_child), &populate_children);
 
     if (children_are_inline) {
-      if (!child_fragment->IsOutOfFlowPositioned() &&
+      if (!child_fragment->IsFloatingOrOutOfFlowPositioned() &&
           !child_fragment->IsListMarker()) {
         if (LayoutObject* layout_object = child_fragment->GetLayoutObject())
           child->AssociateWithLayoutObject(layout_object, last_fragment_map);
diff --git a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter_test.cc b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter_test.cc
index 460f49e..03ae098b 100644
--- a/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/ng/ng_text_fragment_painter_test.cc
@@ -31,7 +31,7 @@
         ScopedLayoutNGForTest(true) {}
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(NGTextFragmentPainterTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(NGTextFragmentPainterTest);
 
 TEST_P(NGTextFragmentPainterTest, TestTextStyle) {
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
index 53f6c8f..187d292 100644
--- a/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_and_raster_invalidation_test.cc
@@ -57,7 +57,7 @@
   )HTML");
 }
 
-INSTANTIATE_PAINT_TEST_CASE_P(PaintAndRasterInvalidationTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PaintAndRasterInvalidationTest);
 
 TEST_P(PaintAndRasterInvalidationTest, TrackingForTracing) {
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc b/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc
index e0152cd..83399364 100644
--- a/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_controller_paint_test.cc
@@ -20,10 +20,10 @@
 
 namespace blink {
 
-INSTANTIATE_PAINT_TEST_CASE_P(PaintControllerPaintTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PaintControllerPaintTest);
 
 using PaintControllerPaintTestForCAP = PaintControllerPaintTest;
-INSTANTIATE_CAP_TEST_CASE_P(PaintControllerPaintTestForCAP);
+INSTANTIATE_CAP_TEST_SUITE_P(PaintControllerPaintTestForCAP);
 
 TEST_P(PaintControllerPaintTest, FullDocumentPaintingWithCaret) {
   SetBodyInnerHTML(
diff --git a/third_party/blink/renderer/core/paint/paint_layer.cc b/third_party/blink/renderer/core/paint/paint_layer.cc
index 86e1c34..a197d63e 100644
--- a/third_party/blink/renderer/core/paint/paint_layer.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer.cc
@@ -599,20 +599,20 @@
   if (!paint_invalidation_layer->GroupedMapping())
     return;
 
-  LayoutBoxModelObject& transformed_ancestor =
-      paint_invalidation_layer->TransformAncestorOrRoot().GetLayoutObject();
+  GraphicsLayer* squashing_layer =
+      paint_invalidation_layer->GroupedMapping()->SquashingLayer();
 
-  // |paintInvalidationContainer| may have a local 2D transform on it, so take
-  // that into account when mapping into the space of the transformed ancestor.
-  point = paint_invalidation_container.LocalToAncestorPoint(
-      point, &transformed_ancestor);
-  // Don't include composited scroll offsets, since
-  // SquashingOffsetFromTransformedAncestor does not.
-  if (transformed_ancestor.UsesCompositedScrolling())
-    point.Move(ToLayoutBox(transformed_ancestor).ScrolledContentOffset());
+  PropertyTreeState source_state =
+      paint_invalidation_container.FirstFragment().LocalBorderBoxProperties();
+  PropertyTreeState dest_state = squashing_layer->GetPropertyTreeState();
 
-  point.MoveBy(-paint_invalidation_layer->GroupedMapping()
-                    ->SquashingOffsetFromTransformedAncestor());
+  // Move the point into the source_state transform space, map to dest_state
+  // transform space, then move into squashing layer state.
+  point.MoveBy(paint_invalidation_container.FirstFragment().PaintOffset());
+  point = GeometryMapper::SourceToDestinationProjection(
+              source_state.Transform(), dest_state.Transform())
+              .MapPoint(point);
+  point.MoveBy(-squashing_layer->GetOffsetFromTransformNode());
 }
 
 void PaintLayer::MapRectInPaintInvalidationContainerToBacking(
@@ -622,22 +622,20 @@
   if (!paint_invalidation_layer->GroupedMapping())
     return;
 
-  LayoutBoxModelObject& transformed_ancestor =
-      paint_invalidation_layer->TransformAncestorOrRoot().GetLayoutObject();
+  GraphicsLayer* squashing_layer =
+      paint_invalidation_layer->GroupedMapping()->SquashingLayer();
 
-  // |paintInvalidationContainer| may have a local 2D transform on it, so take
-  // that into account when mapping into the space of the transformed ancestor.
-  rect = LayoutRect(
-      paint_invalidation_container
-          .LocalToAncestorQuad(FloatRect(rect), &transformed_ancestor)
-          .BoundingBox());
-  // Don't include composited scroll offsets, since
-  // SquashingOffsetFromTransformedAncestor does not.
-  if (transformed_ancestor.UsesCompositedScrolling())
-    rect.Move(ToLayoutBox(transformed_ancestor).ScrolledContentOffset());
+  PropertyTreeState source_state =
+      paint_invalidation_container.FirstFragment().LocalBorderBoxProperties();
+  PropertyTreeState dest_state = squashing_layer->GetPropertyTreeState();
 
-  rect.MoveBy(-paint_invalidation_layer->GroupedMapping()
-                   ->SquashingOffsetFromTransformedAncestor());
+  // Move the point into the source_state transform space, map to dest_state
+  // transform space, then move into squashing layer state.
+  rect.MoveBy(paint_invalidation_container.FirstFragment().PaintOffset());
+  rect = GeometryMapper::SourceToDestinationProjection(source_state.Transform(),
+                                                       dest_state.Transform())
+             .MapRect(rect);
+  rect.MoveBy(-squashing_layer->GetOffsetFromTransformNode());
 }
 
 void PaintLayer::DirtyVisibleContentStatus() {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_clipper.cc b/third_party/blink/renderer/core/paint/paint_layer_clipper.cc
index 5c2b826..49344b4 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_clipper.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_clipper.cc
@@ -453,11 +453,15 @@
   if (is_clipping_root && !context.ShouldRespectRootLayerClip())
     return;
 
-  PropertyTreeState source_property_tree_state(nullptr, nullptr, nullptr);
-  PropertyTreeState destination_property_tree_state(nullptr, nullptr, nullptr);
-  InitializeCommonClipRectState(context, fragment_data,
-                                source_property_tree_state,
-                                destination_property_tree_state);
+  auto source_property_tree_state = fragment_data.LocalBorderBoxProperties();
+  auto destination_property_tree_state =
+      context.root_fragment->LocalBorderBoxProperties();
+  if (context.ShouldRespectRootLayerClip()) {
+    destination_property_tree_state.SetClip(context.root_fragment->PreClip());
+  } else {
+    destination_property_tree_state.SetClip(
+        context.root_fragment->PostOverflowClip());
+  }
 
   // The background rect applies all clips *above* m_layer, but not the overflow
   // clip of m_layer. It also applies a clip to the total painting bounds
@@ -505,27 +509,6 @@
   }
 }
 
-void PaintLayerClipper::InitializeCommonClipRectState(
-    const ClipRectsContext& context,
-    const FragmentData& fragment_data,
-    PropertyTreeState& source_property_tree_state,
-    PropertyTreeState& destination_property_tree_state) const {
-  DCHECK(use_geometry_mapper_);
-  DCHECK(fragment_data.HasLocalBorderBoxProperties());
-  source_property_tree_state = fragment_data.LocalBorderBoxProperties();
-
-  DCHECK(context.root_fragment->HasLocalBorderBoxProperties());
-  destination_property_tree_state =
-      context.root_fragment->LocalBorderBoxProperties();
-
-  if (context.ShouldRespectRootLayerClip()) {
-    destination_property_tree_state.SetClip(context.root_fragment->PreClip());
-  } else {
-    destination_property_tree_state.SetClip(
-        context.root_fragment->PostOverflowClip());
-  }
-}
-
 LayoutRect PaintLayerClipper::LocalVisualRect(
     const ClipRectsContext& context) const {
   const LayoutObject& layout_object = layer_.GetLayoutObject();
diff --git a/third_party/blink/renderer/core/paint/paint_layer_clipper.h b/third_party/blink/renderer/core/paint/paint_layer_clipper.h
index dc5b2f7..e5663274 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_clipper.h
+++ b/third_party/blink/renderer/core/paint/paint_layer_clipper.h
@@ -55,7 +55,6 @@
 
 class FragmentData;
 class PaintLayer;
-class PropertyTreeState;
 
 enum ShouldRespectOverflowClipType {
   kIgnoreOverflowClip,
@@ -221,12 +220,6 @@
       ShouldRespectOverflowClipType should_apply_self_overflow_clip,
       ClipRect& output) const;
 
-  ALWAYS_INLINE void InitializeCommonClipRectState(
-      const ClipRectsContext&,
-      const FragmentData&,
-      PropertyTreeState& source_property_tree_state,
-      PropertyTreeState& destination_property_tree_state) const;
-
   // Same as calculateRects, but using GeometryMapper.
   ALWAYS_INLINE void CalculateRectsWithGeometryMapper(
       const ClipRectsContext&,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc b/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc
index d468351..71bd564 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_painter_test.cc
@@ -52,7 +52,7 @@
   }
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(PaintLayerPainterTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PaintLayerPainterTest);
 
 TEST_P(PaintLayerPainterTest, CachedSubsequence) {
   SetBodyInnerHTML(R"HTML(
@@ -978,7 +978,7 @@
 
 using PaintLayerPainterTestCAP = PaintLayerPainterTest;
 
-INSTANTIATE_CAP_TEST_CASE_P(PaintLayerPainterTestCAP);
+INSTANTIATE_CAP_TEST_SUITE_P(PaintLayerPainterTestCAP);
 
 TEST_P(PaintLayerPainterTestCAP, SimpleCullRect) {
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
index fb6fdabb0..7f1f0a48 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area.cc
@@ -2159,7 +2159,9 @@
     DCHECK(Layer()->HasCompositedLayerMapping());
     ScrollingCoordinator* scrolling_coordinator = GetScrollingCoordinator();
     bool handled_scroll =
-        Layer()->IsRootLayer() && scrolling_coordinator &&
+        (Layer()->IsRootLayer() ||
+         RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) &&
+        scrolling_coordinator &&
         scrolling_coordinator->UpdateCompositedScrollOffset(this);
 
     if (!handled_scroll) {
diff --git a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
index fc4a1d50..587d339 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_scrollable_area_test.cc
@@ -63,7 +63,7 @@
 class PaintLayerScrollableAreaTest : public PaintLayerScrollableAreaTestBase,
                                      public PaintTestConfigurations {};
 
-INSTANTIATE_PAINT_TEST_CASE_P(PaintLayerScrollableAreaTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PaintLayerScrollableAreaTest);
 using PaintLayerScrollableAreaTestSPv1 = PaintLayerScrollableAreaTestBase;
 
 TEST_P(PaintLayerScrollableAreaTest,
diff --git a/third_party/blink/renderer/core/paint/paint_layer_test.cc b/third_party/blink/renderer/core/paint/paint_layer_test.cc
index cdb169b1..82107f7 100644
--- a/third_party/blink/renderer/core/paint/paint_layer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_layer_test.cc
@@ -25,7 +25,7 @@
   }
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(PaintLayerTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PaintLayerTest);
 
 TEST_P(PaintLayerTest, ChildWithoutPaintLayer) {
   SetBodyInnerHTML(
@@ -1794,6 +1794,30 @@
             result.InnerPossiblyPseudoNode());
 }
 
+TEST_P(PaintLayerTest, HitTestFloatInsideInlineBoxContainer) {
+  LoadAhem();
+  SetBodyInnerHTML(R"HTML(
+    <style>
+      body { margin: 0; }
+      #container { font: 10px/10px Ahem; width: 70px; }
+      #inline-container { border: 1px solid black; }
+      #target { float: right; }
+    </style>
+    <div id='container'>
+      <span id='inline-container'>
+        <a href='#' id='target'>bar</a>
+        foo
+      </span>
+    </div>
+  )HTML");
+  Node* target = GetDocument().getElementById("target")->firstChild();
+  HitTestRequest request(HitTestRequest::kReadOnly | HitTestRequest::kActive);
+  HitTestLocation location(LayoutPoint(55, 5));  // At the center of "bar"
+  HitTestResult result(request, location);
+  GetDocument().GetLayoutView()->HitTest(location, result);
+  EXPECT_EQ(target, result.InnerNode());
+}
+
 TEST_P(PaintLayerTest, HitTestFirstLetterPseudoElementDisplayContents) {
   SetBodyInnerHTML(R"HTML(
     <style>
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 91566f35..7f5703f 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
@@ -114,7 +114,7 @@
 #define CHECK_EXACT_VISUAL_RECT(expected, source_object, ancestor) \
   CHECK_VISUAL_RECT(expected, source_object, ancestor, 0)
 
-INSTANTIATE_PAINT_TEST_CASE_P(PaintPropertyTreeBuilderTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PaintPropertyTreeBuilderTest);
 
 TEST_P(PaintPropertyTreeBuilderTest, FixedPosition) {
   LoadTestData("fixed-position.html");
diff --git a/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc b/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc
index 2702524..6a860a14 100644
--- a/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc
+++ b/third_party/blink/renderer/core/paint/paint_property_tree_printer_test.cc
@@ -26,7 +26,7 @@
   }
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(PaintPropertyTreePrinterTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PaintPropertyTreePrinterTest);
 
 TEST_P(PaintPropertyTreePrinterTest, SimpleTransformTree) {
   SetBodyInnerHTML("hello world");
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 39c20eb..8a4e2e9 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
@@ -15,7 +15,7 @@
 // Tests covering incremental updates of paint property trees.
 class PaintPropertyTreeUpdateTest : public PaintPropertyTreeBuilderTest {};
 
-INSTANTIATE_PAINT_TEST_CASE_P(PaintPropertyTreeUpdateTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PaintPropertyTreeUpdateTest);
 
 TEST_P(PaintPropertyTreeUpdateTest,
        ThreadedScrollingDisabledMainThreadScrollReason) {
diff --git a/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc b/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc
index 542f008a..5f49dbf 100644
--- a/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc
+++ b/third_party/blink/renderer/core/paint/pre_paint_tree_walk_test.cc
@@ -54,7 +54,7 @@
   }
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(PrePaintTreeWalkTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(PrePaintTreeWalkTest);
 
 TEST_P(PrePaintTreeWalkTest, PropertyTreesRebuiltWithBorderInvalidation) {
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/paint/table_painter_test.cc b/third_party/blink/renderer/core/paint/table_painter_test.cc
index 107bf4f..6e59de3 100644
--- a/third_party/blink/renderer/core/paint/table_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/table_painter_test.cc
@@ -16,7 +16,7 @@
 namespace blink {
 
 using TablePainterTest = PaintControllerPaintTest;
-INSTANTIATE_PAINT_TEST_CASE_P(TablePainterTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(TablePainterTest);
 
 TEST_P(TablePainterTest, Background) {
   SetBodyInnerHTML(R"HTML(
diff --git a/third_party/blink/renderer/core/paint/view_painter_test.cc b/third_party/blink/renderer/core/paint/view_painter_test.cc
index 7a72558..4b731b9d 100644
--- a/third_party/blink/renderer/core/paint/view_painter_test.cc
+++ b/third_party/blink/renderer/core/paint/view_painter_test.cc
@@ -19,7 +19,7 @@
   void RunFixedBackgroundTest(bool prefer_compositing_to_lcd_text);
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(ViewPainterTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(ViewPainterTest);
 
 void ViewPainterTest::RunFixedBackgroundTest(
     bool prefer_compositing_to_lcd_text) {
@@ -173,7 +173,7 @@
   }
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(ViewPainterTestWithPaintTouchAction);
+INSTANTIATE_PAINT_TEST_SUITE_P(ViewPainterTestWithPaintTouchAction);
 
 TEST_P(ViewPainterTestWithPaintTouchAction, TouchActionRectScrollingContents) {
   SetBodyInnerHTML(R"HTML(
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 c274b606..a1ff9cf 100644
--- a/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
+++ b/third_party/blink/renderer/core/scheduler/frame_throttling_test.cc
@@ -82,7 +82,7 @@
   }
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(FrameThrottlingTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(FrameThrottlingTest);
 
 TEST_P(FrameThrottlingTest, ThrottleInvisibleFrames) {
   SimRequest main_resource("https://example.com/", "text/html");
@@ -686,6 +686,10 @@
 
 TEST_P(FrameThrottlingTest,
        ScrollingCoordinatorShouldSkipCompositedThrottledFrame) {
+  // TODO(crbug.com/922419): The test is broken for LayoutNG.
+  if (RuntimeEnabledFeatures::LayoutNGEnabled())
+    return;
+
   WebView().GetSettings()->SetPreferCompositingToLCDTextEnabled(true);
 
   // Create a hidden frame which is throttled.
diff --git a/third_party/blink/renderer/core/script/classic_pending_script.cc b/third_party/blink/renderer/core/script/classic_pending_script.cc
index 65e1def9..8349537 100644
--- a/third_party/blink/renderer/core/script/classic_pending_script.cc
+++ b/third_party/blink/renderer/core/script/classic_pending_script.cc
@@ -98,7 +98,7 @@
       ready_state_(is_external ? kWaitingForResource : kReady),
       integrity_failure_(false) {
   CHECK(GetElement());
-  MemoryCoordinator::Instance().RegisterClient(this);
+  MemoryPressureListenerRegistry::Instance().RegisterClient(this);
 }
 
 ClassicPendingScript::~ClassicPendingScript() {}
@@ -191,7 +191,7 @@
 }
 
 void ClassicPendingScript::DisposeInternal() {
-  MemoryCoordinator::Instance().UnregisterClient(this);
+  MemoryPressureListenerRegistry::Instance().UnregisterClient(this);
   ClearResource();
   integrity_failure_ = false;
 }
@@ -269,7 +269,7 @@
 
 void ClassicPendingScript::Trace(blink::Visitor* visitor) {
   ResourceClient::Trace(visitor);
-  MemoryCoordinatorClient::Trace(visitor);
+  MemoryPressureListener::Trace(visitor);
   PendingScript::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/script/classic_pending_script.h b/third_party/blink/renderer/core/script/classic_pending_script.h
index 5860329..50fa64f 100644
--- a/third_party/blink/renderer/core/script/classic_pending_script.h
+++ b/third_party/blink/renderer/core/script/classic_pending_script.h
@@ -11,7 +11,7 @@
 #include "third_party/blink/renderer/core/script/classic_script.h"
 #include "third_party/blink/renderer/core/script/pending_script.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_parameters.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 
 namespace blink {
 
@@ -25,7 +25,7 @@
 // guarantee that the data buffer will not be purged.
 class CORE_EXPORT ClassicPendingScript final : public PendingScript,
                                                public ResourceClient,
-                                               public MemoryCoordinatorClient {
+                                               public MemoryPressureListener {
   USING_GARBAGE_COLLECTED_MIXIN(ClassicPendingScript);
 
  public:
@@ -105,7 +105,7 @@
       bool can_use_streamer,
       ScriptStreamer::NotStreamingReason reason);
 
-  // MemoryCoordinatorClient
+  // MemoryPressureListener
   void OnPurgeMemory() override;
 
   const ScriptFetchOptions options_;
diff --git a/third_party/blink/renderer/core/streams/miscellaneous_operations.cc b/third_party/blink/renderer/core/streams/miscellaneous_operations.cc
index 6b8065d..3db17ae 100644
--- a/third_party/blink/renderer/core/streams/miscellaneous_operations.cc
+++ b/third_party/blink/renderer/core/streams/miscellaneous_operations.cc
@@ -225,6 +225,48 @@
   TraceWrapperV8Reference<v8::Value> extra_arg_;
 };
 
+class JavaScriptStreamStartAlgorithm : public StreamStartAlgorithm {
+ public:
+  JavaScriptStreamStartAlgorithm(v8::Isolate* isolate,
+                                 v8::Local<v8::Object> recv,
+                                 const char* method_name_for_error,
+                                 v8::Local<v8::Value> controller)
+      : recv_(isolate, recv),
+        method_name_for_error_(method_name_for_error),
+        controller_(isolate, controller) {}
+
+  v8::MaybeLocal<v8::Promise> Run(ScriptState* script_state,
+                                  ExceptionState& exception_state) override {
+    auto* isolate = script_state->GetIsolate();
+    // https://streams.spec.whatwg.org/#set-up-writable-stream-default-controller-from-underlying-sink
+    // 3. Let startAlgorithm be the following steps:
+    //    a. Return ? InvokeOrNoop(underlyingSink, "start", « controller »).
+    auto value_maybe = CallOrNoop1(
+        script_state, recv_.NewLocal(isolate), "start", method_name_for_error_,
+        controller_.NewLocal(isolate), exception_state);
+    if (exception_state.HadException()) {
+      return v8::MaybeLocal<v8::Promise>();
+    }
+    v8::Local<v8::Value> value;
+    if (!value_maybe.ToLocal(&value)) {
+      exception_state.ThrowTypeError("internal error");
+      return v8::MaybeLocal<v8::Promise>();
+    }
+    return PromiseResolve(script_state, value);
+  }
+
+  void Trace(Visitor* visitor) override {
+    visitor->Trace(recv_);
+    visitor->Trace(controller_);
+    StreamStartAlgorithm::Trace(visitor);
+  }
+
+ private:
+  TraceWrapperV8Reference<v8::Object> recv_;
+  const char* const method_name_for_error_;
+  TraceWrapperV8Reference<v8::Value> controller_;
+};
+
 }  // namespace
 
 // TODO(ricea): For optimal performance, method_name should be cached as an
@@ -271,6 +313,16 @@
       isolate, method.As<v8::Function>(), extra_arg_local, underlying_object);
 }
 
+CORE_EXPORT StreamStartAlgorithm* CreateStartAlgorithm(
+    ScriptState* script_state,
+    v8::Local<v8::Object> underlying_object,
+    const char* method_name_for_error,
+    v8::Local<v8::Value> controller) {
+  return MakeGarbageCollected<JavaScriptStreamStartAlgorithm>(
+      script_state->GetIsolate(), underlying_object, method_name_for_error,
+      controller);
+}
+
 CORE_EXPORT v8::MaybeLocal<v8::Value> CallOrNoop1(
     ScriptState* script_state,
     v8::Local<v8::Object> object,
diff --git a/third_party/blink/renderer/core/streams/miscellaneous_operations.h b/third_party/blink/renderer/core/streams/miscellaneous_operations.h
index 3c1458b2..3e0c42d 100644
--- a/third_party/blink/renderer/core/streams/miscellaneous_operations.h
+++ b/third_party/blink/renderer/core/streams/miscellaneous_operations.h
@@ -17,6 +17,7 @@
 class ScriptState;
 class StrategySizeAlgorithm;
 class StreamAlgorithm;
+class StreamStartAlgorithm;
 
 // This is slightly different than the version in the standard
 // https://streams.spec.whatwg.org/#create-algorithm-from-underlying-method as
@@ -34,6 +35,17 @@
     v8::MaybeLocal<v8::Value> extra_arg,
     ExceptionState&);
 
+// Create a StreamStartAlgorithm from the "start" method on |underlying_object|.
+// Unlike other algorithms, the lookup of the method on the object is done at
+// execution time rather than algorithm creation time. |method_name_for_error|
+// is used in exception messages. It is not copied so must remain valid until
+// the algorithm is run.
+CORE_EXPORT StreamStartAlgorithm* CreateStartAlgorithm(
+    ScriptState*,
+    v8::Local<v8::Object> underlying_object,
+    const char* method_name_for_error,
+    v8::Local<v8::Value> controller);
+
 // Used in place of InvokeOrNoop in spec. Always takes 1 argument.
 // https://streams.spec.whatwg.org/#invoke-or-noop
 CORE_EXPORT v8::MaybeLocal<v8::Value> CallOrNoop1(ScriptState*,
diff --git a/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc b/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc
index 85c688e3..aaf5101 100644
--- a/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc
+++ b/third_party/blink/renderer/core/streams/miscellaneous_operations_test.cc
@@ -169,6 +169,86 @@
       extra_arg, 1, argv));
 }
 
+TEST(MiscellaneousOperationsTest, CreateStartAlgorithmNoMethod) {
+  V8TestingScope scope;
+  auto underlying_object = v8::Object::New(scope.GetIsolate());
+  v8::Local<v8::Value> controller = v8::Undefined(scope.GetIsolate());
+  auto* algo = CreateStartAlgorithm(scope.GetScriptState(), underlying_object,
+                                    "underlyingSink.start", controller);
+  ASSERT_TRUE(algo);
+  auto maybe_result = algo->Run(scope.GetScriptState(), ASSERT_NO_EXCEPTION);
+  ASSERT_FALSE(maybe_result.IsEmpty());
+  auto result = maybe_result.ToLocalChecked();
+  ASSERT_EQ(result->State(), v8::Promise::kFulfilled);
+  EXPECT_TRUE(result->Result()->IsUndefined());
+}
+
+TEST(MiscellaneousOperationsTest, CreateStartAlgorithmNullMethod) {
+  V8TestingScope scope;
+  auto underlying_object = v8::Object::New(scope.GetIsolate());
+  underlying_object
+      ->Set(scope.GetContext(), V8String(scope.GetIsolate(), "start"),
+            v8::Null(scope.GetIsolate()))
+      .Check();
+  v8::Local<v8::Value> controller = v8::Undefined(scope.GetIsolate());
+  auto* algo = CreateStartAlgorithm(scope.GetScriptState(), underlying_object,
+                                    "underlyingSink.start", controller);
+  ASSERT_TRUE(algo);
+  ExceptionState exception_state(scope.GetIsolate(),
+                                 ExceptionState::kExecutionContext, "", "");
+  auto maybe_result = algo->Run(scope.GetScriptState(), exception_state);
+  EXPECT_TRUE(exception_state.HadException());
+  EXPECT_TRUE(maybe_result.IsEmpty());
+}
+
+TEST(MiscellaneousOperationsTest, CreateStartAlgorithmThrowingMethod) {
+  V8TestingScope scope;
+  ScriptValue underlying_value = EvalWithPrintingError(&scope,
+                                                       R"(({
+  start() {
+    throw new Error();
+  }
+}))");
+  ASSERT_TRUE(underlying_value.IsObject());
+  auto underlying_object = underlying_value.V8Value().As<v8::Object>();
+  v8::Local<v8::Value> controller = v8::Undefined(scope.GetIsolate());
+  auto* algo = CreateStartAlgorithm(scope.GetScriptState(), underlying_object,
+                                    "underlyingSink.start", controller);
+  ASSERT_TRUE(algo);
+  ExceptionState exception_state(scope.GetIsolate(),
+                                 ExceptionState::kExecutionContext, "", "");
+  auto maybe_result = algo->Run(scope.GetScriptState(), exception_state);
+  EXPECT_TRUE(exception_state.HadException());
+  EXPECT_TRUE(maybe_result.IsEmpty());
+}
+
+TEST(MiscellaneousOperationsTest, CreateStartAlgorithmReturningController) {
+  V8TestingScope scope;
+  ScriptValue underlying_value = EvalWithPrintingError(&scope,
+                                                       R"(({
+  start(controller) {
+    return controller;
+  }
+}))");
+  ASSERT_TRUE(underlying_value.IsObject());
+  auto underlying_object = underlying_value.V8Value().As<v8::Object>();
+  // In a real stream, |controller| is never a promise, but nothing in
+  // CreateStartAlgorithm() requires this. By making it a promise, we can verify
+  // that a promise returned from start is passed through as-is.
+  v8::Local<v8::Value> controller =
+      v8::Promise::Resolver::New(scope.GetContext())
+          .ToLocalChecked()
+          ->GetPromise();
+  auto* algo = CreateStartAlgorithm(scope.GetScriptState(), underlying_object,
+                                    "underlyingSink.start", controller);
+  ASSERT_TRUE(algo);
+  auto maybe_result = algo->Run(scope.GetScriptState(), ASSERT_NO_EXCEPTION);
+  EXPECT_FALSE(maybe_result.IsEmpty());
+  v8::Local<v8::Value> result = maybe_result.ToLocalChecked();
+  ASSERT_TRUE(result->IsPromise());
+  ASSERT_EQ(result, controller);
+}
+
 TEST(MiscellaneousOperationsTest, CallOrNoop1NoMethod) {
   V8TestingScope scope;
   auto underlying_object = v8::Object::New(scope.GetIsolate());
diff --git a/third_party/blink/renderer/core/streams/readable_stream.cc b/third_party/blink/renderer/core/streams/readable_stream.cc
index 1d4e81ad2..c3bdd33 100644
--- a/third_party/blink/renderer/core/streams/readable_stream.cc
+++ b/third_party/blink/renderer/core/streams/readable_stream.cc
@@ -535,7 +535,9 @@
   if (reader.IsEmpty())
     return;
 
-  ReadableStreamOperations::DefaultReaderRead(script_state, reader);
+  ScriptPromise promise =
+      ReadableStreamOperations::DefaultReaderRead(script_state, reader);
+  promise.MarkAsHandled();
 }
 
 void ReadableStream::Serialize(ScriptState* script_state,
diff --git a/third_party/blink/renderer/core/streams/stream_algorithms.h b/third_party/blink/renderer/core/streams/stream_algorithms.h
index c4f2dd78..242a310 100644
--- a/third_party/blink/renderer/core/streams/stream_algorithms.h
+++ b/third_party/blink/renderer/core/streams/stream_algorithms.h
@@ -34,10 +34,24 @@
   virtual void Trace(Visitor*) {}
 };
 
+// Base class for start algorithms, ie. those that are derived from the start()
+// method of the underlying object. These differ from other underlying
+// algorithms in that they can throw synchronously. Objects of this
+// type must always be reachable by V8's garbage collector.
+class StreamStartAlgorithm
+    : public GarbageCollectedFinalized<StreamStartAlgorithm> {
+ public:
+  virtual ~StreamStartAlgorithm() = default;
+
+  virtual v8::MaybeLocal<v8::Promise> Run(ScriptState*, ExceptionState&) = 0;
+
+  virtual void Trace(Visitor*) {}
+};
+
 // Base class for algorithms which take one or more arguments and return a
 // Promise. This is used as the type for all the algorithms in the standard that
-// do not use StrategySizeAlgorithm. Objects of this type must always be
-// reachable by V8's garbage collector.
+// do not use StrategySizeAlgorithm or StreamStartAlgorithm. Objects of this
+// type must always be reachable by V8's garbage collector.
 class StreamAlgorithm : public GarbageCollectedFinalized<StreamAlgorithm> {
  public:
   virtual ~StreamAlgorithm() = default;
diff --git a/third_party/blink/renderer/core/streams/writable_stream_wrapper.cc b/third_party/blink/renderer/core/streams/writable_stream_wrapper.cc
index e8c7bd1..27e93d1 100644
--- a/third_party/blink/renderer/core/streams/writable_stream_wrapper.cc
+++ b/third_party/blink/renderer/core/streams/writable_stream_wrapper.cc
@@ -131,6 +131,7 @@
   if (locked(script_state, exception_state) &&
       !exception_state.HadException()) {
     exception_state.ThrowTypeError("Cannot abort a locked stream");
+    return ScriptPromise();
   }
 
   v8::Local<v8::Value> args[] = {
diff --git a/third_party/blink/renderer/core/testing/internals.cc b/third_party/blink/renderer/core/testing/internals.cc
index 2b0889e..8f71b974 100644
--- a/third_party/blink/renderer/core/testing/internals.cc
+++ b/third_party/blink/renderer/core/testing/internals.cc
@@ -3508,11 +3508,12 @@
 }
 
 void Internals::setIsLowEndDevice(bool is_low_end_device) {
-  MemoryCoordinator::SetIsLowEndDeviceForTesting(is_low_end_device);
+  MemoryPressureListenerRegistry::SetIsLowEndDeviceForTesting(
+      is_low_end_device);
 }
 
 bool Internals::isLowEndDevice() const {
-  return MemoryCoordinator::IsLowEndDevice();
+  return MemoryPressureListenerRegistry::IsLowEndDevice();
 }
 
 Vector<String> Internals::supportedTextEncodingLabels() const {
diff --git a/third_party/blink/renderer/core/workers/global_scope_creation_params.cc b/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
index 2d76ba6..353507a 100644
--- a/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
+++ b/third_party/blink/renderer/core/workers/global_scope_creation_params.cc
@@ -63,7 +63,8 @@
       if (this->off_main_thread_fetch_option ==
           OffMainThreadWorkerScriptFetchOption::kEnabled) {
         DCHECK(base::FeatureList::IsEnabled(
-            features::kOffMainThreadDedicatedWorkerScriptFetch));
+                   features::kOffMainThreadDedicatedWorkerScriptFetch) ||
+               features::IsOffMainThreadSharedWorkerScriptFetchEnabled());
       }
       break;
     case mojom::ScriptType::kModule:
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 d461ae7e..752c877 100644
--- a/third_party/blink/renderer/core/workers/worker_global_scope.cc
+++ b/third_party/blink/renderer/core/workers/worker_global_scope.cc
@@ -357,13 +357,16 @@
     FetchClientSettingsObjectSnapshot* outside_settings_object,
     const v8_inspector::V8StackTraceId& stack_id) {
   DCHECK(base::FeatureList::IsEnabled(
-      features::kOffMainThreadDedicatedWorkerScriptFetch));
+             features::kOffMainThreadDedicatedWorkerScriptFetch) ||
+         features::IsOffMainThreadSharedWorkerScriptFetchEnabled());
   DCHECK(!IsContextPaused());
 
   // Step 12. "Fetch a classic worker script given url, outside settings,
   // destination, and inside settings."
   mojom::RequestContextType destination = GetDestinationForMainScript();
-  DCHECK_EQ(mojom::RequestContextType::WORKER, destination);
+  DCHECK(destination == mojom::RequestContextType::WORKER ||
+         destination == mojom::RequestContextType::SHARED_WORKER)
+      << "A wrong destination (" << destination << ") is specified.";
 
   // Step 12.1. "Set request's reserved client to inside settings."
   // The browesr process takes care of this.
@@ -390,7 +393,8 @@
     WorkerClassicScriptLoader* classic_script_loader) {
   DCHECK(IsContextThread());
   DCHECK(base::FeatureList::IsEnabled(
-      features::kOffMainThreadDedicatedWorkerScriptFetch));
+             features::kOffMainThreadDedicatedWorkerScriptFetch) ||
+         features::IsOffMainThreadSharedWorkerScriptFetchEnabled());
   probe::didReceiveScriptResponse(this, classic_script_loader->Identifier());
 }
 
@@ -400,7 +404,8 @@
     const v8_inspector::V8StackTraceId& stack_id) {
   DCHECK(IsContextThread());
   DCHECK(base::FeatureList::IsEnabled(
-      features::kOffMainThreadDedicatedWorkerScriptFetch));
+             features::kOffMainThreadDedicatedWorkerScriptFetch) ||
+         features::IsOffMainThreadSharedWorkerScriptFetchEnabled());
 
   // Step 12. "If the algorithm asynchronously completes with null, then:"
   if (classic_script_loader->Failed()) {
@@ -430,11 +435,11 @@
     SetReferrerPolicy(referrer_policy);
   }
 
-  // Step 13.6. "Execute the Initialize a global object's CSP list algorithm
+  // Step 12.6. "Execute the Initialize a global object's CSP list algorithm
   // on worker global scope and response. [CSP]"
   // This is done in the constructor of WorkerGlobalScope.
 
-  // Step 13.7. "Asynchronously complete the perform the fetch steps with
+  // Step 12.7. "Asynchronously complete the perform the fetch steps with
   // response."
   EvaluateClassicScript(
       classic_script_loader->ResponseURL(), classic_script_loader->SourceText(),
diff --git a/third_party/blink/renderer/core/workers/worker_thread.h b/third_party/blink/renderer/core/workers/worker_thread.h
index da78755..8b1a8f8 100644
--- a/third_party/blink/renderer/core/workers/worker_thread.h
+++ b/third_party/blink/renderer/core/workers/worker_thread.h
@@ -113,14 +113,14 @@
                              const v8_inspector::V8StackTraceId& stack_id);
 
   // Posts a task to import a top-level classic script on the worker thread.
-  // Called on the main thread after start().
+  // Called on the main thread after Start().
   void ImportClassicScript(
       const KURL& script_url,
       FetchClientSettingsObjectSnapshot* outside_settings_object,
       const v8_inspector::V8StackTraceId& stack_id);
 
   // Posts a task to import a top-level module script on the worker thread.
-  // Called on the main thread after start().
+  // Called on the main thread after Start().
   void ImportModuleScript(
       const KURL& script_url,
       FetchClientSettingsObjectSnapshot* outside_settings_object,
diff --git a/third_party/blink/renderer/core/xml/xml_serializer.cc b/third_party/blink/renderer/core/xml/xml_serializer.cc
index a0aef9b1a..a866fc0 100644
--- a/third_party/blink/renderer/core/xml/xml_serializer.cc
+++ b/third_party/blink/renderer/core/xml/xml_serializer.cc
@@ -30,7 +30,7 @@
   DCHECK(root);
   MarkupAccumulator accumulator(kDoNotResolveURLs,
                                 SerializationType::kForcedXML);
-  return SerializeNodes<EditingStrategy>(accumulator, *root, kIncludeNode);
+  return accumulator.SerializeNodes<EditingStrategy>(*root, kIncludeNode);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/devtools/BUILD.gn b/third_party/blink/renderer/devtools/BUILD.gn
index fa36800..e18e75c 100644
--- a/third_party/blink/renderer/devtools/BUILD.gn
+++ b/third_party/blink/renderer/devtools/BUILD.gn
@@ -401,6 +401,7 @@
   "front_end/mobile_throttling/throttlingSettingsTab.css",
   "front_end/mobile_throttling/ThrottlingSettingsTab.js",
   "front_end/ndb_app.json",
+  "front_end/network/binaryResourceView.css",
   "front_end/network/blockedURLsPane.css",
   "front_end/network/BinaryResourceView.js",
   "front_end/network/BlockedURLsPane.js",
diff --git a/third_party/blink/renderer/devtools/front_end/main/Main.js b/third_party/blink/renderer/devtools/front_end/main/Main.js
index b3c5efbb..ea1a21dd 100644
--- a/third_party/blink/renderer/devtools/front_end/main/Main.js
+++ b/third_party/blink/renderer/devtools/front_end/main/Main.js
@@ -106,7 +106,6 @@
   _initializeExperiments() {
     // Keep this sorted alphabetically: both keys and values.
     Runtime.experiments.register('applyCustomStylesheet', 'Allow custom UI themes');
-    Runtime.experiments.register('binaryWebsocketViewer', 'Binary WebSocket Message Viewer');
     Runtime.experiments.register('blackboxJSFramesOnTimeline', 'Blackbox JavaScript frames on Timeline', true);
     Runtime.experiments.register('emptySourceMapAutoStepping', 'Empty sourcemap auto-stepping');
     Runtime.experiments.register('inputEventsOnTimelineOverview', 'Input events on Timeline overview', true);
diff --git a/third_party/blink/renderer/devtools/front_end/network/BinaryResourceView.js b/third_party/blink/renderer/devtools/front_end/network/BinaryResourceView.js
index 0d4339d0..0e0f627d 100644
--- a/third_party/blink/renderer/devtools/front_end/network/BinaryResourceView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/BinaryResourceView.js
@@ -10,28 +10,167 @@
    */
   constructor(base64content, contentUrl, resourceType) {
     super();
+    this.registerRequiredCSS('network/binaryResourceView.css');
 
-    if (!base64content.length) {
+    /** @type {boolean} */
+    this._empty = !base64content.length;
+    if (this._empty) {
       new UI.EmptyWidget('No data present in selected item').show(this.element);
       return;
     }
 
-    const binaryResourceViewFactory =
+    this._binaryResourceViewFactory =
         new SourceFrame.BinaryResourceViewFactory(base64content, contentUrl, resourceType);
 
-    const tabbedPane = new UI.TabbedPane();
-    tabbedPane.appendTab(
-        'base64-tab-id', 'Base64', binaryResourceViewFactory.createBase64View(),
-        ls`View selected binary WebSocket message as a Base64 string`);
-    tabbedPane.appendTab(
-        'hex-tab-id', 'Hex', binaryResourceViewFactory.createHexView(),
-        ls`View selected binary WebSocket message as a hexadecimal string`);
-    tabbedPane.appendTab(
-        'hex-viewer-tab-id', 'Hex Viewer', binaryResourceViewFactory.createHexViewerView(),
-        ls`View selected binary WebSocket message in a hex viewer`);
-    tabbedPane.appendTab(
-        'utf8-id', 'UTF-8', binaryResourceViewFactory.createUtf8View(),
-        ls`View selected binary WebSocket message decoded as UTF-8`);
-    tabbedPane.show(this.element);
+    this._toolbar = new UI.Toolbar('binary-view-toolbar', this.element);
+
+    /** @type {!Array<!Network.BinaryResourceView.BinaryViewObject>} */
+    this._binaryViewObjects = [
+      new Network.BinaryResourceView.BinaryViewObject(
+          'base64', ls`Base64`, ls`Copied as Base64`,
+          this._binaryResourceViewFactory.createBase64View.bind(this._binaryResourceViewFactory),
+          this._binaryResourceViewFactory.base64.bind(this._binaryResourceViewFactory)),
+      new Network.BinaryResourceView.BinaryViewObject(
+          'hex', ls`Hex Viewer`, ls`Copied as Hex`,
+          this._binaryResourceViewFactory.createHexView.bind(this._binaryResourceViewFactory),
+          this._binaryResourceViewFactory.hex.bind(this._binaryResourceViewFactory)),
+      new Network.BinaryResourceView.BinaryViewObject(
+          'utf8', ls`UTF-8`, ls`Copied as UTF-8`,
+          this._binaryResourceViewFactory.createUtf8View.bind(this._binaryResourceViewFactory),
+          this._binaryResourceViewFactory.utf8.bind(this._binaryResourceViewFactory)),
+    ];
+    this._binaryViewTypeSetting = Common.settings.createSetting('binaryViewType', 'hex');
+    this._binaryViewTypeCombobox = new UI.ToolbarComboBox(this._binaryViewTypeChanged.bind(this));
+    for (const viewObject of this._binaryViewObjects) {
+      this._binaryViewTypeCombobox.addOption(
+          this._binaryViewTypeCombobox.createOption(viewObject.label, viewObject.label, viewObject.type));
+    }
+    this._toolbar.appendToolbarItem(this._binaryViewTypeCombobox);
+
+    const copyButton = new UI.ToolbarButton(ls`Copy to clipboard`, 'largeicon-copy');
+    copyButton.addEventListener(UI.ToolbarButton.Events.Click, this._copySelectedViewToClipboard.bind(this), this);
+    this._toolbar.appendToolbarItem(copyButton);
+
+    this._copiedText = new UI.ToolbarText();
+    this._copiedText.element.classList.add('binary-view-copied-text');
+    this._toolbar.element.appendChild(this._copiedText.element);
+
+    /** @type {?number} */
+    this._addFadeoutSettimeoutId = null;
+
+    /** @type {?UI.Widget} */
+    this._lastView = null;
+    this._updateView();
+  }
+
+  /**
+   * @return {?Network.BinaryResourceView.BinaryViewObject}
+   */
+  _getCurrentViewObject() {
+    const filter = obj => obj.type === this._binaryViewTypeSetting.get();
+    const binaryViewObject = this._binaryViewObjects.find(filter);
+    console.assert(
+        binaryViewObject,
+        `No binary view found for binary view type found in setting 'binaryViewType': ${
+            this._binaryViewTypeSetting.get()}`);
+    return binaryViewObject || null;
+  }
+
+  async _copySelectedViewToClipboard() {
+    const viewObject = this._getCurrentViewObject();
+    InspectorFrontendHost.copyText(await viewObject.content());
+    this._copiedText.setText(viewObject.copiedMessage);
+    this._copiedText.element.classList.remove('fadeout');
+    /**
+     * @this {!Network.BinaryResourceView}
+     */
+    function addFadeoutClass() {
+      this._copiedText.element.classList.add('fadeout');
+    }
+    if (this._addFadeoutSettimeoutId) {
+      clearTimeout(this._addFadeoutSettimeoutId);
+      this._addFadeoutSettimeoutId = null;
+    }
+    this._addFadeoutSettimeoutId = setTimeout(addFadeoutClass.bind(this), 2000);
+  }
+
+  /**
+   * @override
+   */
+  wasShown() {
+    if (!this._empty)
+      this._updateView();
+  }
+
+  _updateView() {
+    const newViewObject = this._getCurrentViewObject();
+    if (!newViewObject)
+      return;
+
+    const newView = newViewObject.getView();
+    if (newView === this._lastView)
+      return;
+
+    if (this._lastView)
+      this._lastView.detach();
+    this._lastView = newView;
+
+    newView.show(this.element, this._toolbar.element);
+    this._binaryViewTypeCombobox.selectElement().value = this._binaryViewTypeSetting.get();
+  }
+
+  _binaryViewTypeChanged() {
+    const newViewType = this._binaryViewTypeCombobox.selectedOption().value;
+    if (this._binaryViewTypeSetting.get() === newViewType)
+      return;
+    this._binaryViewTypeSetting.set(newViewType);
+    this._updateView();
+  }
+
+  /**
+   * @param {!UI.ContextMenu} contextMenu
+   * @param {string} submenuItemText
+   */
+  addCopyToContextMenu(contextMenu, submenuItemText) {
+    if (this._empty)
+      return;
+    const copyMenu = contextMenu.clipboardSection().appendSubMenuItem(submenuItemText);
+    const footerSection = copyMenu.footerSection();
+
+    footerSection.appendItem(
+        ls`Copy as Base64`, async () => InspectorFrontendHost.copyText(await this._binaryResourceViewFactory.base64()));
+    footerSection.appendItem(
+        ls`Copy as Hex`, async () => InspectorFrontendHost.copyText(await this._binaryResourceViewFactory.hex()));
+    footerSection.appendItem(
+        ls`Copy as UTF-8`, async () => InspectorFrontendHost.copyText(await this._binaryResourceViewFactory.utf8()));
+  }
+};
+
+Network.BinaryResourceView.BinaryViewObject = class {
+  /**
+   * @param {string} type
+   * @param {string} label
+   * @param {string} copiedMessage
+   * @param {function():!UI.Widget} createViewFn
+   * @param {function():Promise<string>} content
+   */
+  constructor(type, label, copiedMessage, createViewFn, content) {
+    this.type = type;
+    this.label = label;
+    this.copiedMessage = copiedMessage;
+    this.content = content;
+    this._createViewFn = createViewFn;
+
+    /** @type {?UI.Widget} */
+    this._view = null;
+  }
+
+  /**
+   * @return {!UI.Widget}
+   */
+  getView() {
+    if (!this._view)
+      this._view = this._createViewFn();
+    return this._view;
   }
 };
diff --git a/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js b/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js
index e431016..4b42276 100644
--- a/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js
+++ b/third_party/blink/renderer/devtools/front_end/network/ResourceWebSocketFrameView.js
@@ -92,12 +92,19 @@
 
     /**
      * @param {!UI.ContextMenu} contextMenu
-     * @param {!DataGrid.DataGridNode} node
+     * @param {!DataGrid.DataGridNode} genericNode
      * @this {Network.ResourceWebSocketFrameView}
      */
-    function onRowContextMenu(contextMenu, node) {
-      contextMenu.clipboardSection().appendItem(
-          Common.UIString('Copy message'), InspectorFrontendHost.copyText.bind(InspectorFrontendHost, node.data.data));
+    function onRowContextMenu(contextMenu, genericNode) {
+      const node = /** @type {!Network.ResourceWebSocketFrameNode} */ (genericNode);
+      const binaryView = node.binaryView();
+      if (binaryView) {
+        binaryView.addCopyToContextMenu(contextMenu, ls`Copy message...`);
+      } else {
+        contextMenu.clipboardSection().appendItem(
+            Common.UIString('Copy message'),
+            InspectorFrontendHost.copyText.bind(InspectorFrontendHost, node.data.data));
+      }
       contextMenu.footerSection().appendItem(Common.UIString('Clear all'), this._clearFrames.bind(this));
     }
   }
@@ -169,12 +176,10 @@
     this._currentSelectedNode = /** @type {!Network.ResourceWebSocketFrameNode} */ (event.data);
     const content = this._currentSelectedNode.dataText();
 
-    if (Runtime.experiments.isEnabled('binaryWebsocketViewer')) {
-      if (this._currentSelectedNode.opCode() === Network.ResourceWebSocketFrameView.OpCodes.BinaryFrame) {
-        this._splitWidget.setSidebarWidget(
-            new Network.BinaryResourceView(content, this._request.url(), Common.resourceTypes.WebSocket));
-        return;
-      }
+    const binaryView = this._currentSelectedNode.binaryView();
+    if (binaryView) {
+      this._splitWidget.setSidebarWidget(binaryView);
+      return;
     }
 
     const jsonView = await SourceFrame.JSONView.createView(content);
@@ -265,10 +270,8 @@
     if (isTextFrame) {
       description = dataText;
 
-    } else if (
-        Runtime.experiments.isEnabled('binaryWebsocketViewer') &&
-        frame.opCode === Network.ResourceWebSocketFrameView.OpCodes.BinaryFrame) {
-      length = base64ToSize(frame.text) + ' bytes';
+    } else if (frame.opCode === Network.ResourceWebSocketFrameView.OpCodes.BinaryFrame) {
+      length = Number.bytesToString(base64ToSize(frame.text));
       description = 'Binary Message';
 
     } else {
@@ -319,6 +322,18 @@
   opCode() {
     return /** @type {!Network.ResourceWebSocketFrameView.OpCodes} */ (this._frame.opCode);
   }
+
+  /**
+   * @return {?Network.BinaryResourceView}
+   */
+  binaryView() {
+    if (this._isTextFrame)
+      return null;
+
+    if (!this._binaryView)
+      this._binaryView = new Network.BinaryResourceView(this._dataText, /* url */ '', Common.resourceTypes.WebSocket);
+    return this._binaryView;
+  }
 };
 
 /**
diff --git a/third_party/blink/renderer/devtools/front_end/network/binaryResourceView.css b/third_party/blink/renderer/devtools/front_end/network/binaryResourceView.css
new file mode 100644
index 0000000..12594b7
--- /dev/null
+++ b/third_party/blink/renderer/devtools/front_end/network/binaryResourceView.css
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019 The Chromium Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+.panel.network .toolbar.binary-view-toolbar {
+  border-top: 1px solid #ccc;
+  border-bottom: 0px;
+  padding-left: 5px;
+  background-color: #eee;
+}
+
+.binary-view-copied-text {
+  opacity: 1;
+}
+
+.binary-view-copied-text.fadeout {
+  opacity: 0;
+  transition: opacity 1s;
+}
diff --git a/third_party/blink/renderer/devtools/front_end/network/module.json b/third_party/blink/renderer/devtools/front_end/network/module.json
index 8dfcc3d..bf275b4 100644
--- a/third_party/blink/renderer/devtools/front_end/network/module.json
+++ b/third_party/blink/renderer/devtools/front_end/network/module.json
@@ -208,6 +208,7 @@
         "NetworkPanel.js"
     ],
     "resources": [
+        "binaryResourceView.css",
         "blockedURLsPane.css",
         "eventSourceMessagesView.css",
         "networkConfigView.css",
diff --git a/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js b/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
index 6790512..ec3bb18 100644
--- a/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
+++ b/third_party/blink/renderer/devtools/front_end/sdk/NetworkManager.js
@@ -841,8 +841,16 @@
   _finishNetworkRequest(networkRequest, finishTime, encodedDataLength, shouldReportCorbBlocking) {
     networkRequest.endTime = finishTime;
     networkRequest.finished = true;
-    if (encodedDataLength >= 0)
-      networkRequest.setTransferSize(encodedDataLength);
+    if (encodedDataLength >= 0) {
+      const redirectSource = networkRequest.redirectSource();
+      if (redirectSource && redirectSource.signedExchangeInfo()) {
+        networkRequest.setTransferSize(0);
+        redirectSource.setTransferSize(encodedDataLength);
+        this._updateNetworkRequest(redirectSource);
+      } else {
+        networkRequest.setTransferSize(encodedDataLength);
+      }
+    }
     this._manager.dispatchEventToListeners(SDK.NetworkManager.Events.RequestFinished, networkRequest);
     delete this._inflightRequestsById[networkRequest.requestId()];
     delete this._inflightRequestsByURL[networkRequest.url()];
diff --git a/third_party/blink/renderer/devtools/front_end/source_frame/BinaryResourceViewFactory.js b/third_party/blink/renderer/devtools/front_end/source_frame/BinaryResourceViewFactory.js
index 5fd9503..855bc2ad 100644
--- a/third_party/blink/renderer/devtools/front_end/source_frame/BinaryResourceViewFactory.js
+++ b/third_party/blink/renderer/devtools/front_end/source_frame/BinaryResourceViewFactory.js
@@ -14,6 +14,10 @@
     this._resourceType = resourceType;
     /** @type {?Promise<!Uint8Array>} */
     this._arrayPromise = null;
+    /** @type {?Promise<string>} */
+    this._hexPromise = null;
+    /** @type {?Promise<string>} */
+    this._utf8Promise = null;
   }
 
   async _fetchContentAsArray() {
@@ -27,6 +31,43 @@
   }
 
   /**
+   * @return {!Promise<string>}
+   */
+  async hex() {
+    if (!this._hexPromise) {
+      this._hexPromise = new Promise(async resolve => {
+        const content = await this._fetchContentAsArray();
+        const hexString = SourceFrame.BinaryResourceViewFactory.uint8ArrayToHexString(content);
+        resolve(hexString);
+      });
+    }
+
+    return this._hexPromise;
+  }
+
+  /**
+   * @return {!Promise<string>}
+   */
+  async base64() {
+    return this._base64content;
+  }
+
+  /**
+   * @return {!Promise<string>}
+   */
+  async utf8() {
+    if (!this._utf8Promise) {
+      this._utf8Promise = new Promise(async resolve => {
+        const content = await this._fetchContentAsArray();
+        const utf8String = new TextDecoder('utf8').decode(content);
+        resolve(utf8String);
+      });
+    }
+
+    return this._utf8Promise;
+  }
+
+  /**
    * @return {!SourceFrame.ResourceSourceFrame}
    */
   createBase64View() {
@@ -39,18 +80,6 @@
    * @return {!SourceFrame.ResourceSourceFrame}
    */
   createHexView() {
-    const hexContentProvider = new Common.StaticContentProvider(
-        this._contentUrl, this._resourceType,
-        async () => SourceFrame.BinaryResourceViewFactory.uint8ArrayToHexString(await this._fetchContentAsArray()));
-    return new SourceFrame.ResourceSourceFrame(
-        hexContentProvider,
-        /* autoPrettyPrint */ false, {lineNumbers: false, lineWrapping: true});
-  }
-
-  /**
-   * @return {!SourceFrame.ResourceSourceFrame}
-   */
-  createHexViewerView() {
     const hexViewerContentProvider = new Common.StaticContentProvider(
         this._contentUrl, this._resourceType,
         async () => SourceFrame.BinaryResourceViewFactory.uint8ArrayToHexViewer(await this._fetchContentAsArray()));
@@ -63,9 +92,8 @@
    * @return {!SourceFrame.ResourceSourceFrame}
    */
   createUtf8View() {
-    const utf8ContentProvider = new Common.StaticContentProvider(
-        this._contentUrl, this._resourceType,
-        async () => new TextDecoder('utf8').decode(await this._fetchContentAsArray()));
+    const utf8fn = /** @type {function():!Promise<?string>} */ (this.utf8.bind(this));
+    const utf8ContentProvider = new Common.StaticContentProvider(this._contentUrl, this._resourceType, utf8fn);
     return new SourceFrame.ResourceSourceFrame(
         utf8ContentProvider,
         /* autoPrettyPrint */ false, {lineNumbers: true, lineWrapping: true});
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 65809eba..4d61d98 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
@@ -716,7 +716,6 @@
       case recordTypes.LayoutInvalidationTracking:
       case recordTypes.LayerInvalidationTracking:
       case recordTypes.PaintInvalidationTracking:
-      case recordTypes.ScrollInvalidationTracking:
         this._invalidationTracker.addInvalidation(new TimelineModel.InvalidationTrackingEvent(event));
         break;
 
@@ -1196,7 +1195,6 @@
   LayoutInvalidationTracking: 'LayoutInvalidationTracking',
   LayerInvalidationTracking: 'LayerInvalidationTracking',
   PaintInvalidationTracking: 'PaintInvalidationTracking',
-  ScrollInvalidationTracking: 'ScrollInvalidationTracking',
 
   ParseHTML: 'ParseHTML',
   ParseAuthorStyleSheet: 'ParseAuthorStyleSheet',
@@ -1809,8 +1807,7 @@
     const types = [
       TimelineModel.TimelineModel.RecordType.StyleRecalcInvalidationTracking,
       TimelineModel.TimelineModel.RecordType.LayoutInvalidationTracking,
-      TimelineModel.TimelineModel.RecordType.PaintInvalidationTracking,
-      TimelineModel.TimelineModel.RecordType.ScrollInvalidationTracking
+      TimelineModel.TimelineModel.RecordType.PaintInvalidationTracking
     ];
     for (const invalidation of this._invalidationsOfTypes(types)) {
       if (invalidation.paintId === effectivePaintId)
diff --git a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
index 63a7b889..3bad59ac 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_layout_object.cc
@@ -230,9 +230,14 @@
   if (layout_object_->IsSVGRoot())
     return ax::mojom::Role::kSvgRoot;
 
-  // Table sections should be ignored.
-  if (layout_object_->IsTableSection())
+  // Table sections should be ignored if there is no reason not to, e.g.
+  // ARIA 1.2 (and Core-AAM 1.1) state that elements which are focusable
+  // and not hidden must be included in the accessibility tree.
+  if (layout_object_->IsTableSection()) {
+    if (CanSetFocusAttribute())
+      return ax::mojom::Role::kGroup;
     return ax::mojom::Role::kIgnored;
+  }
 
   if (layout_object_->IsHR())
     return ax::mojom::Role::kSplitter;
@@ -3193,6 +3198,9 @@
   if (!parent)
     return ax::mojom::Role::kGenericContainer;
 
+  if (parent->GetLayoutObject()->IsTableSection())
+    parent = parent->ParentObjectUnignored();
+
   if (parent->RoleValue() == ax::mojom::Role::kLayoutTable)
     return ax::mojom::Role::kLayoutTableRow;
 
@@ -3210,6 +3218,9 @@
     return ax::mojom::Role::kGenericContainer;
 
   AXObject* grandparent = parent->ParentObjectUnignored();
+  if (grandparent && grandparent->GetLayoutObject()->IsTableSection())
+    grandparent = grandparent->ParentObjectUnignored();
+
   if (!grandparent || !grandparent->IsTableLikeRole())
     return ax::mojom::Role::kGenericContainer;
 
diff --git a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
index ff5f1056..bcda509 100644
--- a/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
+++ b/third_party/blink/renderer/modules/animationworklet/animation_worklet_proxy_client.cc
@@ -124,18 +124,23 @@
                           mutator_item.mutator_dispatcher,
                           WrapCrossThreadPersistent(this)));
     }
-
-    // At worklet scope termination break the reference cycle between
-    // AnimationWorkletGlobalScope and AnimationWorkletProxyClient.
-    global_scopes_.clear();
   }
-
-  mutator_items_.clear();
   state_ = RunState::kDisposed;
+
+  // At worklet scope termination break the reference cycle between
+  // AnimationWorkletGlobalScope and AnimationWorkletProxyClient.
+  global_scopes_.clear();
+  mutator_items_.clear();
 }
 
 std::unique_ptr<AnimationWorkletOutput> AnimationWorkletProxyClient::Mutate(
     std::unique_ptr<AnimationWorkletInput> input) {
+  std::unique_ptr<AnimationWorkletOutput> output =
+      std::make_unique<AnimationWorkletOutput>();
+
+  if (state_ == RunState::kDisposed)
+    return output;
+
   base::ElapsedTimer timer;
   DCHECK(input);
 #if DCHECK_IS_ON()
@@ -149,9 +154,6 @@
     global_scope->UpdateAnimatorsList(*input);
   }
 
-  std::unique_ptr<AnimationWorkletOutput> output =
-      std::make_unique<AnimationWorkletOutput>();
-
   // Assume animators are stateful.
   // TODO(https://crbug.com/914918): Implement filter for detecting stateless
   // and stateful animators. Call mutate on stateful and stateless global
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
index 966439c..10c8aa8 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.cc
@@ -16,6 +16,7 @@
 #include "third_party/blink/renderer/core/animation/worklet_animation_controller.h"
 #include "third_party/blink/renderer/core/dom/node.h"
 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
+#include "third_party/blink/renderer/core/frame/frame_console.h"
 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
 #include "third_party/blink/renderer/core/inspector/console_message.h"
 #include "third_party/blink/renderer/core/layout/layout_box.h"
@@ -132,6 +133,17 @@
   int group = 0;
   base::Optional<double> start_time = base::nullopt;
   double time_offset = 0;
+
+  // Normally the playback rate of a blink animation gets translated into
+  // equivalent playback rate of cc::KeyframeModels.
+  // This has worked for regular animations since their current time was not
+  // exposed in cc. However, for worklet animations this does not work because
+  // the current time is exposed and it is an animation level concept as
+  // opposed to a keyframe model level concept.
+  // So it makes sense here that we use "1" as playback rate for KeyframeModels
+  // and separately plumb the playback rate to cc worklet animation.
+  // TODO(majidvp): Remove playbackRate from KeyframeModel in favor of having
+  // it on animation. https://crbug.com/925373.
   double playback_rate = 1;
 
   effect->StartAnimationOnCompositor(group, start_time, time_offset,
@@ -229,6 +241,7 @@
       animator_name_(animator_name),
       play_state_(Animation::kIdle),
       last_play_state_(play_state_),
+      playback_rate_(1),
       document_(document),
       effects_(effects),
       timeline_(timeline),
@@ -332,6 +345,69 @@
   Update(kTimingUpdateOnDemand);
 }
 
+double WorkletAnimation::playbackRate(ScriptState* script_state) const {
+  return playback_rate_;
+}
+
+void WorkletAnimation::setPlaybackRate(ScriptState* script_state,
+                                       double playback_rate) {
+  if (playback_rate == playback_rate_)
+    return;
+
+  // TODO(https://crbug.com/821910): Implement 0 playback rate after pause()
+  // support is in.
+  if (!playback_rate) {
+    if (document_->GetFrame() && ExecutionContext::From(script_state)) {
+      document_->GetFrame()->Console().AddMessage(
+          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
+                                 "WorkletAnimation currently does not support "
+                                 "playback rate of Zero."));
+    }
+    return;
+  }
+
+  // TODO(gerchiko): Implement support playback_rate for scroll-linked
+  // animations. http://crbug.com/852475.
+  if (timeline_->IsScrollTimeline()) {
+    if (document_->GetFrame() && ExecutionContext::From(script_state)) {
+      document_->GetFrame()->Console().AddMessage(
+          ConsoleMessage::Create(kJSMessageSource, kWarningMessageLevel,
+                                 "Scroll-linked WorkletAnimation currently "
+                                 "does not support setting playback rate."));
+    }
+    return;
+  }
+  SetPlaybackRateInternal(playback_rate);
+}
+
+void WorkletAnimation::SetPlaybackRateInternal(double playback_rate) {
+  DCHECK(std::isfinite(playback_rate));
+  DCHECK_NE(playback_rate, playback_rate_);
+  DCHECK(playback_rate);
+  DCHECK(!timeline_->IsScrollTimeline());
+
+  if (start_time_) {
+    base::Optional<base::TimeDelta> current_time = CurrentTime();
+    // TODO(gerchiko): support unresolved current time.
+    // Blocked by ability to change timeline.
+    if (current_time) {
+      // Update startTime in order to maintain previous currentTime and, as a
+      // result, prevent the animation from jumping.
+      // See currentTime calculation in CurrentTime().
+      bool is_null;
+      double timeline_time_ms = timeline_->currentTime(is_null);
+      DCHECK(!is_null);
+
+      start_time_ = base::TimeDelta::FromMillisecondsD(timeline_time_ms) -
+                    current_time.value() / playback_rate;
+    }
+  }
+  playback_rate_ = playback_rate;
+
+  if (Playing())
+    document_->GetWorkletAnimationController().InvalidateAnimation(*this);
+}
+
 void WorkletAnimation::EffectInvalidated() {
   InvalidateCompositingState();
 }
@@ -445,7 +521,7 @@
     // update the compositor to have the correct orientation and start/end
     // offset information.
     compositor_animation_ = CompositorAnimation::CreateWorkletAnimation(
-        id_, animator_name_,
+        id_, animator_name_, playback_rate_,
         scroll_timeline_util::ToCompositorScrollTimeline(timeline_),
         std::move(options_));
     compositor_animation_->SetAnimationDelegate(this);
@@ -506,6 +582,7 @@
         scroll_timeline_util::GetCompositorScrollElementId(scroll_source),
         start_scroll_offset, end_scroll_offset);
   }
+  compositor_animation_->UpdatePlaybackRate(playback_rate_);
 }
 
 void WorkletAnimation::DestroyCompositorAnimation() {
@@ -551,7 +628,7 @@
   if (timeline_->IsScrollTimeline())
     return timeline_time;
   DCHECK(start_time_);
-  return timeline_time - start_time_.value();
+  return (timeline_time - start_time_.value()) * playback_rate_;
 }
 
 bool WorkletAnimation::NeedsPeek(base::TimeDelta current_time) {
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
index 4cf8ccb..6527d08c 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.h
@@ -73,6 +73,8 @@
   AnimationTimeline* timeline() { return timeline_; }
   String playState();
   double currentTime(bool& is_null);
+  double playbackRate(ScriptState* script_state) const;
+  void setPlaybackRate(ScriptState* script_state, double playback_rate);
   void play(ExceptionState& exception_state);
   void cancel();
 
@@ -142,6 +144,13 @@
   bool CheckCanStart(String* failure_message);
   void SetStartTimeToNow();
 
+  // For DocumentTimeline animations, adjusts start_time_ according to playback
+  // rate change to preserve current time and avoid the animation output from
+  // jumping.
+  // Setting playback rate is currently not supported for scroll-linked
+  // animations.
+  void SetPlaybackRateInternal(double);
+
   // Updates a running animation on the compositor side.
   void UpdateOnCompositor();
 
@@ -161,6 +170,9 @@
   const String animator_name_;
   Animation::AnimationPlayState play_state_;
   Animation::AnimationPlayState last_play_state_;
+  // Controls speed of the animation.
+  // https://drafts.csswg.org/web-animations-2/#animation-effect-playback-rate
+  double playback_rate_;
   base::Optional<base::TimeDelta> start_time_;
   Vector<base::Optional<base::TimeDelta>> local_times_;
   // We use this to skip updating if current time has not changed since last
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation.idl b/third_party/blink/renderer/modules/animationworklet/worklet_animation.idl
index 3f77238..951dbf9 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation.idl
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation.idl
@@ -19,6 +19,7 @@
   readonly attribute AnimationTimeline? timeline;
   readonly attribute AnimationPlayState playState;
   readonly attribute double? currentTime;
+  [CallWith=ScriptState] attribute double playbackRate;
   [RaisesException] void play();
   void cancel();
 };
diff --git a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
index 3d4f0e1..bb9ff5e 100644
--- a/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
+++ b/third_party/blink/renderer/modules/animationworklet/worklet_animation_test.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/animationworklet/worklet_animation.h"
 
+#include <memory>
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/bindings/core/v8/serialization/serialized_script_value.h"
 #include "third_party/blink/renderer/bindings/modules/v8/animation_effect_or_animation_effect_sequence.h"
@@ -256,4 +257,87 @@
   state.reset(new AnimationWorkletDispatcherInput);
 }
 
+// Verifies correctness of current time when playback rate is set while the
+// animation is in idle state.
+TEST_F(WorkletAnimationTest, DocumentTimelineSetPlaybackRate) {
+  GetDocument().GetAnimationClock().ResetTimeForTesting();
+  GetDocument().Timeline().ResetForTesting();
+  double error = base::TimeDelta::FromMicrosecondsD(1).InMillisecondsF();
+
+  WorkletAnimationId id = worklet_animation_->GetWorkletAnimationId();
+  base::TimeTicks first_ticks =
+      base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111.0);
+  base::TimeTicks second_ticks =
+      base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111.0 + 123.4);
+  double playback_rate = 2.0;
+
+  GetDocument().GetAnimationClock().ResetTimeForTesting(first_ticks);
+  DummyExceptionStateForTesting exception_state;
+  worklet_animation_->setPlaybackRate(nullptr, playback_rate);
+  worklet_animation_->play(exception_state);
+  worklet_animation_->UpdateCompositingState();
+
+  std::unique_ptr<AnimationWorkletDispatcherInput> state =
+      std::make_unique<AnimationWorkletDispatcherInput>();
+  worklet_animation_->UpdateInputState(state.get());
+
+  std::unique_ptr<AnimationWorkletInput> input =
+      state->TakeWorkletState(id.worklet_id);
+
+  // Zero current time is not impacted by playback rate.
+  EXPECT_NEAR(0, input->added_and_updated_animations[0].current_time, error);
+  state.reset(new AnimationWorkletDispatcherInput);
+
+  // Play the animation until second_ticks.
+  GetDocument().GetAnimationClock().ResetTimeForTesting(second_ticks);
+  worklet_animation_->UpdateInputState(state.get());
+  input = state->TakeWorkletState(id.worklet_id);
+
+  // Verify that the current time is updated playback_rate faster than the
+  // timeline time.
+  EXPECT_NEAR(123.4 * playback_rate, input->updated_animations[0].current_time,
+              error);
+}
+
+// Verifies correctness of current time when playback rate is set while the
+// animation is playing.
+TEST_F(WorkletAnimationTest, DocumentTimelineSetPlaybackRateWhilePlaying) {
+  GetDocument().GetAnimationClock().ResetTimeForTesting();
+  GetDocument().Timeline().ResetForTesting();
+  double error = base::TimeDelta::FromMicrosecondsD(1).InMillisecondsF();
+
+  WorkletAnimationId id = worklet_animation_->GetWorkletAnimationId();
+  base::TimeTicks first_ticks =
+      base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111.0);
+  base::TimeTicks second_ticks =
+      base::TimeTicks() + base::TimeDelta::FromMillisecondsD(111.0 + 123.4);
+  base::TimeTicks third_ticks =
+      base::TimeTicks() +
+      base::TimeDelta::FromMillisecondsD(111.0 + 123.4 + 200.0);
+  double playback_rate = 0.5;
+
+  // Start animation.
+  GetDocument().GetAnimationClock().ResetTimeForTesting(first_ticks);
+  DummyExceptionStateForTesting exception_state;
+  worklet_animation_->play(exception_state);
+  worklet_animation_->UpdateCompositingState();
+  std::unique_ptr<AnimationWorkletDispatcherInput> state =
+      std::make_unique<AnimationWorkletDispatcherInput>();
+  worklet_animation_->UpdateInputState(state.get());
+  state.reset(new AnimationWorkletDispatcherInput);
+
+  // Update playback rate after second tick.
+  GetDocument().GetAnimationClock().ResetTimeForTesting(second_ticks);
+  worklet_animation_->UpdateInputState(state.get());
+  worklet_animation_->setPlaybackRate(nullptr, playback_rate);
+  state.reset(new AnimationWorkletDispatcherInput);
+
+  // Verify current time after third tick.
+  GetDocument().GetAnimationClock().ResetTimeForTesting(third_ticks);
+  worklet_animation_->UpdateInputState(state.get());
+  std::unique_ptr<AnimationWorkletInput> input =
+      state->TakeWorkletState(id.worklet_id);
+  EXPECT_NEAR(123.4 + 200.0 * playback_rate,
+              input->updated_animations[0].current_time, error);
+}
 }  //  namespace blink
diff --git a/third_party/blink/renderer/modules/cache_storage/cache.cc b/third_party/blink/renderer/modules/cache_storage/cache.cc
index 11aaea6..b07fafd 100644
--- a/third_party/blink/renderer/modules/cache_storage/cache.cc
+++ b/third_party/blink/renderer/modules/cache_storage/cache.cc
@@ -438,15 +438,17 @@
     } else {
       // Schedule an idle task to generate code cache later.
       ServiceWorkerGlobalScope* global_scope = GetServiceWorkerGlobalScope();
-      auto* thread_scheduler =
-          global_scope->GetScheduler()->GetWorkerThreadScheduler();
-      DCHECK(thread_scheduler);
-      int task_id = global_scope->WillStartTask();
-      thread_scheduler->IdleTaskRunner()->PostIdleTask(
-          FROM_HERE, WTF::Bind(&Cache::CodeCacheHandleCallbackForPut::
-                                   GenerateCodeCacheOnIdleTask,
-                               WrapPersistent(this), task_id,
-                               WrapPersistent(array_buffer), response_time));
+      if (global_scope) {
+        auto* thread_scheduler =
+            global_scope->GetScheduler()->GetWorkerThreadScheduler();
+        DCHECK(thread_scheduler);
+        int task_id = global_scope->WillStartTask();
+        thread_scheduler->IdleTaskRunner()->PostIdleTask(
+            FROM_HERE, WTF::Bind(&Cache::CodeCacheHandleCallbackForPut::
+                                     GenerateCodeCacheOnIdleTask,
+                                 WrapPersistent(this), task_id,
+                                 WrapPersistent(array_buffer), response_time));
+      }
     }
 
     barrier_callback_->OnSuccess(index_, std::move(batch_operation));
@@ -467,10 +469,12 @@
 
  private:
   ServiceWorkerGlobalScope* GetServiceWorkerGlobalScope() {
+    ExecutionContext* context = ExecutionContext::From(script_state_);
+    if (!context || context->IsContextDestroyed())
+      return nullptr;
     // Currently |this| is only created for triggering V8 code caching after
     // Cache#put() is used by a service worker so |script_state_| should be
     // ServiceWorkerGlobalScope.
-    ExecutionContext* context = ExecutionContext::From(script_state_);
     auto* global_scope = DynamicTo<ServiceWorkerGlobalScope>(context);
     DCHECK(global_scope);
     return global_scope;
@@ -497,6 +501,9 @@
                                    base::Time response_time,
                                    base::TimeTicks) {
     ServiceWorkerGlobalScope* global_scope = GetServiceWorkerGlobalScope();
+    if (!global_scope)
+      return;
+
     scoped_refptr<CachedMetadata> cached_metadata =
         GenerateFullCodeCache(array_buffer);
     if (!cached_metadata) {
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc
index 441b1ae..9a59e2b8 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_input_element.cc
@@ -273,7 +273,7 @@
 }
 
 WebSize MediaControlInputElement::GetSizeOrDefault() const {
-  if (HasOverflowButton()) {
+  if (HasOverflowButton() || DisplayType() == kMediaOverflowButton) {
     // If this has an overflow button then it is a button control and therefore
     // has a default size of kDefaultButtonSize.
     return MediaControlElementsHelper::GetSizeOrDefault(
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
index 9e63a1e..0c58255 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.cc
@@ -21,24 +21,6 @@
 
 namespace blink {
 
-namespace {
-
-// Focus the given item in the list if it is displayed. Returns whether it was
-// focused.
-bool FocusListItemIfDisplayed(Node* node) {
-  Element* element = ToElement(node);
-
-  if (!element->InlineStyle() ||
-      !element->InlineStyle()->HasProperty(CSSPropertyDisplay)) {
-    element->focus();
-    return true;
-  }
-
-  return false;
-}
-
-}  // anonymous namespace
-
 class MediaControlPopupMenuElement::EventListener final
     : public NativeEventListener {
  public:
@@ -147,6 +129,7 @@
   if (event.type() == event_type_names::kPointermove &&
       event.target() != this) {
     ToElement(event.target()->ToNode())->focus();
+    last_focused_element_ = ToElement(event.target()->ToNode());
   } else if (event.type() == event_type_names::kFocusout) {
     GetDocument()
         .GetTaskRunner(TaskType::kMediaElementEvent)
@@ -158,6 +141,11 @@
 
     event.stopPropagation();
     event.SetDefaultHandled();
+  } else if (event.type() == event_type_names::kFocus) {
+    // When popup menu gain focus from scrolling, switch focus
+    // back to the last focused item in the menu
+    DCHECK(last_focused_element_);
+    last_focused_element_->focus();
   }
 
   MediaControlDivElement::DefaultEventHandler(event);
@@ -179,6 +167,7 @@
 void MediaControlPopupMenuElement::Trace(blink::Visitor* visitor) {
   MediaControlDivElement::Trace(visitor);
   visitor->Trace(event_listener_);
+  visitor->Trace(last_focused_element_);
 }
 
 MediaControlPopupMenuElement::MediaControlPopupMenuElement(
@@ -233,6 +222,21 @@
   }
 }
 
+// Focus the given item in the list if it is displayed. Returns whether it was
+// focused.
+bool MediaControlPopupMenuElement::FocusListItemIfDisplayed(Node* node) {
+  Element* element = ToElement(node);
+
+  if (!element->InlineStyle() ||
+      !element->InlineStyle()->HasProperty(CSSPropertyDisplay)) {
+    element->focus();
+    last_focused_element_ = element;
+    return true;
+  }
+
+  return false;
+}
+
 void MediaControlPopupMenuElement::SelectFirstItem() {
   for (Node* target = lastChild(); target; target = target->previousSibling()) {
     if (FocusListItemIfDisplayed(target))
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.h b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.h
index 67808d74..4e9d228a 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.h
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_popup_menu_element.h
@@ -50,6 +50,7 @@
 
   void HideIfNotFocused();
 
+  bool FocusListItemIfDisplayed(Node* node);
   void SelectFirstItem();
 
   // Actions called by the EventListener object when specific evenst are
@@ -59,6 +60,7 @@
   void CloseFromKeyboard();
 
   Member<EventListener> event_listener_;
+  Member<Element> last_focused_element_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
index a39597f8..30b9a61 100644
--- a/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
+++ b/third_party/blink/renderer/modules/media_controls/resources/modernMediaControls.css
@@ -204,6 +204,9 @@
   background-color: initial;
   color: inherit;
   cursor: pointer;
+
+  /* Cursor: pointer will cause a highlight when touch on it, this will disable it */
+  -webkit-tap-highlight-color: transparent;
 }
 
 /*
@@ -601,6 +604,10 @@
   margin: 0;
   background: transparent;
   cursor: pointer;
+
+  /* Cursor: pointer will cause a highlight when touch on it, this will disable it */
+  -webkit-tap-highlight-color: transparent;
+
   /* This prevents layout issues in quirks mode */
   box-sizing: unset !important;
 }
@@ -760,6 +767,10 @@
   padding: 22px 0; /* (48px button panel height - 4px slider height) / 2  */
   background: transparent;
   cursor: pointer;
+
+  /* Cursor: pointer will cause a highlight when touch on it, this will disable it */
+  -webkit-tap-highlight-color: transparent;
+
   /* This prevents layout issues in quirks mode. */
   box-sizing: unset !important;
 }
@@ -857,6 +868,15 @@
   padding-left: 16px;
   padding-right: 16px;
   cursor: pointer;
+  /* Cursor: pointer will cause a highlight when touch on it, this will disable it */
+  -webkit-tap-highlight-color: transparent;
+
+  /*
+   * Avoid slight text movement after item transition ends.
+   * By setting translateZ, we are using hardware acceleration,
+   * which allows a smoother transition.
+   */
+  transform: translateZ(0);
 }
 
 video::-webkit-media-controls div[pseudo="-internal-media-controls-text-track-list" i].pip-presented,
diff --git a/third_party/blink/renderer/modules/modules_idl_files.gni b/third_party/blink/renderer/modules/modules_idl_files.gni
index a5849ff..eb177e22 100644
--- a/third_party/blink/renderer/modules/modules_idl_files.gni
+++ b/third_party/blink/renderer/modules/modules_idl_files.gni
@@ -230,6 +230,8 @@
           "peerconnection/rtc_dtls_transport.idl",
           "peerconnection/rtc_dtmf_sender.idl",
           "peerconnection/rtc_dtmf_tone_change_event.idl",
+          "peerconnection/rtc_error.idl",
+          "peerconnection/rtc_error_event.idl",
           "peerconnection/rtc_ice_candidate.idl",
           "peerconnection/rtc_ice_transport.idl",
           "peerconnection/rtc_legacy_stats_report.idl",
@@ -632,6 +634,8 @@
           "peerconnection/rtc_data_channel_init.idl",
           "peerconnection/rtc_dtls_fingerprint.idl",
           "peerconnection/rtc_dtmf_tone_change_event_init.idl",
+          "peerconnection/rtc_error_event_init.idl",
+          "peerconnection/rtc_error_init.idl",
           "peerconnection/rtc_ice_candidate_init.idl",
           "peerconnection/rtc_ice_candidate_pair.idl",
           "peerconnection/rtc_ice_gather_options.idl",
diff --git a/third_party/blink/renderer/modules/payments/payment_request.cc b/third_party/blink/renderer/modules/payments/payment_request.cc
index c25412e7..a3105b5 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request.cc
@@ -1046,7 +1046,6 @@
 }
 
 void PaymentRequest::OnUpdatePaymentDetails(
-    const AtomicString& event_type,
     const ScriptValue& details_script_value) {
   if (!GetPendingAcceptPromiseResolver() || !payment_provider_)
     return;
@@ -1083,22 +1082,6 @@
     return;
   }
 
-  // TODO(https://crbug.com/902291): We should make shippingOptions optional.
-  if (options_->requestShipping() && !details->hasShippingOptions()) {
-    if (event_type == event_type_names::kShippingaddresschange) {
-      UseCounter::Count(
-          GetExecutionContext(),
-          WebFeature::kUpdateWithoutShippingOptionOnShippingAddressChange);
-      validated_details->shipping_options = Vector<PaymentShippingOptionPtr>();
-    }
-    if (event_type == event_type_names::kShippingoptionchange) {
-      UseCounter::Count(
-          GetExecutionContext(),
-          WebFeature::kUpdateWithoutShippingOptionOnShippingOptionChange);
-      validated_details->shipping_options = Vector<PaymentShippingOptionPtr>();
-    }
-  }
-
   if (!options_->requestShipping())
     validated_details->shipping_options = base::nullopt;
 
diff --git a/third_party/blink/renderer/modules/payments/payment_request.h b/third_party/blink/renderer/modules/payments/payment_request.h
index 089fd8b1..a4e7f6a 100644
--- a/third_party/blink/renderer/modules/payments/payment_request.h
+++ b/third_party/blink/renderer/modules/payments/payment_request.h
@@ -94,8 +94,7 @@
   ScriptPromise Retry(ScriptState*, const PaymentValidationErrors*) override;
 
   // PaymentUpdater:
-  void OnUpdatePaymentDetails(const AtomicString& event_type,
-                              const ScriptValue& details_script_value) override;
+  void OnUpdatePaymentDetails(const ScriptValue& details_script_value) override;
   void OnUpdatePaymentDetailsFailure(const String& error) override;
 
   void Trace(blink::Visitor*) override;
diff --git a/third_party/blink/renderer/modules/payments/payment_request_test.cc b/third_party/blink/renderer/modules/payments/payment_request_test.cc
index 08cc5ee..2be918d 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_test.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_test.cc
@@ -390,7 +390,6 @@
       ->OnPaymentResponse(BuildPaymentResponseForTest());
 
   request->OnUpdatePaymentDetails(
-      event_type_names::kShippingaddresschange,
       ScriptValue::From(scope.GetScriptState(), "foo"));
 }
 
@@ -407,7 +406,6 @@
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall());
 
   request->OnUpdatePaymentDetails(
-      event_type_names::kShippingaddresschange,
       ScriptValue::From(scope.GetScriptState(), "NotPaymentDetails"));
 }
 
@@ -424,7 +422,6 @@
       .Then(funcs.ExpectNoCall(), funcs.ExpectCall());
 
   request->OnUpdatePaymentDetails(
-      event_type_names::kShippingaddresschange,
       ScriptValue::From(
           scope.GetScriptState(),
           FromJSONString(scope.GetScriptState()->GetIsolate(),
@@ -456,7 +453,6 @@
       "\"Standard shipping\", \"amount\": {\"currency\": \"USD\", \"value\": "
       "\"5.00\"}, \"selected\": true}]}";
   request->OnUpdatePaymentDetails(
-      event_type_names::kShippingaddresschange,
       ScriptValue::From(scope.GetScriptState(),
                         FromJSONString(scope.GetScriptState()->GetIsolate(),
                                        scope.GetScriptState()->GetContext(),
@@ -469,7 +465,6 @@
       "\"value\": \"5.00\"}}}";
 
   request->OnUpdatePaymentDetails(
-      event_type_names::kShippingaddresschange,
       ScriptValue::From(scope.GetScriptState(),
                         FromJSONString(scope.GetScriptState()->GetIsolate(),
                                        scope.GetScriptState()->GetContext(),
@@ -503,7 +498,6 @@
       "\"USD\", \"value\": \"50.00\"}}]}";
 
   request->OnUpdatePaymentDetails(
-      event_type_names::kShippingaddresschange,
       ScriptValue::From(scope.GetScriptState(),
                         FromJSONString(scope.GetScriptState()->GetIsolate(),
                                        scope.GetScriptState()->GetContext(),
@@ -534,7 +528,6 @@
       "\"USD\", \"value\": \"50.00\"}, \"selected\": true}]}";
 
   request->OnUpdatePaymentDetails(
-      event_type_names::kShippingaddresschange,
       ScriptValue::From(scope.GetScriptState(),
                         FromJSONString(scope.GetScriptState()->GetIsolate(),
                                        scope.GetScriptState()->GetContext(),
@@ -561,7 +554,6 @@
       "\"error\": \"This is an error message.\"}";
 
   request->OnUpdatePaymentDetails(
-      event_type_names::kShippingaddresschange,
       ScriptValue::From(
           scope.GetScriptState(),
           FromJSONString(scope.GetScriptState()->GetIsolate(),
diff --git a/third_party/blink/renderer/modules/payments/payment_request_update_event.cc b/third_party/blink/renderer/modules/payments/payment_request_update_event.cc
index 4035b7f..c6ec2a99 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_update_event.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_update_event.cc
@@ -45,7 +45,7 @@
 
  private:
   ScriptValue Call(ScriptValue value) override {
-    update_event_->OnUpdatePaymentDetails(update_event_->type(), value);
+    update_event_->OnUpdatePaymentDetails(value);
     return ScriptValue();
   }
 
@@ -134,12 +134,11 @@
 }
 
 void PaymentRequestUpdateEvent::OnUpdatePaymentDetails(
-    const AtomicString& event_type,
     const ScriptValue& details_script_value) {
   if (!updater_)
     return;
   abort_timer_.Stop();
-  updater_->OnUpdatePaymentDetails(event_type, details_script_value);
+  updater_->OnUpdatePaymentDetails(details_script_value);
   updater_ = nullptr;
 }
 
diff --git a/third_party/blink/renderer/modules/payments/payment_request_update_event.h b/third_party/blink/renderer/modules/payments/payment_request_update_event.h
index d8618f53..f8c99a6 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_update_event.h
+++ b/third_party/blink/renderer/modules/payments/payment_request_update_event.h
@@ -44,8 +44,7 @@
   bool is_waiting_for_update() const { return wait_for_update_; }
 
   // PaymentUpdater:
-  void OnUpdatePaymentDetails(const AtomicString& event_type,
-                              const ScriptValue& details_script_value) override;
+  void OnUpdatePaymentDetails(const ScriptValue& details_script_value) override;
   void OnUpdatePaymentDetailsFailure(const String& error) override;
 
   void Trace(blink::Visitor*) override;
diff --git a/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc b/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc
index 6e44d1ab..d93a941 100644
--- a/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc
+++ b/third_party/blink/renderer/modules/payments/payment_request_update_event_test.cc
@@ -28,9 +28,8 @@
   MockPaymentUpdater() = default;
   ~MockPaymentUpdater() override = default;
 
-  MOCK_METHOD2(OnUpdatePaymentDetails,
-               void(const AtomicString& event_type,
-                    const ScriptValue& detailsScriptValue));
+  MOCK_METHOD1(OnUpdatePaymentDetails,
+               void(const ScriptValue& detailsScriptValue));
   MOCK_METHOD1(OnUpdatePaymentDetailsFailure, void(const String& error));
 
   void Trace(blink::Visitor* visitor) override {}
@@ -50,9 +49,7 @@
                     scope.GetExceptionState());
   EXPECT_FALSE(scope.GetExceptionState().HadException());
 
-  EXPECT_CALL(*updater,
-              OnUpdatePaymentDetails(event_type_names::kShippingaddresschange,
-                                     testing::_));
+  EXPECT_CALL(*updater, OnUpdatePaymentDetails(testing::_));
   EXPECT_CALL(*updater, OnUpdatePaymentDetailsFailure(testing::_)).Times(0);
 
   payment_details->Resolve("foo");
@@ -72,10 +69,7 @@
                     scope.GetExceptionState());
   EXPECT_FALSE(scope.GetExceptionState().HadException());
 
-  EXPECT_CALL(*updater,
-              OnUpdatePaymentDetails(event_type_names::kShippingaddresschange,
-                                     testing::_))
-      .Times(0);
+  EXPECT_CALL(*updater, OnUpdatePaymentDetails(testing::_)).Times(0);
   EXPECT_CALL(*updater, OnUpdatePaymentDetailsFailure(testing::_));
 
   payment_details->Reject("oops");
diff --git a/third_party/blink/renderer/modules/payments/payment_updater.h b/third_party/blink/renderer/modules/payments/payment_updater.h
index 57cccf9..0e278dd 100644
--- a/third_party/blink/renderer/modules/payments/payment_updater.h
+++ b/third_party/blink/renderer/modules/payments/payment_updater.h
@@ -16,7 +16,6 @@
 class MODULES_EXPORT PaymentUpdater : public GarbageCollectedMixin {
  public:
   virtual void OnUpdatePaymentDetails(
-      const AtomicString& event_type,
       const ScriptValue& details_script_value) = 0;
   virtual void OnUpdatePaymentDetailsFailure(const String& error) = 0;
 
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index 63bf6037..9a307742 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -59,6 +59,10 @@
     "rtc_dtmf_sender.h",
     "rtc_dtmf_tone_change_event.cc",
     "rtc_dtmf_tone_change_event.h",
+    "rtc_error.cc",
+    "rtc_error.h",
+    "rtc_error_event.cc",
+    "rtc_error_event.h",
     "rtc_error_util.cc",
     "rtc_error_util.h",
     "rtc_ice_candidate.cc",
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error.cc b/third_party/blink/renderer/modules/peerconnection/rtc_error.cc
new file mode 100644
index 0000000..df1e781
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error.cc
@@ -0,0 +1,65 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/peerconnection/rtc_error.h"
+
+#include <utility>
+
+namespace blink {
+
+// static
+RTCError* RTCError::Create(String message, const RTCErrorInit* init) {
+  return MakeGarbageCollected<RTCError>(std::move(message), init);
+}
+
+RTCError::RTCError(String message, const RTCErrorInit* init)
+    : DOMException(0u, "RTCError", std::move(message), String()),
+      error_detail_(init->errorDetail()),
+      sdp_line_number_(init->hasSdpLineNumber()
+                           ? base::Optional<int32_t>(init->sdpLineNumber())
+                           : base::nullopt),
+      http_request_status_code_(
+          init->hasHttpRequestStatusCode()
+              ? base::Optional<int32_t>(init->httpRequestStatusCode())
+              : base::nullopt),
+      sctp_cause_code_(init->hasSctpCauseCode()
+                           ? base::Optional<int32_t>(init->sctpCauseCode())
+                           : base::nullopt),
+      received_alert_(init->hasReceivedAlert()
+                          ? base::Optional<uint32_t>(init->receivedAlert())
+                          : base::nullopt),
+      sent_alert_(init->hasSentAlert()
+                      ? base::Optional<uint32_t>(init->sentAlert())
+                      : base::nullopt) {}
+
+const String& RTCError::errorDetail() const {
+  return error_detail_;
+}
+
+int32_t RTCError::sdpLineNumber(bool& is_null) const {
+  is_null = !sdp_line_number_;
+  return sdp_line_number_ ? *sdp_line_number_ : 0;
+}
+
+int32_t RTCError::httpRequestStatusCode(bool& is_null) const {
+  is_null = !http_request_status_code_;
+  return http_request_status_code_ ? *http_request_status_code_ : 0;
+}
+
+int32_t RTCError::sctpCauseCode(bool& is_null) const {
+  is_null = !sctp_cause_code_;
+  return sctp_cause_code_ ? *sctp_cause_code_ : 0;
+}
+
+uint32_t RTCError::receivedAlert(bool& is_null) const {
+  is_null = !received_alert_;
+  return received_alert_ ? *received_alert_ : 0u;
+}
+
+uint32_t RTCError::sentAlert(bool& is_null) const {
+  is_null = !sent_alert_;
+  return sent_alert_ ? *sent_alert_ : 0u;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error.h b/third_party/blink/renderer/modules/peerconnection/rtc_error.h
new file mode 100644
index 0000000..67f52ab
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error.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 THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_ERROR_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_ERROR_H_
+
+#include "base/optional.h"
+#include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_error_init.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+class RTCError final : public DOMException {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static RTCError* Create(String message, const RTCErrorInit* init);
+  RTCError(String message, const RTCErrorInit* init);
+
+  const String& errorDetail() const;
+  int32_t sdpLineNumber(bool& is_null) const;
+  int32_t httpRequestStatusCode(bool& is_null) const;
+  int32_t sctpCauseCode(bool& is_null) const;
+  uint32_t receivedAlert(bool& is_null) const;
+  uint32_t sentAlert(bool& is_null) const;
+
+ private:
+  // idl enum RTCErrorDetailType.
+  String error_detail_;
+  base::Optional<int32_t> sdp_line_number_;
+  base::Optional<int32_t> http_request_status_code_;
+  base::Optional<int32_t> sctp_cause_code_;
+  base::Optional<uint32_t> received_alert_;
+  base::Optional<uint32_t> sent_alert_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_ERROR_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error.idl b/third_party/blink/renderer/modules/peerconnection/rtc_error.idl
new file mode 100644
index 0000000..e01c915
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error.idl
@@ -0,0 +1,34 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webrtc-pc/#dfn-rtcerrordetailtype
+enum RTCErrorDetailType {
+    "data-channel-failure",
+    "dtls-failure",
+    "fingerprint-failure",
+    "idp-bad-script-failure",
+    "idp-execution-failure",
+    "idp-load-failure",
+    "idp-need-login",
+    "idp-timeout",
+    "idp-tls-failure",
+    "idp-token-expired",
+    "idp-token-invalid",
+    "sctp-failure",
+    "sdp-syntax-error",
+    "hardware-encoder-not-available",
+    "hardware-encoder-error"
+};
+
+// https://w3c.github.io/webrtc-pc/#dfn-rtcerror
+[
+    Constructor(DOMString message, RTCErrorInit init)
+] interface RTCError : DOMException {
+    readonly attribute RTCErrorDetailType errorDetail;
+    readonly attribute long? sdpLineNumber;
+    readonly attribute long? httpRequestStatusCode;
+    readonly attribute long? sctpCauseCode;
+    readonly attribute unsigned long? receivedAlert;
+    readonly attribute unsigned long? sentAlert;
+};
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error_event.cc b/third_party/blink/renderer/modules/peerconnection/rtc_error_event.cc
new file mode 100644
index 0000000..ba5e9950
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error_event.cc
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/modules/peerconnection/rtc_error_event.h"
+
+#include "third_party/blink/renderer/platform/heap/heap.h"
+
+namespace blink {
+
+// static
+RTCErrorEvent* RTCErrorEvent::Create(const AtomicString& type,
+                                     const RTCErrorEventInit* event_init_dict) {
+  return MakeGarbageCollected<RTCErrorEvent>(type, event_init_dict);
+}
+
+RTCErrorEvent::RTCErrorEvent(const AtomicString& type,
+                             const RTCErrorEventInit* event_init_dict)
+    : Event(type, event_init_dict), error_(event_init_dict->error()) {
+  DCHECK(event_init_dict);
+}
+
+RTCError* RTCErrorEvent::error() const {
+  return error_;
+}
+
+void RTCErrorEvent::Trace(blink::Visitor* visitor) {
+  visitor->Trace(error_);
+  Event::Trace(visitor);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error_event.h b/third_party/blink/renderer/modules/peerconnection/rtc_error_event.h
new file mode 100644
index 0000000..e768586
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error_event.h
@@ -0,0 +1,38 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_ERROR_EVENT_H_
+#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_ERROR_EVENT_H_
+
+#include "third_party/blink/renderer/modules/event_modules.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_error.h"
+#include "third_party/blink/renderer/modules/peerconnection/rtc_error_event_init.h"
+#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
+#include "third_party/blink/renderer/platform/heap/member.h"
+#include "third_party/blink/renderer/platform/heap/visitor.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+
+class RTCErrorEvent final : public Event {
+  DEFINE_WRAPPERTYPEINFO();
+
+ public:
+  static RTCErrorEvent* Create(const AtomicString& type,
+                               const RTCErrorEventInit* event_init_dict);
+
+  RTCErrorEvent(const AtomicString& type,
+                const RTCErrorEventInit* event_init_dict);
+
+  RTCError* error() const;
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  Member<RTCError> error_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_RTC_ERROR_EVENT_H_
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error_event.idl b/third_party/blink/renderer/modules/peerconnection/rtc_error_event.idl
new file mode 100644
index 0000000..8ef88d0a
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error_event.idl
@@ -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.
+
+// https://w3c.github.io/webrtc-pc/#dom-rtcerrorevent
+[
+    Exposed=Window,
+    Constructor(DOMString type, RTCErrorEventInit eventInitDict)
+] interface RTCErrorEvent : Event {
+    readonly attribute RTCError error;
+};
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error_event_init.idl b/third_party/blink/renderer/modules/peerconnection/rtc_error_event_init.idl
new file mode 100644
index 0000000..fe3e5cf
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error_event_init.idl
@@ -0,0 +1,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.
+
+// https://w3c.github.io/webrtc-pc/#dom-rtcerroreventinit
+dictionary RTCErrorEventInit : EventInit {
+    required RTCError error;
+};
diff --git a/third_party/blink/renderer/modules/peerconnection/rtc_error_init.idl b/third_party/blink/renderer/modules/peerconnection/rtc_error_init.idl
new file mode 100644
index 0000000..558fd26
--- /dev/null
+++ b/third_party/blink/renderer/modules/peerconnection/rtc_error_init.idl
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://w3c.github.io/webrtc-pc/#dfn-rtcerrorinit
+dictionary RTCErrorInit {
+    required RTCErrorDetailType errorDetail;
+    long sdpLineNumber;
+    long httpRequestStatusCode;
+    long sctpCauseCode;
+    unsigned long receivedAlert;
+    unsigned long sentAlert;
+};
diff --git a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
index eb3674a..1c2521ca 100644
--- a/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
+++ b/third_party/blink/renderer/modules/remoteplayback/remote_playback.cc
@@ -19,7 +19,7 @@
 #include "third_party/blink/renderer/modules/presentation/presentation_availability_state.h"
 #include "third_party/blink/renderer/modules/presentation/presentation_controller.h"
 #include "third_party/blink/renderer/modules/remoteplayback/availability_callback_wrapper.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 #include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
 #include "third_party/blink/renderer/platform/wtf/text/base64.h"
 
@@ -67,7 +67,7 @@
 }
 
 bool IsBackgroundAvailabilityMonitoringDisabled() {
-  return MemoryCoordinator::IsLowEndDevice();
+  return MemoryPressureListenerRegistry::IsLowEndDevice();
 }
 
 }  // anonymous namespace
diff --git a/third_party/blink/renderer/modules/webdatabase/database_client.cc b/third_party/blink/renderer/modules/webdatabase/database_client.cc
index a05e62d..5b0a433b 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_client.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database_client.cc
@@ -56,14 +56,11 @@
 
 const char DatabaseClient::kSupplementName[] = "DatabaseClient";
 
-bool DatabaseClient::AllowDatabase(ExecutionContext* context,
-                                   const String& name,
-                                   const String& display_name,
-                                   unsigned estimated_size) {
+bool DatabaseClient::AllowDatabase(ExecutionContext* context) {
   DCHECK(context->IsContextThread());
   Document* document = To<Document>(context);
   if (auto* client = document->GetFrame()->GetContentSettingsClient())
-    return client->AllowDatabase(name, display_name, estimated_size);
+    return client->AllowDatabase();
   return true;
 }
 
diff --git a/third_party/blink/renderer/modules/webdatabase/database_client.h b/third_party/blink/renderer/modules/webdatabase/database_client.h
index 1b8bb92..18c6d89 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_client.h
+++ b/third_party/blink/renderer/modules/webdatabase/database_client.h
@@ -56,10 +56,7 @@
 
   void Trace(blink::Visitor*) override;
 
-  bool AllowDatabase(ExecutionContext*,
-                     const String& name,
-                     const String& display_name,
-                     unsigned estimated_size);
+  bool AllowDatabase(ExecutionContext*);
 
   void DidOpenDatabase(Database*,
                        const String& domain,
diff --git a/third_party/blink/renderer/modules/webdatabase/database_manager.cc b/third_party/blink/renderer/modules/webdatabase/database_manager.cc
index abe7320..eafeea1 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_manager.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database_manager.cc
@@ -142,8 +142,7 @@
   DCHECK_EQ(error, DatabaseError::kNone);
 
   DatabaseContext* backend_context = DatabaseContextFor(context)->Backend();
-  if (DatabaseTracker::Tracker().CanEstablishDatabase(
-          backend_context, name, display_name, estimated_size, error)) {
+  if (DatabaseTracker::Tracker().CanEstablishDatabase(backend_context, error)) {
     Database* backend = MakeGarbageCollected<Database>(
         backend_context, name, expected_version, display_name, estimated_size);
     if (backend->OpenAndVerifyVersion(set_version_in_new_database, error,
diff --git a/third_party/blink/renderer/modules/webdatabase/database_tracker.cc b/third_party/blink/renderer/modules/webdatabase/database_tracker.cc
index 99bdad21..d66f60a0 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_tracker.cc
+++ b/third_party/blink/renderer/modules/webdatabase/database_tracker.cc
@@ -71,14 +71,10 @@
 }
 
 bool DatabaseTracker::CanEstablishDatabase(DatabaseContext* database_context,
-                                           const String& name,
-                                           const String& display_name,
-                                           unsigned estimated_size,
                                            DatabaseError& error) {
   ExecutionContext* execution_context = database_context->GetExecutionContext();
-  bool success = DatabaseClient::From(execution_context)
-                     ->AllowDatabase(execution_context, name, display_name,
-                                     estimated_size);
+  bool success =
+      DatabaseClient::From(execution_context)->AllowDatabase(execution_context);
   if (!success)
     error = DatabaseError::kGenericSecurityError;
   return success;
diff --git a/third_party/blink/renderer/modules/webdatabase/database_tracker.h b/third_party/blink/renderer/modules/webdatabase/database_tracker.h
index 36ed498a..7a1ee75 100644
--- a/third_party/blink/renderer/modules/webdatabase/database_tracker.h
+++ b/third_party/blink/renderer/modules/webdatabase/database_tracker.h
@@ -64,9 +64,6 @@
   // notificationMutex() is currently independent of the other locks.
 
   bool CanEstablishDatabase(DatabaseContext*,
-                            const String& name,
-                            const String& display_name,
-                            unsigned estimated_size,
                             DatabaseError&);
   String FullPathForDatabase(const SecurityOrigin*,
                              const String& name,
diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn
index 57f3c69..f6dceff 100644
--- a/third_party/blink/renderer/platform/BUILD.gn
+++ b/third_party/blink/renderer/platform/BUILD.gn
@@ -509,6 +509,7 @@
     "exported/mediastream/media_stream_audio_track.cc",
     "exported/mediastream/web_platform_media_stream_source.cc",
     "exported/mediastream/web_platform_media_stream_track.cc",
+    "exported/notification_data_conversions.cc",
     "exported/platform.cc",
     "exported/service_registry.cc",
     "exported/url_conversion.cc",
@@ -552,7 +553,7 @@
     "exported/web_media_stream_audio_sink.cc",
     "exported/web_media_stream_source.cc",
     "exported/web_media_stream_track.cc",
-    "exported/web_memory_coordinator.cc",
+    "exported/web_memory_pressure_listener.cc",
     "exported/web_mixed_content.cc",
     "exported/web_network_state_notifier.cc",
     "exported/web_prerender.cc",
@@ -1075,7 +1076,6 @@
     "graphics/paint/raster_invalidation_tracking.h",
     "graphics/paint/raster_invalidator.cc",
     "graphics/paint/raster_invalidator.h",
-    "graphics/paint/ref_counted_property_tree_state.cc",
     "graphics/paint/ref_counted_property_tree_state.h",
     "graphics/paint/scoped_display_item_fragment.h",
     "graphics/paint/scoped_paint_chunk_properties.h",
@@ -1206,8 +1206,8 @@
     "mediastream/media_stream_source.h",
     "mediastream/media_stream_web_audio_source.cc",
     "mediastream/media_stream_web_audio_source.h",
-    "memory_coordinator.cc",
-    "memory_coordinator.h",
+    "memory_pressure_listener.cc",
+    "memory_pressure_listener.h",
     "mhtml/archive_resource.cc",
     "mhtml/archive_resource.h",
     "mhtml/mhtml_archive.cc",
@@ -1468,6 +1468,7 @@
     "//third_party/blink/public:embedded_frame_sink_mojo_bindings_blink",
     "//third_party/blink/public/common",
     "//third_party/ced",
+    "//third_party/emoji-segmenter",
     "//third_party/icu",
     "//third_party/webrtc/p2p:rtc_p2p",
     "//third_party/webrtc_overrides:init_webrtc",
@@ -1678,6 +1679,7 @@
     "bindings/runtime_call_stats_test.cc",
     "drag_image_test.cc",
     "exported/file_path_conversion_test.cc",
+    "exported/notification_data_conversions_test.cc",
     "exported/web_canonical_cookie_test.cc",
     "exported/web_string_test.cc",
     "fonts/android/font_cache_android_test.cc",
diff --git a/third_party/blink/renderer/platform/animation/animated_layers_test.cc b/third_party/blink/renderer/platform/animation/animated_layers_test.cc
index b2f4699..0b273f9 100644
--- a/third_party/blink/renderer/platform/animation/animated_layers_test.cc
+++ b/third_party/blink/renderer/platform/animation/animated_layers_test.cc
@@ -33,9 +33,9 @@
   ViewportLayersSetup layers_;
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        AnimatedLayersTest,
-                        testing::Values(0, kBlinkGenPropertyTrees));
+INSTANTIATE_TEST_SUITE_P(All,
+                         AnimatedLayersTest,
+                         testing::Values(0, kBlinkGenPropertyTrees));
 
 class AnimationForTesting : public CompositorAnimationClient {
  public:
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.cc b/third_party/blink/renderer/platform/animation/compositor_animation.cc
index 093ef25..c1dc980 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation.cc
+++ b/third_party/blink/renderer/platform/animation/compositor_animation.cc
@@ -22,11 +22,12 @@
 CompositorAnimation::CreateWorkletAnimation(
     cc::WorkletAnimationId worklet_animation_id,
     const String& name,
+    double playback_rate,
     std::unique_ptr<CompositorScrollTimeline> scroll_timeline,
     std::unique_ptr<cc::AnimationOptions> options) {
   return std::make_unique<CompositorAnimation>(cc::WorkletAnimation::Create(
       worklet_animation_id, std::string(name.Ascii().data(), name.length()),
-      std::move(scroll_timeline), std::move(options)));
+      playback_rate, std::move(scroll_timeline), std::move(options)));
 }
 
 CompositorAnimation::CompositorAnimation(
@@ -90,6 +91,10 @@
                              end_scroll_offset);
 }
 
+void CompositorAnimation::UpdatePlaybackRate(double playback_rate) {
+  cc::ToWorkletAnimation(animation_.get())->UpdatePlaybackRate(playback_rate);
+}
+
 void CompositorAnimation::NotifyAnimationStarted(base::TimeTicks monotonic_time,
                                                  int target_property,
                                                  int group) {
diff --git a/third_party/blink/renderer/platform/animation/compositor_animation.h b/third_party/blink/renderer/platform/animation/compositor_animation.h
index 8781610d..0d233ea 100644
--- a/third_party/blink/renderer/platform/animation/compositor_animation.h
+++ b/third_party/blink/renderer/platform/animation/compositor_animation.h
@@ -37,6 +37,7 @@
   static std::unique_ptr<CompositorAnimation> CreateWorkletAnimation(
       cc::WorkletAnimationId,
       const String& name,
+      double playback_rate,
       std::unique_ptr<CompositorScrollTimeline>,
       std::unique_ptr<cc::AnimationOptions>);
 
@@ -64,6 +65,7 @@
   void UpdateScrollTimeline(base::Optional<cc::ElementId>,
                             base::Optional<double> start_scroll_offset,
                             base::Optional<double> end_scroll_offset);
+  void UpdatePlaybackRate(double playback_rate);
 
  private:
   // cc::AnimationDelegate implementation.
diff --git a/third_party/blink/renderer/platform/bindings/DEPS b/third_party/blink/renderer/platform/bindings/DEPS
index 455e089..65ed8d0 100644
--- a/third_party/blink/renderer/platform/bindings/DEPS
+++ b/third_party/blink/renderer/platform/bindings/DEPS
@@ -11,7 +11,7 @@
     "+third_party/blink/renderer/platform/heap",
     "+third_party/blink/renderer/platform/instance_counters.h",
     "+third_party/blink/renderer/platform/instrumentation",
-    "+third_party/blink/renderer/platform/memory_coordinator.h",
+    "+third_party/blink/renderer/platform/memory_pressure_listener.h",
     "+third_party/blink/renderer/platform/platform_export.h",
     "+third_party/blink/renderer/platform/runtime_enabled_features.h",
     "+third_party/blink/renderer/platform/scheduler",
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc b/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc
index b38c876b..adf81acf 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_manager.cc
@@ -14,7 +14,7 @@
 #include "base/trace_event/process_memory_dump.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/bindings/parkable_string.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
@@ -26,7 +26,7 @@
 namespace {
 
 class OnPurgeMemoryListener : public GarbageCollected<OnPurgeMemoryListener>,
-                              public MemoryCoordinatorClient {
+                              public MemoryPressureListener {
   USING_GARBAGE_COLLECTED_MIXIN(OnPurgeMemoryListener);
 
   void OnPurgeMemory() override {
@@ -335,7 +335,7 @@
       parked_strings_() {
   // No need to ever unregister, as the only ParkableStringManager instance
   // lives forever.
-  MemoryCoordinator::Instance().RegisterClient(
+  MemoryPressureListenerRegistry::Instance().RegisterClient(
       MakeGarbageCollected<OnPurgeMemoryListener>());
 }
 
diff --git a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
index a42626e9..3b278fc 100644
--- a/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
+++ b/third_party/blink/renderer/platform/bindings/parkable_string_test.cc
@@ -18,7 +18,7 @@
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/renderer/platform/bindings/parkable_string.h"
 #include "third_party/blink/renderer/platform/bindings/parkable_string_manager.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 
 namespace blink {
 
@@ -593,7 +593,7 @@
   EXPECT_FALSE(parkable.Impl()->is_parked());
   EXPECT_TRUE(parkable.Impl()->has_compressed_data());
 
-  MemoryCoordinator::Instance().OnPurgeMemory();
+  MemoryPressureListenerRegistry::Instance().OnPurgeMemory();
   EXPECT_TRUE(parkable.Impl()->is_parked());
 
   parkable.ToString();
@@ -619,7 +619,7 @@
   String retained = parkable2.ToString();
   EXPECT_TRUE(parkable2.Impl()->has_compressed_data());
 
-  MemoryCoordinator::Instance().OnPurgeMemory();
+  MemoryPressureListenerRegistry::Instance().OnPurgeMemory();
   EXPECT_TRUE(parkable1.Impl()->is_parked());  // Parked synchronously.
   EXPECT_FALSE(parkable2.Impl()->is_parked());
   EXPECT_FALSE(parkable2.Impl()->has_compressed_data());  // Purged.
diff --git a/third_party/blink/renderer/platform/exported/DEPS b/third_party/blink/renderer/platform/exported/DEPS
index 53f699c..c2bd6b1 100644
--- a/third_party/blink/renderer/platform/exported/DEPS
+++ b/third_party/blink/renderer/platform/exported/DEPS
@@ -1,5 +1,12 @@
-
 include_rules = [
     "+net/cookies/canonical_cookie.h",
     "+net/cookies/cookie_constants.h",
 ]
+
+specific_include_rules = {
+    "notification_data_conversions_test.cc": [
+        "+base/strings/nullable_string16.h",
+        "+base/strings/utf_string_conversions.h",
+    ],
+}
+
diff --git a/third_party/blink/renderer/platform/exported/OWNERS b/third_party/blink/renderer/platform/exported/OWNERS
index fa50614..9a717779 100644
--- a/third_party/blink/renderer/platform/exported/OWNERS
+++ b/third_party/blink/renderer/platform/exported/OWNERS
@@ -1 +1,2 @@
+per-file notification_data_conversions*=peter@chromium.org
 per-file web_rtc_*=hbos@chromium.org
diff --git a/third_party/blink/renderer/platform/exported/notification_data_conversions.cc b/third_party/blink/renderer/platform/exported/notification_data_conversions.cc
new file mode 100644
index 0000000..d5d3613
--- /dev/null
+++ b/third_party/blink/renderer/platform/exported/notification_data_conversions.cc
@@ -0,0 +1,71 @@
+// 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/public/platform/notification_data_conversions.h"
+
+#include "third_party/blink/public/mojom/notifications/notification.mojom-shared.h"
+#include "third_party/blink/public/platform/modules/notifications/web_notification_action.h"
+#include "third_party/blink/public/platform/url_conversion.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+#include "third_party/blink/public/platform/web_vector.h"
+#include "third_party/blink/renderer/platform/weborigin/kurl.h"
+
+namespace blink {
+
+WebNotificationData ToWebNotificationData(
+    const PlatformNotificationData& platform_data) {
+  WebNotificationData web_data;
+  web_data.title = WebString::FromUTF16(platform_data.title);
+  switch (platform_data.direction) {
+    case PlatformNotificationData::DIRECTION_LEFT_TO_RIGHT:
+      web_data.direction = mojom::NotificationDirection::LEFT_TO_RIGHT;
+      break;
+    case PlatformNotificationData::DIRECTION_RIGHT_TO_LEFT:
+      web_data.direction = mojom::NotificationDirection::RIGHT_TO_LEFT;
+      break;
+    case PlatformNotificationData::DIRECTION_AUTO:
+      web_data.direction = mojom::NotificationDirection::AUTO;
+      break;
+  }
+
+  web_data.lang = WebString::FromUTF8(platform_data.lang);
+  web_data.body = WebString::FromUTF16(platform_data.body);
+  web_data.tag = WebString::FromUTF8(platform_data.tag);
+  web_data.image = WebURL(KURL(platform_data.image));
+  web_data.icon = WebURL(KURL(platform_data.icon));
+  web_data.badge = WebURL(KURL(platform_data.badge));
+  web_data.vibrate = platform_data.vibration_pattern;
+  web_data.timestamp = platform_data.timestamp.ToJsTime();
+  web_data.renotify = platform_data.renotify;
+  web_data.silent = platform_data.silent;
+  web_data.require_interaction = platform_data.require_interaction;
+  web_data.data = platform_data.data;
+  WebVector<WebNotificationAction> resized(platform_data.actions.size());
+  web_data.actions.Swap(resized);
+  for (size_t i = 0; i < platform_data.actions.size(); ++i) {
+    switch (platform_data.actions[i].type) {
+      case PLATFORM_NOTIFICATION_ACTION_TYPE_BUTTON:
+        web_data.actions[i].type = WebNotificationAction::kButton;
+        break;
+      case PLATFORM_NOTIFICATION_ACTION_TYPE_TEXT:
+        web_data.actions[i].type = WebNotificationAction::kText;
+        break;
+      default:
+        NOTREACHED() << "Unknown platform data type: "
+                     << platform_data.actions[i].type;
+    }
+    web_data.actions[i].action =
+        WebString::FromUTF8(platform_data.actions[i].action);
+    web_data.actions[i].title =
+        WebString::FromUTF16(platform_data.actions[i].title);
+    web_data.actions[i].icon = WebURL(KURL(platform_data.actions[i].icon));
+    web_data.actions[i].placeholder =
+        WebString::FromUTF16(platform_data.actions[i].placeholder);
+  }
+
+  return web_data;
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/exported/notification_data_conversions_test.cc b/third_party/blink/renderer/platform/exported/notification_data_conversions_test.cc
new file mode 100644
index 0000000..dc422ef
--- /dev/null
+++ b/third_party/blink/renderer/platform/exported/notification_data_conversions_test.cc
@@ -0,0 +1,114 @@
+// 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/public/platform/notification_data_conversions.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/stl_util.h"
+#include "base/strings/nullable_string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/common/notifications/platform_notification_data.h"
+#include "third_party/blink/public/platform/modules/notifications/web_notification_data.h"
+#include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/platform/web_url.h"
+
+namespace blink {
+
+const char kNotificationTitle[] = "My Notification";
+const char kNotificationLang[] = "nl";
+const char kNotificationBody[] = "Hello, world!";
+const char kNotificationTag[] = "my_tag";
+const char kNotificationImageUrl[] = "https://example.com/image.jpg";
+const char kNotificationIconUrl[] = "https://example.com/icon.png";
+const char kNotificationBadgeUrl[] = "https://example.com/badge.png";
+const int kNotificationVibrationPattern[] = {100, 200, 300};
+const double kNotificationTimestamp = 621046800.;
+const unsigned char kNotificationData[] = {0xdf, 0xff, 0x0, 0x0, 0xff, 0xdf};
+const char kAction1Name[] = "btn1";
+const char kAction1Title[] = "Button 1";
+const char kAction1IconUrl[] = "https://example.com/action_icon_1.png";
+const char kAction1Placeholder[] = "Run into the... friendliness pellets.";
+const char kAction2Name[] = "btn2";
+const char kAction2Title[] = "Button 2";
+const char kAction2IconUrl[] = "https://example.com/action_icon_2.png";
+
+TEST(NotificationDataConversionsTest, ToWebNotificationData) {
+  std::vector<int> vibration_pattern(
+      kNotificationVibrationPattern,
+      kNotificationVibrationPattern +
+          base::size(kNotificationVibrationPattern));
+
+  std::vector<char> developer_data(
+      kNotificationData, kNotificationData + base::size(kNotificationData));
+
+  PlatformNotificationData platform_data;
+  platform_data.title = base::ASCIIToUTF16(kNotificationTitle);
+  platform_data.direction = PlatformNotificationData::DIRECTION_LEFT_TO_RIGHT;
+  platform_data.lang = kNotificationLang;
+  platform_data.body = base::ASCIIToUTF16(kNotificationBody);
+  platform_data.tag = kNotificationTag;
+  platform_data.image = GURL(kNotificationImageUrl);
+  platform_data.icon = GURL(kNotificationIconUrl);
+  platform_data.badge = GURL(kNotificationBadgeUrl);
+  platform_data.vibration_pattern = vibration_pattern;
+  platform_data.timestamp = base::Time::FromJsTime(kNotificationTimestamp);
+  platform_data.renotify = true;
+  platform_data.silent = true;
+  platform_data.require_interaction = true;
+  platform_data.data = developer_data;
+  platform_data.actions.resize(2);
+  platform_data.actions[0].type = PLATFORM_NOTIFICATION_ACTION_TYPE_BUTTON;
+  platform_data.actions[0].action = kAction1Name;
+  platform_data.actions[0].title = base::ASCIIToUTF16(kAction1Title);
+  platform_data.actions[0].icon = GURL(kAction1IconUrl);
+  platform_data.actions[0].placeholder =
+      base::NullableString16(base::ASCIIToUTF16(kAction1Placeholder), false);
+  platform_data.actions[1].type = PLATFORM_NOTIFICATION_ACTION_TYPE_TEXT;
+  platform_data.actions[1].action = kAction2Name;
+  platform_data.actions[1].title = base::ASCIIToUTF16(kAction2Title);
+  platform_data.actions[1].icon = GURL(kAction2IconUrl);
+  platform_data.actions[1].placeholder = base::NullableString16();
+
+  WebNotificationData web_data = ToWebNotificationData(platform_data);
+  EXPECT_EQ(kNotificationTitle, web_data.title);
+  EXPECT_EQ(blink::mojom::NotificationDirection::LEFT_TO_RIGHT,
+            web_data.direction);
+  EXPECT_EQ(kNotificationLang, web_data.lang);
+  EXPECT_EQ(kNotificationBody, web_data.body);
+  EXPECT_EQ(kNotificationTag, web_data.tag);
+  EXPECT_EQ(kNotificationImageUrl, web_data.image.GetString());
+  EXPECT_EQ(kNotificationIconUrl, web_data.icon.GetString());
+  EXPECT_EQ(kNotificationBadgeUrl, web_data.badge.GetString());
+
+  ASSERT_EQ(vibration_pattern.size(), web_data.vibrate.size());
+  for (size_t i = 0; i < vibration_pattern.size(); ++i)
+    EXPECT_EQ(vibration_pattern[i], web_data.vibrate[i]);
+
+  EXPECT_DOUBLE_EQ(kNotificationTimestamp, web_data.timestamp);
+  EXPECT_TRUE(web_data.renotify);
+  EXPECT_TRUE(web_data.silent);
+  EXPECT_TRUE(web_data.require_interaction);
+
+  ASSERT_EQ(developer_data.size(), web_data.data.size());
+  for (size_t i = 0; i < developer_data.size(); ++i)
+    EXPECT_EQ(developer_data[i], web_data.data[i]);
+
+  ASSERT_EQ(platform_data.actions.size(), web_data.actions.size());
+  EXPECT_EQ(WebNotificationAction::kButton, web_data.actions[0].type);
+  EXPECT_EQ(kAction1Name, web_data.actions[0].action);
+  EXPECT_EQ(kAction1Title, web_data.actions[0].title);
+  EXPECT_EQ(kAction1IconUrl, web_data.actions[0].icon.GetString());
+  EXPECT_EQ(kAction1Placeholder, web_data.actions[0].placeholder);
+  EXPECT_EQ(WebNotificationAction::kText, web_data.actions[1].type);
+  EXPECT_EQ(kAction2Name, web_data.actions[1].action);
+  EXPECT_EQ(kAction2Title, web_data.actions[1].title);
+  EXPECT_EQ(kAction2IconUrl, web_data.actions[1].icon.GetString());
+  EXPECT_TRUE(web_data.actions[1].placeholder.IsNull());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/exported/platform.cc b/third_party/blink/renderer/platform/exported/platform.cc
index fcea591..7b1643a7 100644
--- a/third_party/blink/renderer/platform/exported/platform.cc
+++ b/third_party/blink/renderer/platform/exported/platform.cc
@@ -61,7 +61,7 @@
 #include "third_party/blink/renderer/platform/instrumentation/resource_coordinator/renderer_resource_coordinator.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h"
 #include "third_party/blink/renderer/platform/language.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 #include "third_party/blink/renderer/platform/partition_alloc_memory_dump_provider.h"
 #include "third_party/blink/renderer/platform/scheduler/common/simple_thread_scheduler.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
@@ -186,7 +186,7 @@
   Thread::SetMainThread(std::move(main_thread));
 
   ProcessHeap::Init();
-  MemoryCoordinator::Initialize();
+  MemoryPressureListenerRegistry::Initialize();
   if (base::ThreadTaskRunnerHandle::IsSet()) {
     base::trace_event::MemoryDumpProvider::Options options;
     base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
diff --git a/third_party/blink/renderer/platform/exported/web_memory_coordinator.cc b/third_party/blink/renderer/platform/exported/web_memory_coordinator.cc
deleted file mode 100644
index b8a5c236..0000000
--- a/third_party/blink/renderer/platform/exported/web_memory_coordinator.cc
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright (c) 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/public/platform/web_memory_coordinator.h"
-
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
-
-namespace blink {
-
-void WebMemoryCoordinator::OnMemoryPressure(
-    WebMemoryPressureLevel pressure_level) {
-  MemoryCoordinator::Instance().OnMemoryPressure(pressure_level);
-}
-
-void WebMemoryCoordinator::OnMemoryStateChange(MemoryState state) {
-  MemoryCoordinator::Instance().OnMemoryStateChange(state);
-}
-
-void WebMemoryCoordinator::OnPurgeMemory() {
-  MemoryCoordinator::Instance().OnPurgeMemory();
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/exported/web_memory_pressure_listener.cc b/third_party/blink/renderer/platform/exported/web_memory_pressure_listener.cc
new file mode 100644
index 0000000..1ac7037
--- /dev/null
+++ b/third_party/blink/renderer/platform/exported/web_memory_pressure_listener.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/public/platform/web_memory_pressure_listener.h"
+
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
+
+namespace blink {
+
+void WebMemoryPressureListener::OnMemoryPressure(
+    WebMemoryPressureLevel pressure_level) {
+  MemoryPressureListenerRegistry::Instance().OnMemoryPressure(pressure_level);
+}
+
+void WebMemoryPressureListener::OnPurgeMemory() {
+  MemoryPressureListenerRegistry::Instance().OnPurgeMemory();
+}
+
+}  // namespace blink
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 4e8379a..7064f86a2 100644
--- a/third_party/blink/renderer/platform/exported/web_runtime_features.cc
+++ b/third_party/blink/renderer/platform/exported/web_runtime_features.cc
@@ -335,6 +335,11 @@
   RuntimeEnabledFeatures::SetPaymentRequestEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnablePaymentRequestHasEnrolledInstrument(
+    bool enable) {
+  RuntimeEnabledFeatures::SetPaymentRequestHasEnrolledInstrumentEnabled(enable);
+}
+
 void WebRuntimeFeatures::EnablePermissionsAPI(bool enable) {
   RuntimeEnabledFeatures::SetPermissionsEnabled(enable);
 }
@@ -609,4 +614,16 @@
   RuntimeEnabledFeatures::SetForbidSyncXHRInPageDismissalEnabled(enable);
 }
 
+void WebRuntimeFeatures::EnableShadowDOMV0(bool enable) {
+  RuntimeEnabledFeatures::SetShadowDOMV0Enabled(enable);
+}
+
+void WebRuntimeFeatures::EnableCustomElementsV0(bool enable) {
+  RuntimeEnabledFeatures::SetCustomElementsV0Enabled(enable);
+}
+
+void WebRuntimeFeatures::EnableHTMLImports(bool enable) {
+  RuntimeEnabledFeatures::SetHTMLImportsEnabled(enable);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/font_cache.cc b/third_party/blink/renderer/platform/fonts/font_cache.cc
index 92d453b..d8b2c1c 100644
--- a/third_party/blink/renderer/platform/fonts/font_cache.cc
+++ b/third_party/blink/renderer/platform/fonts/font_cache.cc
@@ -35,6 +35,7 @@
 #include "base/debug/alias.h"
 #include "base/memory/ptr_util.h"
 #include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/platform/font_family_names.h"
@@ -134,7 +135,7 @@
 
   {
     // addResult's scope must end before we recurse for alternate family names
-    // below, to avoid trigering its dtor hash-changed asserts.
+    // below, to avoid triggering its dtor hash-changed asserts.
     SizedFontPlatformDataSet* sized_fonts =
         &font_platform_data_cache_.insert(key, SizedFontPlatformDataSet())
              .stored_value->value;
@@ -190,6 +191,8 @@
     const FontDescription& font_description,
     const FontFaceCreationParams& creation_params,
     float font_size) {
+  TRACE_EVENT0("ui", "FontCache::ScaleFontPlatformData");
+
 #if defined(OS_MACOSX)
   return CreateFontPlatformData(font_description, creation_params, font_size);
 #else
@@ -308,6 +311,7 @@
 }
 
 void FontCache::PurgePlatformFontDataCache() {
+  TRACE_EVENT0("ui", "FontCache::PurgePlatformFontDataCache");
   Vector<FontCacheKey> keys_to_remove;
   keys_to_remove.ReserveInitialCapacity(font_platform_data_cache_.size());
   for (auto& sized_fonts : font_platform_data_cache_) {
@@ -326,6 +330,7 @@
 }
 
 void FontCache::PurgeFallbackListShaperCache() {
+  TRACE_EVENT0("ui", "FontCache::PurgeFallbackListShaperCache");
   unsigned items = 0;
   FallbackListShaperCache::iterator iter;
   for (iter = fallback_list_shaper_cache_.begin();
@@ -345,7 +350,7 @@
 void FontCache::Purge(PurgeSeverity purge_severity) {
   // Ideally we should never be forcing the purge while the
   // FontCachePurgePreventer is in scope, but we call purge() at any timing
-  // via MemoryCoordinator.
+  // via MemoryPressureListenerRegistry.
   if (purge_prevent_count_)
     return;
 
@@ -372,6 +377,7 @@
 }
 
 void FontCache::Invalidate() {
+  TRACE_EVENT0("ui", "FontCache::Invalidate");
   font_platform_data_cache_.clear();
   generation_++;
 
diff --git a/third_party/blink/renderer/platform/fonts/font_global_context.h b/third_party/blink/renderer/platform/fonts/font_global_context.h
index 7cd6a0b..d3308f5 100644
--- a/third_party/blink/renderer/platform/fonts/font_global_context.h
+++ b/third_party/blink/renderer/platform/fonts/font_global_context.h
@@ -43,7 +43,7 @@
 
   static FontUniqueNameLookup* GetFontUniqueNameLookup();
 
-  // Called by MemoryCoordinator to clear memory.
+  // Called by MemoryPressureListenerRegistry to clear memory.
   static void ClearMemory();
 
  private:
diff --git a/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc b/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc
index 0432f68..fbe65733 100644
--- a/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc
+++ b/third_party/blink/renderer/platform/fonts/script_run_iterator_test.cc
@@ -390,9 +390,9 @@
     : public ScriptRunIteratorTest,
       public testing::WithParamInterface<JapaneseMixedScript> {};
 
-INSTANTIATE_TEST_CASE_P(ScriptRunIteratorTest,
-                        JapaneseMixedScriptTest,
-                        testing::ValuesIn(japanese_mixed_scripts));
+INSTANTIATE_TEST_SUITE_P(ScriptRunIteratorTest,
+                         JapaneseMixedScriptTest,
+                         testing::ValuesIn(japanese_mixed_scripts));
 
 TEST_P(JapaneseMixedScriptTest, Data) {
   const auto& data = GetParam();
diff --git a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
index 8767c55..71815fab7 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/harfbuzz_shaper_test.cc
@@ -171,10 +171,10 @@
   }
 };
 
-INSTANTIATE_TEST_CASE_P(HarfBuzzShaperTest,
-                        ShapeParameterTest,
-                        testing::Values(TextDirection::kLtr,
-                                        TextDirection::kRtl));
+INSTANTIATE_TEST_SUITE_P(HarfBuzzShaperTest,
+                         ShapeParameterTest,
+                         testing::Values(TextDirection::kLtr,
+                                         TextDirection::kRtl));
 
 TEST_F(HarfBuzzShaperTest, MutableUnique) {
   scoped_refptr<ShapeResult> result =
@@ -532,14 +532,14 @@
 class ShapeStringTest : public HarfBuzzShaperTest,
                         public testing::WithParamInterface<const char16_t*> {};
 
-INSTANTIATE_TEST_CASE_P(HarfBuzzShaperTest,
-                        ShapeStringTest,
-                        testing::Values(
-                            // U+FFF0 is not assigned as of Unicode 10.0.
-                            u"\uFFF0",
-                            u"\uFFF0Hello",
-                            // U+00AD SOFT HYPHEN often does not have glyphs.
-                            u"\u00AD"));
+INSTANTIATE_TEST_SUITE_P(HarfBuzzShaperTest,
+                         ShapeStringTest,
+                         testing::Values(
+                             // U+FFF0 is not assigned as of Unicode 10.0.
+                             u"\uFFF0",
+                             u"\uFFF0Hello",
+                             // U+00AD SOFT HYPHEN often does not have glyphs.
+                             u"\u00AD"));
 
 TEST_P(ShapeStringTest, MissingGlyph) {
   String string(GetParam());
@@ -741,9 +741,9 @@
     : public HarfBuzzShaperTest,
       public testing::WithParamInterface<GlyphDataRangeTestData> {};
 
-INSTANTIATE_TEST_CASE_P(HarfBuzzShaperTest,
-                        GlyphDataRangeTest,
-                        testing::ValuesIn(glyph_data_range_test_data));
+INSTANTIATE_TEST_SUITE_P(HarfBuzzShaperTest,
+                         GlyphDataRangeTest,
+                         testing::ValuesIn(glyph_data_range_test_data));
 
 TEST_P(GlyphDataRangeTest, Data) {
   auto data = GetParam();
@@ -803,7 +803,7 @@
     : public HarfBuzzShaperTest,
       public testing::WithParamInterface<OffsetForPositionTestData> {};
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     HarfBuzzShaperTest,
     OffsetForPositionTest,
     testing::ValuesIn(offset_for_position_fixed_pitch_test_data));
@@ -880,7 +880,7 @@
     : public HarfBuzzShaperTest,
       public ::testing::WithParamInterface<IncludePartialGlyphsOption> {};
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     HarfBuzzShaperTest,
     IncludePartialGlyphsTest,
     ::testing::Values(IncludePartialGlyphsOption::OnlyFullGlyphs,
@@ -1091,9 +1091,9 @@
     : public HarfBuzzShaperTest,
       public testing::WithParamInterface<ShapeResultCopyRangeTestData> {};
 
-INSTANTIATE_TEST_CASE_P(HarfBuzzShaperTest,
-                        ShapeResultCopyRangeTest,
-                        testing::ValuesIn(shape_result_copy_range_test_data));
+INSTANTIATE_TEST_SUITE_P(HarfBuzzShaperTest,
+                         ShapeResultCopyRangeTest,
+                         testing::ValuesIn(shape_result_copy_range_test_data));
 
 // Split a ShapeResult and combine them should match to the original result.
 TEST_P(ShapeResultCopyRangeTest, Split) {
diff --git a/third_party/blink/renderer/platform/fonts/shaping/run_segmenter_test.cc b/third_party/blink/renderer/platform/fonts/shaping/run_segmenter_test.cc
index 20c9d58..480e1f7f 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/run_segmenter_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/run_segmenter_test.cc
@@ -192,10 +192,10 @@
         FontFallbackPriority::kEmojiEmoji},
        {"abcd", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
         FontFallbackPriority::kText},
-       {"👩‍👩‍", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
+       {"👩‍👩", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
         FontFallbackPriority::kEmojiEmoji},
-       {"efg", USCRIPT_LATIN, OrientationIterator::kOrientationKeep,
-        FontFallbackPriority::kText}});
+       {u8"\U0000200D‍efg", USCRIPT_LATIN,
+        OrientationIterator::kOrientationKeep, FontFallbackPriority::kText}});
 }
 
 TEST_F(RunSegmenterTest, JapaneseLetterlikeEnd) {
@@ -243,10 +243,8 @@
 
 TEST_F(RunSegmenterTest, NonEmojiPresentationSymbols) {
   CheckRunsHorizontal(
-      {{u8"\U00002626\U0000262a\U00002638\U0000271d\U00002721", USCRIPT_COMMON,
-        OrientationIterator::kOrientationKeep,
-        FontFallbackPriority::kEmojiText},
-       {u8"\U00002627\U00002628\U00002629\U0000262b\U0000262c\U00002670"
+      {{u8"\U00002626\U0000262a\U00002638\U0000271d\U00002721\U00002627"
+        u8"\U00002628\U00002629\U0000262b\U0000262c\U00002670"
         "\U00002671\U0000271f\U00002720",
         USCRIPT_COMMON, OrientationIterator::kOrientationKeep,
         FontFallbackPriority::kText}});
diff --git a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc
index 2e05f33..82eeefad 100644
--- a/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc
+++ b/third_party/blink/renderer/platform/fonts/shaping/shaping_line_breaker_test.cc
@@ -333,7 +333,7 @@
     : public ShapingLineBreakerTest,
       public testing::WithParamInterface<BreakOpportunityTestData> {};
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     ShapingLineBreakerTest,
     BreakOpportunityTest,
     testing::Values(BreakOpportunityTestData{u"x y z", {1, 3, 5}},
diff --git a/third_party/blink/renderer/platform/fonts/symbols_iterator.cc b/third_party/blink/renderer/platform/fonts/symbols_iterator.cc
index d24adf3f..83420c1 100644
--- a/third_party/blink/renderer/platform/fonts/symbols_iterator.cc
+++ b/third_party/blink/renderer/platform/fonts/symbols_iterator.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "third_party/blink/renderer/platform/fonts/symbols_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h"
 
 #include <unicode/uchar.h>
 #include <unicode/uniset.h>
@@ -10,122 +11,49 @@
 
 namespace blink {
 
+namespace {
+using emoji_text_iter_t = UTF16RagelIterator;
+// Scanner gode generated by Ragel and imported from third_party.
+#include "third_party/emoji-segmenter/src/emoji_presentation_scanner.c"
+}  // namespace
+
 SymbolsIterator::SymbolsIterator(const UChar* buffer, unsigned buffer_size)
-    : utf16_iterator_(std::make_unique<UTF16TextIterator>(buffer, buffer_size)),
-      buffer_size_(buffer_size),
-      next_char_(0),
-      at_end_(buffer_size == 0),
-      current_font_fallback_priority_(FontFallbackPriority::kInvalid) {}
-
-FontFallbackPriority SymbolsIterator::FontFallbackPriorityForCharacter(
-    UChar32 codepoint) {
-  // Those should only be Emoji presentation as combinations of two.
-  if (Character::IsEmojiKeycapBase(codepoint) ||
-      Character::IsRegionalIndicator(codepoint))
-    return FontFallbackPriority::kText;
-
-  if (codepoint == kCombiningEnclosingKeycapCharacter)
-    return FontFallbackPriority::kEmojiEmoji;
-
-  if (Character::IsEmojiEmojiDefault(codepoint) ||
-      Character::IsEmojiModifierBase(codepoint) ||
-      Character::IsModifier(codepoint))
-    return FontFallbackPriority::kEmojiEmoji;
-
-  if (Character::IsEmojiTextDefault(codepoint))
-    return FontFallbackPriority::kEmojiText;
-
-  // Here we could segment into Symbols and Math categories as well, similar
-  // to what the Windows font fallback does. Map the math Unicode and Symbols
-  // blocks to Text for now since we don't have a good cross-platform way to
-  // select suitable math fonts.
-  return FontFallbackPriority::kText;
+    : cursor_(0), next_token_end_(0), next_token_emoji_(false) {
+  if (buffer_size) {
+    buffer_iterator_ = UTF16RagelIterator(buffer, buffer_size, cursor_);
+    next_token_end_ = cursor_ + (scan_emoji_presentation(buffer_iterator_,
+                                                         buffer_iterator_.end(),
+                                                         &next_token_emoji_) -
+                                 buffer_iterator_);
+  }
 }
 
 bool SymbolsIterator::Consume(unsigned* symbols_limit,
                               FontFallbackPriority* font_fallback_priority) {
-  if (at_end_)
+  if (cursor_ >= buffer_iterator_.end().Cursor())
     return false;
 
-  while (utf16_iterator_->Consume(next_char_)) {
-    previous_font_fallback_priority_ = current_font_fallback_priority_;
-    unsigned iterator_offset = utf16_iterator_->Offset();
-    utf16_iterator_->Advance();
+  bool current_token_emoji = false;
+  do {
+    cursor_ = next_token_end_;
+    current_token_emoji = next_token_emoji_;
 
-    // Except at the beginning, ZWJ just carries over the emoji or neutral
-    // text type, VS15 & VS16 we just carry over as well, since we already
-    // resolved those through lookahead. Also, don't downgrade to text
-    // presentation for emoji that are part of a ZWJ sequence, example
-    // U+1F441 U+200D U+1F5E8, eye (text presentation) + ZWJ + left speech
-    // bubble, see below.
-    if ((!(next_char_ == kZeroWidthJoinerCharacter &&
-           previous_font_fallback_priority_ ==
-               FontFallbackPriority::kEmojiEmoji) &&
-         next_char_ != kVariationSelector15Character &&
-         next_char_ != kVariationSelector16Character &&
-         next_char_ != kCombiningEnclosingCircleBackslashCharacter &&
-         !Character::IsRegionalIndicator(next_char_) &&
-         !((next_char_ == kLeftSpeechBubbleCharacter ||
-            next_char_ == kRainbowCharacter ||
-            next_char_ == kMaleSignCharacter ||
-            next_char_ == kFemaleSignCharacter ||
-            next_char_ == kStaffOfAesculapiusCharacter) &&
-           previous_font_fallback_priority_ ==
-               FontFallbackPriority::kEmojiEmoji) &&
-         !Character::IsEmojiTagSequence(next_char_) &&
-         kCancelTag != next_char_) ||
-        current_font_fallback_priority_ == FontFallbackPriority::kInvalid) {
-      current_font_fallback_priority_ =
-          FontFallbackPriorityForCharacter(next_char_);
-    }
+    if (cursor_ >= buffer_iterator_.end().Cursor())
+      break;
 
-    UChar32 peek_char = 0;
-    if (utf16_iterator_->Consume(peek_char) && peek_char != 0) {
-      // Variation Selectors
-      if (current_font_fallback_priority_ ==
-              FontFallbackPriority::kEmojiEmoji &&
-          peek_char == kVariationSelector15Character) {
-        current_font_fallback_priority_ = FontFallbackPriority::kEmojiText;
-      }
+    buffer_iterator_.SetCursor(cursor_);
+    next_token_end_ = cursor_ + (scan_emoji_presentation(buffer_iterator_,
+                                                         buffer_iterator_.end(),
+                                                         &next_token_emoji_) -
+                                 buffer_iterator_);
 
-      if ((current_font_fallback_priority_ ==
-               FontFallbackPriority::kEmojiText ||
-           Character::IsEmojiKeycapBase(next_char_)) &&
-          peek_char == kVariationSelector16Character) {
-        current_font_fallback_priority_ = FontFallbackPriority::kEmojiEmoji;
-      }
+  } while (current_token_emoji == next_token_emoji_);
 
-      // Combining characters Keycap...
-      if (Character::IsEmojiKeycapBase(next_char_) &&
-          peek_char == kCombiningEnclosingKeycapCharacter) {
-        current_font_fallback_priority_ = FontFallbackPriority::kEmojiEmoji;
-      };
+  *font_fallback_priority = current_token_emoji
+                                ? FontFallbackPriority::kEmojiEmoji
+                                : FontFallbackPriority::kText;
+  *symbols_limit = cursor_;
 
-      // Regional indicators
-      if (Character::IsRegionalIndicator(next_char_) &&
-          Character::IsRegionalIndicator(peek_char)) {
-        current_font_fallback_priority_ = FontFallbackPriority::kEmojiEmoji;
-      }
-
-      // Upgrade text presentation emoji to emoji presentation when followed by
-      // ZWJ, Example U+1F441 U+200D U+1F5E8, eye + ZWJ + left speech bubble.
-      if ((next_char_ == kEyeCharacter ||
-           next_char_ == kWavingWhiteFlagCharacter) &&
-          peek_char == kZeroWidthJoinerCharacter) {
-        current_font_fallback_priority_ = FontFallbackPriority::kEmojiEmoji;
-      }
-    }
-
-    if (previous_font_fallback_priority_ != current_font_fallback_priority_ &&
-        (previous_font_fallback_priority_ != FontFallbackPriority::kInvalid)) {
-      *symbols_limit = iterator_offset;
-      *font_fallback_priority = previous_font_fallback_priority_;
-      return true;
-    }
-  }
-  *symbols_limit = buffer_size_;
-  *font_fallback_priority = current_font_fallback_priority_;
-  at_end_ = true;
   return true;
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/symbols_iterator.h b/third_party/blink/renderer/platform/fonts/symbols_iterator.h
index 7484c126..c6ea4ff 100644
--- a/third_party/blink/renderer/platform/fonts/symbols_iterator.h
+++ b/third_party/blink/renderer/platform/fonts/symbols_iterator.h
@@ -7,9 +7,7 @@
 
 #include <memory>
 #include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h"
-#include "third_party/blink/renderer/platform/fonts/font_orientation.h"
-#include "third_party/blink/renderer/platform/fonts/script_run_iterator.h"
-#include "third_party/blink/renderer/platform/fonts/utf16_text_iterator.h"
+#include "third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
 #include "third_party/blink/renderer/platform/wtf/noncopyable.h"
 
@@ -25,17 +23,13 @@
   bool Consume(unsigned* symbols_limit, FontFallbackPriority*);
 
  private:
-  FontFallbackPriority FontFallbackPriorityForCharacter(UChar32);
+  UTF16RagelIterator buffer_iterator_;
+  unsigned cursor_;
 
-  std::unique_ptr<UTF16TextIterator> utf16_iterator_;
-  unsigned buffer_size_;
-  UChar32 next_char_;
-  bool at_end_;
-
-  FontFallbackPriority current_font_fallback_priority_;
-  FontFallbackPriority previous_font_fallback_priority_;
+  unsigned next_token_end_;
+  bool next_token_emoji_;
 };
 
 }  // namespace blink
 
-#endif
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_SYMBOLS_ITERATOR_H_
diff --git a/third_party/blink/renderer/platform/fonts/symbols_iterator_test.cc b/third_party/blink/renderer/platform/fonts/symbols_iterator_test.cc
index 7abfaf8..3ec12d4 100644
--- a/third_party/blink/renderer/platform/fonts/symbols_iterator_test.cc
+++ b/third_party/blink/renderer/platform/fonts/symbols_iterator_test.cc
@@ -4,9 +4,12 @@
 
 #include "third_party/blink/renderer/platform/fonts/symbols_iterator.h"
 
-#include "testing/gtest/include/gtest/gtest.h"
 #include <string>
 
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
 namespace blink {
 
 struct FallbackTestRun {
@@ -71,10 +74,14 @@
   CheckRuns({{"Aa", FontFallbackPriority::kText}});
 }
 
+TEST_F(SymbolsIteratorTest, BMPEmoji) {
+  CheckRuns({{"⌚⌛⌚⌛⌚⌛⌚⌛", FontFallbackPriority::kEmojiEmoji}});
+}
+
 TEST_F(SymbolsIteratorTest, LatinColorEmojiTextEmoji) {
   CheckRuns({{"a", FontFallbackPriority::kText},
              {"⌚", FontFallbackPriority::kEmojiEmoji},
-             {"☎", FontFallbackPriority::kEmojiText}});
+             {"☎", FontFallbackPriority::kText}});
 }
 
 TEST_F(SymbolsIteratorTest, IgnoreVSInMath) {
@@ -97,16 +104,37 @@
 
 TEST_F(SymbolsIteratorTest, NumbersAndHashNormalAndEmoji) {
   CheckRuns({{"0123456789#*", FontFallbackPriority::kText},
-             {"0⃣1⃣2⃣3⃣4⃣5⃣6⃣7⃣8⃣9⃣*⃣", FontFallbackPriority::kEmojiEmoji},
+             {"0\uFE0F⃣1\uFE0F⃣2\uFE0F⃣3\uFE0F⃣4\uFE0F⃣5\uFE0F⃣6\uFE0F⃣7\uFE0F⃣8\uFE0F⃣9"
+              "\uFE0F⃣*\uFE0F⃣",
+              FontFallbackPriority::kEmojiEmoji},
              {"0123456789#*", FontFallbackPriority::kText}});
 }
 
 TEST_F(SymbolsIteratorTest, VS16onDigits) {
   CheckRuns({{"#", FontFallbackPriority::kText},
-             {"#\uFE0F#\uFE0F\u20E3", FontFallbackPriority::kEmojiEmoji},
+             {"#\uFE0F\u20E3", FontFallbackPriority::kEmojiEmoji},
              {"#", FontFallbackPriority::kText}});
 }
 
+TEST_F(SymbolsIteratorTest, EmojiVS15AndVS16) {
+  CheckRuns({{u8"\U0001F642", FontFallbackPriority::kEmojiEmoji},
+             {u8"\U0001F642\U0000FE0E", FontFallbackPriority::kText},
+             {u8"\U0001F642\U0000FE0F", FontFallbackPriority::kEmojiEmoji}});
+}
+
+TEST_F(SymbolsIteratorTest, MultipleMisplacedVS) {
+  CheckRuns({
+      {u8"\U0000FE0E\U0000FE0F", FontFallbackPriority::kText},
+      {u8"\U0001F642\U0000FE0F", FontFallbackPriority::kEmojiEmoji},
+      {u8"\U0001F642\U0000FE0E", FontFallbackPriority::kText},
+      {u8"\U0001F642\U0000FE0F", FontFallbackPriority::kEmojiEmoji},
+      {u8"\U0000FE0E\U0000FE0F", FontFallbackPriority::kText},
+      {u8"\U0001F642\U0000FE0F", FontFallbackPriority::kEmojiEmoji},
+      {u8"\U0001F642\U0000FE0E\U0000FE0E\U0000FE0F",
+       FontFallbackPriority::kText},
+  });
+}
+
 TEST_F(SymbolsIteratorTest, SingleFlag) {
   CheckRuns({{"🇺", FontFallbackPriority::kText}});
 }
@@ -117,33 +145,33 @@
 
 TEST_F(SymbolsIteratorTest, CombiningEnclosingCircleBackslash) {
   CheckRuns({{"A⃠B⃠C⃠", FontFallbackPriority::kText},
-             {"🚷🚯🚱🔞📵🚭🚫", FontFallbackPriority::kEmojiEmoji},
-             {"🎙⃠", FontFallbackPriority::kEmojiText},
-             {"📸⃠🔫⃠", FontFallbackPriority::kEmojiEmoji},
+             {"🚷🚯🚱🔞📵🚭🚫🎙⃠📸⃠🔫⃠",
+              FontFallbackPriority::kEmojiEmoji},
              {"a⃠b⃠c⃠", FontFallbackPriority::kText}});
 }
 
 // TODO: Perhaps check for invalid country indicator combinations?
 
 TEST_F(SymbolsIteratorTest, FlagsVsNonFlags) {
-  CheckRuns({{"🇺🇸🇸", FontFallbackPriority::kEmojiEmoji},  // "US"
-             {"abc", FontFallbackPriority::kText},
+  CheckRuns({{"🇺🇸", FontFallbackPriority::kEmojiEmoji},  // "US"
+             {"🇸abc", FontFallbackPriority::kText},
              {"🇺🇸", FontFallbackPriority::kEmojiEmoji},
              {"a🇿", FontFallbackPriority::kText}});
 }
 
 TEST_F(SymbolsIteratorTest, EmojiVS15) {
   // A VS15 after the anchor must trigger text display.
-  CheckRuns({{"⚓\U0000FE0E", FontFallbackPriority::kEmojiText},
+  CheckRuns({{"⚓\U0000FE0E", FontFallbackPriority::kText},
              {"⛵", FontFallbackPriority::kEmojiEmoji}});
 }
 
 TEST_F(SymbolsIteratorTest, EmojiZWSSequences) {
-  CheckRuns({{"👩‍👩‍👧‍👦👩‍❤️‍💋‍👨",
-              FontFallbackPriority::kEmojiEmoji},
-             {"abcd", FontFallbackPriority::kText},
-             {"👩‍👩‍", FontFallbackPriority::kEmojiEmoji},
-             {"efgh", FontFallbackPriority::kText}});
+  CheckRuns(
+      {{"👩‍👩‍👧‍👦👩‍❤️‍💋‍👨",
+        FontFallbackPriority::kEmojiEmoji},
+       {"abcd", FontFallbackPriority::kText},
+       {u8"\U0001F469\U0000200D\U0001F469", FontFallbackPriority::kEmojiEmoji},
+       {u8"\U0000200Defgh", FontFallbackPriority::kText}});
 }
 
 TEST_F(SymbolsIteratorTest, AllEmojiZWSSequences) {
@@ -170,7 +198,7 @@
 }
 
 TEST_F(SymbolsIteratorTest, TextMemberZwjSequence) {
-  CheckRuns({{"👨‍⚕", FontFallbackPriority::kEmojiEmoji}});
+  CheckRuns({{"👨‍⚕️", FontFallbackPriority::kEmojiEmoji}});
 }
 
 TEST_F(SymbolsIteratorTest, FacepalmCartwheelShrugModifierFemale) {
@@ -180,8 +208,7 @@
 
 TEST_F(SymbolsIteratorTest, AesculapiusMaleFemalEmoji) {
   // Emoji Data 4 has upgraded those three characters to Emoji.
-  CheckRuns({{"a", FontFallbackPriority::kText},
-             {"⚕♀♂", FontFallbackPriority::kEmojiText}});
+  CheckRuns({{"a⚕♀♂", FontFallbackPriority::kText}});
 }
 
 TEST_F(SymbolsIteratorTest, EyeSpeechBubble) {
@@ -203,6 +230,16 @@
               FontFallbackPriority::kEmojiEmoji}});
 }
 
+TEST_F(SymbolsIteratorTest, StrayZWJAndVS) {
+  CheckRuns({{u8"\U0000200D\U0000FE0E\U0000FE0E\U0000FE0E\U0000200D\U0000200D",
+              FontFallbackPriority::kText},
+             {u8"\U0001F469\U0000200D\U00002764\U0000FE0F\U0000200D\U0001F48B"
+              u8"\U0000200D\U0001F468",
+              FontFallbackPriority::kEmojiEmoji},
+             {u8"\U0000200D\U0000FE0E\U0000FE0E\U0000FE0E\U0000200D\U0000200D",
+              FontFallbackPriority::kText}});
+}
+
 TEST_F(SymbolsIteratorTest, Arrows) {
   CheckRuns({{"x→←x←↑↓→", FontFallbackPriority::kText}});
 }
@@ -212,6 +249,10 @@
               FontFallbackPriority::kEmojiEmoji}});
 }
 
+TEST_F(SymbolsIteratorTest, EmojiPunctuationText) {
+  CheckRuns({{"⁉⁉⁉⁈⁈⁈", FontFallbackPriority::kText}});
+}
+
 // Extracted from http://unicode.org/emoji/charts/emoji-released.html for Emoji
 // v5.0, except for the subdivision-flag section.
 // Before ICU 59 new emoji sequences and new single emoji are not detected as
@@ -331,9 +372,12 @@
 }
 
 TEST_F(SymbolsIteratorTest, EmojiSubdivisionFlags) {
-  CheckRuns(
-      {{"🏴󠁧󠁢󠁷󠁬󠁳󠁿🏴󠁧󠁢󠁳󠁣󠁴󠁿🏴󠁧󠁢",
-        FontFallbackPriority::kEmojiEmoji}});
+  CheckRuns({{u8"\U0001F3F4\U000E0067\U000E0062\U000E0077\U000E006C\U000E0073"
+              u8"\U000E007F\U0001F3F4\U000E0067\U000E0062\U000E0073\U000E0063"
+              u8"\U000E0074\U000E007F\U0001F3F4",
+              FontFallbackPriority::kEmojiEmoji},
+             // Tag sequences on their own do not mean they're emoji.
+             {u8"\U000E0067\U000E0062", FontFallbackPriority::kText}});
 }
 
 // Extracted from http://unicode.org/emoji/charts/emoji-released.html for Emoji
diff --git a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.cc b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.cc
index 45da419..a43813a 100644
--- a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.cc
+++ b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.cc
@@ -57,7 +57,17 @@
 
 }  // namespace
 
+UTF16RagelIterator& UTF16RagelIterator::SetCursor(unsigned new_cursor) {
+  CHECK_GE(new_cursor, 0u);
+  CHECK_LT(new_cursor, buffer_size_);
+  cursor_ = new_cursor;
+  UpdateCachedCategory();
+  return *this;
+}
+
 void UTF16RagelIterator::UpdateCachedCategory() {
+  if (cursor_ >= buffer_size_)
+    return;
   cached_category_ = EmojiSegmentationCategory(Codepoint());
 }
 
diff --git a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
index 26a0cdf..9784933 100644
--- a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
+++ b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator.h
@@ -39,7 +39,9 @@
     return ret;
   }
 
-  unsigned cursor() { return cursor_; }
+  UTF16RagelIterator& SetCursor(unsigned new_cursor);
+
+  unsigned Cursor() { return cursor_; }
 
   UTF16RagelIterator& operator+=(int v) {
     if (v > 0) {
diff --git a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator_test.cc b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator_test.cc
index bbe5036..46cd3d6 100644
--- a/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator_test.cc
+++ b/third_party/blink/renderer/platform/fonts/utf16_ragel_iterator_test.cc
@@ -59,7 +59,7 @@
       class_examples_unicode_string.length(),
       class_examples_unicode_string.length() - 1);
   size_t i = base::size(categories) - 1;
-  while (reverse_ragel_iterator.cursor() > 0) {
+  while (reverse_ragel_iterator.Cursor() > 0) {
     CHECK_EQ(categories[i], *reverse_ragel_iterator);
     i--;
     reverse_ragel_iterator--;
@@ -104,10 +104,33 @@
 
 TEST(UTF16RagelIteratorTest, InvalidOperationOnEmpty) {
   UTF16RagelIterator ragel_iterator;
-  CHECK_EQ(ragel_iterator.cursor(), 0u);
+  CHECK_EQ(ragel_iterator.Cursor(), 0u);
   EXPECT_DEATH_IF_SUPPORTED(ragel_iterator++, "");
   EXPECT_DEATH_IF_SUPPORTED(ragel_iterator--, "");
   EXPECT_DEATH_IF_SUPPORTED(*ragel_iterator, "");
 }
 
+TEST(UTF16RagelIteratorTest, CursorPositioning) {
+  UChar32 flags_codepoints[] = {0x1F99E, 0x1F99E, 0x1F99E,
+                                kLeftSpeechBubbleCharacter};
+
+  icu_63::UnicodeString flags_unicode_string = icu_63::UnicodeString::fromUTF32(
+      flags_codepoints, base::size(flags_codepoints));
+  UTF16RagelIterator ragel_iterator(
+      reinterpret_cast<const UChar*>(flags_unicode_string.getBuffer()),
+      flags_unicode_string.length());
+
+  CHECK_EQ(ragel_iterator.end().Cursor(), 8u);
+
+  CHECK_EQ(*ragel_iterator, UTF16RagelIterator::EMOJI_EMOJI_PRESENTATION);
+  CHECK_EQ(*(ragel_iterator.SetCursor(4)),
+           UTF16RagelIterator::EMOJI_EMOJI_PRESENTATION);
+  CHECK_EQ(*(ragel_iterator.SetCursor(6)),
+           UTF16RagelIterator::EMOJI_TEXT_PRESENTATION);
+
+  EXPECT_DEATH_IF_SUPPORTED(ragel_iterator.SetCursor(-1), "");
+  EXPECT_DEATH_IF_SUPPORTED(
+      ragel_iterator.SetCursor(ragel_iterator.end().Cursor()), "");
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc b/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc
index 99641e3..ec5d2f6 100644
--- a/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc
+++ b/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc
@@ -36,6 +36,7 @@
 
 #include "base/debug/alias.h"
 #include "base/stl_util.h"
+#include "base/trace_event/trace_event.h"
 #include "third_party/blink/renderer/platform/fonts/bitmap_glyphs_blacklist.h"
 #include "third_party/blink/renderer/platform/fonts/font_description.h"
 #include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
@@ -170,6 +171,8 @@
     UChar32 character,
     const SimpleFontData* original_font_data,
     FontFallbackPriority fallback_priority) {
+  TRACE_EVENT0("ui", "FontCache::PlatformFallbackFontForCharacter");
+
   // First try the specified font with standard style & weight.
   if (fallback_priority != FontFallbackPriority::kEmojiEmoji &&
       (font_description.Style() == ItalicSlopeValue() ||
@@ -395,6 +398,8 @@
     const FontFaceCreationParams& creation_params,
     float font_size,
     AlternateFontName alternate_font_name) {
+  TRACE_EVENT0("ui", "FontCache::CreateFontPlatformData");
+
   DCHECK_EQ(creation_params.CreationType(), kCreateFontByFamily);
   sk_sp<SkTypeface> typeface;
 
diff --git a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
index 25118e7..8b46875 100644
--- a/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
+++ b/third_party/blink/renderer/platform/graphics/bitmap_image_test.cc
@@ -831,7 +831,7 @@
         {"wrong-frame-dimensions.ico", BitmapImageMetrics::kImageICO},
         {"lenna.bmp", BitmapImageMetrics::kImageBMP}};
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     DecodedImageTypeHistogramTest,
     DecodedImageTypeHistogramTest,
     testing::ValuesIn(kDecodedImageTypeHistogramTestparams));
@@ -854,7 +854,7 @@
         {"exif-orientation-7-rl.jpg", kOriginRightBottom},
         {"exif-orientation-8-llo.jpg", kOriginLeftBottom}};
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     DecodedImageOrientationHistogramTest,
     DecodedImageOrientationHistogramTest,
     testing::ValuesIn(kDecodedImageOrientationHistogramTestParams));
@@ -877,7 +877,7 @@
         // 632x475 too big for the 100-399px range.
         {"cat.jpg", DecodedImageDensityHistogramTest100px::kNoSamplesReported}};
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     DecodedImageDensityHistogramTest100px,
     DecodedImageDensityHistogramTest100px,
     testing::ValuesIn(kDecodedImageDensityHistogramTest100pxParams));
@@ -899,7 +899,7 @@
         // 632x475, 68826 bytes --> 1.83
         {"cat.jpg", 183}};
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     DecodedImageDensityHistogramTest400px,
     DecodedImageDensityHistogramTest400px,
     testing::ValuesIn(kDecodedImageDensityHistogramTest400pxParams));
diff --git a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
index 21cba56..7520d04f 100644
--- a/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
+++ b/third_party/blink/renderer/platform/graphics/canvas_resource_dispatcher_test.cc
@@ -278,5 +278,5 @@
     {true, false},
     {true, true}};
 
-INSTANTIATE_TEST_CASE_P(, CanvasResourceDispatcherTest, ValuesIn(kTestCases));
+INSTANTIATE_TEST_SUITE_P(, CanvasResourceDispatcherTest, ValuesIn(kTestCases));
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.cc b/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.cc
index d5058e2b..adab4e36 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.cc
@@ -9,6 +9,17 @@
 
 namespace blink {
 
+ChunkToLayerMapper::ChunkToLayerMapper(
+    const PropertyTreeState& layer_state,
+    const gfx::Vector2dF& layer_offset,
+    const FloatSize& visual_rect_subpixel_offset)
+    : layer_state_(layer_state.Unalias()),
+      layer_offset_(layer_offset),
+      visual_rect_subpixel_offset_(visual_rect_subpixel_offset),
+      chunk_state_(layer_state_),
+      transform_(TransformationMatrix().Translate(-layer_offset.x(),
+                                                  -layer_offset.y())) {}
+
 void ChunkToLayerMapper::SwitchToChunk(const PaintChunk& chunk) {
   outset_for_raster_effects_ = chunk.outset_for_raster_effects;
 
diff --git a/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h b/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h
index b86d945..50591ca 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/chunk_to_layer_mapper.h
@@ -19,13 +19,10 @@
   DISALLOW_NEW();
 
  public:
-  ChunkToLayerMapper(const PropertyTreeState& layer_state,
-                     const gfx::Vector2dF& layer_offset,
-                     const FloatSize& visual_rect_subpixel_offset = FloatSize())
-      : layer_state_(layer_state.Unalias()),
-        layer_offset_(layer_offset),
-        visual_rect_subpixel_offset_(visual_rect_subpixel_offset),
-        chunk_state_(nullptr, nullptr, nullptr) {}
+  ChunkToLayerMapper(
+      const PropertyTreeState& layer_state,
+      const gfx::Vector2dF& layer_offset,
+      const FloatSize& visual_rect_subpixel_offset = FloatSize());
 
   // This class can map from multiple chunks. Before mapping from a chunk, this
   // method must be called to prepare for the chunk.
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
index 4916b79..c5918ef 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
@@ -27,7 +27,7 @@
       raster_invalidator_([this](const IntRect& rect) {
         cc_picture_layer_->SetNeedsDisplayRect(rect);
       }),
-      layer_state_(nullptr, nullptr, nullptr),
+      layer_state_(PropertyTreeState::Uninitialized()),
       weak_ptr_factory_(this) {
   cc_picture_layer_->SetLayerClient(weak_ptr_factory_.GetWeakPtr());
 }
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 973f8bd..07c61b6 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
@@ -802,6 +802,8 @@
   if (!NeedsUpdate())
     return;
 
+  TRACE_EVENT0("blink", "PaintArtifactCompositor::Update");
+
   // When using BlinkGenPropertyTrees, the compositor accepts a list of layers
   // and property trees instead of building property trees. This DCHECK ensures
   // we have not forgotten to set |use_layer_lists|.
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 a601fe2..c71db90d 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
@@ -55,6 +55,13 @@
   return transform;
 }
 
+void SetTransform(PaintChunk& chunk,
+                  const TransformPaintPropertyNode* transform) {
+  auto properties = chunk.properties.GetPropertyTreeState();
+  properties.SetTransform(transform);
+  chunk.properties = RefCountedPropertyTreeState(properties);
+}
+
 class FakeScrollClient {
  public:
   FakeScrollClient() : did_scroll_count(0) {}
@@ -291,7 +298,7 @@
   std::unique_ptr<LayerTreeHostEmbedder> layer_tree_;
 };
 
-INSTANTIATE_LAYER_LIST_TEST_CASE_P(PaintArtifactCompositorTest);
+INSTANTIATE_LAYER_LIST_TEST_SUITE_P(PaintArtifactCompositorTest);
 
 const auto kNotScrollingOnMain = MainThreadScrollingReason::kNotScrollingOnMain;
 
@@ -1839,7 +1846,7 @@
   auto transform = CreateTransform(
       t0(), TransformationMatrix().Translate(99, 0), FloatPoint3D(100, 100, 0));
   {
-    paint_chunk2.properties.SetTransform(transform.get());
+    SetTransform(paint_chunk2, transform.get());
     PendingLayer pending_layer2(paint_chunk2, 1, false);
     EXPECT_TRUE(MightOverlap(pending_layer, pending_layer2));
   }
@@ -1848,7 +1855,7 @@
       CreateTransform(t0(), TransformationMatrix().Translate(100, 0),
                       FloatPoint3D(100, 100, 0));
   {
-    paint_chunk2.properties.SetTransform(transform2.get());
+    SetTransform(paint_chunk2, transform2.get());
     PendingLayer pending_layer2(paint_chunk2, 1, false);
     EXPECT_FALSE(MightOverlap(pending_layer, pending_layer2));
   }
@@ -1903,7 +1910,7 @@
 
   PaintChunk chunk2 = DefaultChunk();
   chunk2.properties = chunk1.properties;
-  chunk2.properties.SetTransform(transform.get());
+  SetTransform(chunk2, transform.get());
   chunk2.bounds = FloatRect(0, 0, 50, 60);
   pending_layer.Merge(PendingLayer(chunk2, 1, false));
 
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc b/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
index c01f25e..d5a59e6 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer_test.cc
@@ -71,9 +71,9 @@
   ViewportLayersSetup layers_;
 };
 
-INSTANTIATE_TEST_CASE_P(All,
-                        GraphicsLayerTest,
-                        testing::Values(0, kBlinkGenPropertyTrees));
+INSTANTIATE_TEST_SUITE_P(All,
+                         GraphicsLayerTest,
+                         testing::Values(0, kBlinkGenPropertyTrees));
 
 TEST_P(GraphicsLayerTest, Paint) {
   IntRect interest_rect(1, 2, 3, 4);
diff --git a/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc b/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc
index 771392a..1a2edede 100644
--- a/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/display_item_raster_invalidator_test.cc
@@ -43,7 +43,7 @@
   RasterInvalidator invalidator_;
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(DisplayItemRasterInvalidatorTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(DisplayItemRasterInvalidatorTest);
 
 TEST_P(DisplayItemRasterInvalidatorTest, RemoveItemInMiddle) {
   FakeDisplayItemClient first("first", LayoutRect(100, 100, 300, 300));
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
index 914e5dbd..7417a98b 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper.cc
@@ -253,8 +253,9 @@
     OverlayScrollbarClipBehavior clip_behavior,
     InclusiveIntersectOrNot inclusive_behavior,
     bool& success) {
-  PropertyTreeState last_transform_and_clip_state(local_state.Transform(),
-                                                  local_state.Clip(), nullptr);
+  PropertyTreeState last_transform_and_clip_state(
+      local_state.Transform(), local_state.Clip(),
+      &EffectPaintPropertyNode::Root());
 
   auto* ancestor_effect = ancestor_state.Effect()->Unalias();
   for (const auto* effect = local_state.Effect()->Unalias();
@@ -264,8 +265,9 @@
       continue;
 
     DCHECK(effect->OutputClip());
-    PropertyTreeState transform_and_clip_state(effect->LocalTransformSpace(),
-                                               effect->OutputClip(), nullptr);
+    PropertyTreeState transform_and_clip_state(
+        effect->LocalTransformSpace(), effect->OutputClip(),
+        &EffectPaintPropertyNode::Root());
     bool intersects = LocalToAncestorVisualRectInternal(
         last_transform_and_clip_state, transform_and_clip_state, mapping_rect,
         clip_behavior, inclusive_behavior, success);
@@ -280,7 +282,8 @@
   }
 
   PropertyTreeState final_transform_and_clip_state(
-      ancestor_state.Transform(), ancestor_state.Clip(), nullptr);
+      ancestor_state.Transform(), ancestor_state.Clip(),
+      &EffectPaintPropertyNode::Root());
   bool intersects = LocalToAncestorVisualRectInternal(
       last_transform_and_clip_state, final_transform_and_clip_state,
       mapping_rect, clip_behavior, inclusive_behavior, success);
diff --git a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc
index 4111f356..3cfce43 100644
--- a/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/geometry_mapper_test.cc
@@ -51,7 +51,7 @@
   FloatRect expected_transformed_rect;
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(GeometryMapperTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(GeometryMapperTest);
 
 #define EXPECT_FLOAT_RECT_NEAR(expected, actual)                             \
   do {                                                                       \
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h b/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h
index e60c767..4c8bb287 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_chunk.h
@@ -37,14 +37,7 @@
         id(id),
         properties(props),
         is_cacheable(id.client.IsCacheable()),
-        client_is_just_created(id.client.IsJustCreated()) {
-    // PaintChunk properties should not be null. If these checks are hit,
-    // we may be missing a call to ScopedPaintChunkProperties, see comment in
-    // PaintChunker::IncrementDisplayItemIndex for more information.
-    CHECK(props.Transform());
-    CHECK(props.Clip());
-    CHECK(props.Effect());
-  }
+        client_is_just_created(id.client.IsJustCreated()) {}
 
   size_t size() const {
     DCHECK_GE(end_index, begin_index);
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc
index e86ae5b..bb497e5 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.cc
@@ -6,39 +6,25 @@
 
 namespace blink {
 
-static const PropertyTreeState& UninitializedProperties() {
-  DEFINE_STATIC_LOCAL(PropertyTreeState, initial_properties,
-                      (nullptr, nullptr, nullptr));
-  return initial_properties;
-}
-
 PaintChunker::PaintChunker()
-    : current_properties_(UninitializedProperties()), force_new_chunk_(false) {}
+    : current_properties_(PropertyTreeState::Uninitialized()),
+      force_new_chunk_(false) {}
 
 PaintChunker::~PaintChunker() = default;
 
+#if DCHECK_IS_ON()
 bool PaintChunker::IsInInitialState() const {
-  if (current_properties_ != UninitializedProperties())
+  if (current_properties_ != PropertyTreeState::Uninitialized())
     return false;
 
   DCHECK(chunks_.IsEmpty());
   return true;
 }
+#endif
 
 void PaintChunker::UpdateCurrentPaintChunkProperties(
     const base::Optional<PaintChunk::Id>& chunk_id,
     const PropertyTreeState& properties) {
-  // TODO(crbug.com/923729): This is a temporary set of checks to determine the
-  // cause of the referenced bug. At this point we should have all of the
-  // properties given to us. Note that scoped objects that restore previous
-  // properties might put us back into uninitialized properties state, but if
-  // we're not being set to uninitialized then we must have all properties set.
-  if (properties != UninitializedProperties()) {
-    CHECK(properties.Transform());
-    CHECK(properties.Clip());
-    CHECK(properties.Effect());
-  }
-
   // If properties are the same, continue to use the previously set
   // |next_chunk_id_| because the id of the outer painting is likely to be
   // more stable to reduce invalidation because of chunk id changes.
@@ -59,13 +45,15 @@
 }
 
 bool PaintChunker::IncrementDisplayItemIndex(const DisplayItem& item) {
-  // Property nodes should never be null because they should either be set to
-  // properties created by a LayoutObject/FrameView, or be set to a non-null
-  // root node. If these DCHECKs are hit we are missing a call to update the
-  // properties. See: ScopedPaintChunkProperties.
-  DCHECK(current_properties_.Transform());
-  DCHECK(current_properties_.Clip());
-  DCHECK(current_properties_.Effect());
+#if DCHECK_IS_ON()
+  // If this DCHECKs are hit we are missing a call to update the properties.
+  // See: ScopedPaintChunkProperties.
+  DCHECK(!IsInInitialState());
+  // TODO(crbug.com/923729): This CHECK is a temporary to determine the cause of
+  // the referenced bug. At this point we should have all of the properties
+  // given to us.
+  CHECK(current_properties_.IsInitialized());
+#endif
 
   bool item_forces_new_chunk = item.IsForeignLayer() || item.IsScrollHitTest();
   if (item_forces_new_chunk)
@@ -103,7 +91,7 @@
 
 Vector<PaintChunk> PaintChunker::ReleasePaintChunks() {
   next_chunk_id_ = base::nullopt;
-  current_properties_ = UninitializedProperties();
+  current_properties_ = PropertyTreeState::Uninitialized();
   chunks_.ShrinkToFit();
   return std::move(chunks_);
 }
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h
index 28bf697..6b51617 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_chunker.h
@@ -27,7 +27,9 @@
   PaintChunker();
   ~PaintChunker();
 
+#if DCHECK_IS_ON()
   bool IsInInitialState() const;
+#endif
 
   const PropertyTreeState& CurrentPaintChunkProperties() const {
     return current_properties_;
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
index dc1799e..39530d5 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.cc
@@ -510,8 +510,10 @@
   if (usage_ == kTransient)
     return;
 
+#if DCHECK_IS_ON()
   DCHECK(new_display_item_list_.IsEmpty());
   DCHECK(new_paint_chunks_.IsInInitialState());
+#endif
 
   if (committed_) {
     committed_ = false;
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
index 469ae05..082f5d0 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller.h
@@ -184,9 +184,11 @@
 
   // Get the artifact generated after the last commit.
   const PaintArtifact& GetPaintArtifact() const {
+#if DCHECK_IS_ON()
     DCHECK(new_display_item_list_.IsEmpty());
     DCHECK(new_paint_chunks_.IsInInitialState());
     DCHECK(current_paint_artifact_);
+#endif
     return *current_paint_artifact_;
   }
   scoped_refptr<const PaintArtifact> GetPaintArtifactShared() const {
diff --git a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
index 79d6e95..69dc4cc9 100644
--- a/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/paint_controller_test.cc
@@ -30,7 +30,7 @@
               ElementsAre(IsPaintChunk(0, size, DefaultRootChunkId(), \
                                        DefaultPaintChunkProperties())))
 
-INSTANTIATE_TEST_CASE_P(
+INSTANTIATE_TEST_SUITE_P(
     All,
     PaintControllerTest,
     testing::Values(0,
diff --git a/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc b/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc
index 741fe87..60e0306 100644
--- a/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/property_tree_state.cc
@@ -8,6 +8,15 @@
 
 namespace blink {
 
+// For Uninitialized().
+PropertyTreeState::PropertyTreeState()
+    : transform_(nullptr), clip_(nullptr), effect_(nullptr) {}
+
+const PropertyTreeState& PropertyTreeState::Uninitialized() {
+  DEFINE_STATIC_LOCAL(PropertyTreeState, uninitialized, ());
+  return uninitialized;
+}
+
 const PropertyTreeState& PropertyTreeState::Root() {
   DEFINE_STATIC_LOCAL(
       PropertyTreeState, root,
@@ -17,21 +26,20 @@
 }
 
 PropertyTreeState PropertyTreeState::Unalias() const {
-  return PropertyTreeState(transform_ ? transform_->Unalias() : nullptr,
-                           clip_ ? clip_->Unalias() : nullptr,
-                           effect_ ? effect_->Unalias() : nullptr);
+  return PropertyTreeState(Transform()->Unalias(), Clip()->Unalias(),
+                           Effect()->Unalias());
 }
 
 String PropertyTreeState::ToString() const {
-  return String::Format("t:%p c:%p e:%p", Transform(), Clip(), Effect());
+  return String::Format("t:%p c:%p e:%p", transform_, clip_, effect_);
 }
 
 #if DCHECK_IS_ON()
 
 String PropertyTreeState::ToTreeString() const {
-  return "transform:\n" + (Transform() ? Transform()->ToTreeString() : "null") +
-         "\nclip:\n" + (Clip() ? Clip()->ToTreeString() : "null") +
-         "\neffect:\n" + (Effect() ? Effect()->ToTreeString() : "null");
+  return "transform:\n" + (transform_ ? transform_->ToTreeString() : "null") +
+         "\nclip:\n" + (clip_ ? clip_->ToTreeString() : "null") +
+         "\neffect:\n" + (effect_ ? effect_->ToTreeString() : "null");
 }
 
 #endif
diff --git a/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h b/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h
index 4cb5d2b..748543e 100644
--- a/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h
+++ b/third_party/blink/renderer/platform/graphics/paint/property_tree_state.h
@@ -20,23 +20,61 @@
   PropertyTreeState(const TransformPaintPropertyNode* transform,
                     const ClipPaintPropertyNode* clip,
                     const EffectPaintPropertyNode* effect)
-      : transform_(transform), clip_(clip), effect_(effect) {}
+      : transform_(transform), clip_(clip), effect_(effect) {
+    // TODO(crbug.com/923729): These CHECKs are temporary to determine the cause
+    // of the referenced bug.
+    CHECK(transform_);
+    CHECK(clip_);
+    CHECK(effect_);
+  }
+
+  static const PropertyTreeState& Root();
+
+  // This is used as the initial value of uninitialized PropertyTreeState.
+  // Access to the nodes are not allowed.
+  static const PropertyTreeState& Uninitialized();
+
+  // Returns true if all fields are initialized.
+  bool IsInitialized() const {
+    return transform_ != Uninitialized().transform_ &&
+           clip_ != Uninitialized().clip_ && effect_ != Uninitialized().effect_;
+  }
 
   // Returns an unaliased property tree state.
   PropertyTreeState Unalias() const;
 
-  const TransformPaintPropertyNode* Transform() const { return transform_; }
+  const TransformPaintPropertyNode* Transform() const {
+    DCHECK_NE(transform_, Uninitialized().transform_);
+    return transform_;
+  }
   void SetTransform(const TransformPaintPropertyNode* node) {
+    // TODO(crbug.com/923729): This CHECK is temporary to determine the cause
+    // of the referenced bug.
+    CHECK(node);
     transform_ = node;
   }
 
-  const ClipPaintPropertyNode* Clip() const { return clip_; }
-  void SetClip(const ClipPaintPropertyNode* node) { clip_ = node; }
+  const ClipPaintPropertyNode* Clip() const {
+    DCHECK_NE(clip_, Uninitialized().clip_);
+    return clip_;
+  }
+  void SetClip(const ClipPaintPropertyNode* node) {
+    // TODO(crbug.com/923729): This CHECK is temporary to determine the cause
+    // of the referenced bug.
+    CHECK(node);
+    clip_ = node;
+  }
 
-  const EffectPaintPropertyNode* Effect() const { return effect_; }
-  void SetEffect(const EffectPaintPropertyNode* node) { effect_ = node; }
-
-  static const PropertyTreeState& Root();
+  const EffectPaintPropertyNode* Effect() const {
+    DCHECK_NE(effect_, Uninitialized().effect_);
+    return effect_;
+  }
+  void SetEffect(const EffectPaintPropertyNode* node) {
+    // TODO(crbug.com/923729): This CHECK is temporary to determine the cause
+    // of the referenced bug.
+    CHECK(node);
+    effect_ = node;
+  }
 
   void ClearChangedToRoot() const {
     Transform()->ClearChangedToRoot();
@@ -54,21 +92,23 @@
   // ancestors.
   size_t CacheMemoryUsageInBytes() const;
 
+  bool operator==(const PropertyTreeState& other) const {
+    return transform_ == other.transform_ && clip_ == other.clip_ &&
+           effect_ == other.effect_;
+  }
+  bool operator!=(const PropertyTreeState& other) const {
+    return !(*this == other);
+  }
+
  private:
+  // For Uninitialized().
+  PropertyTreeState();
+
   const TransformPaintPropertyNode* transform_;
   const ClipPaintPropertyNode* clip_;
   const EffectPaintPropertyNode* effect_;
 };
 
-inline bool operator==(const PropertyTreeState& a, const PropertyTreeState& b) {
-  return a.Transform() == b.Transform() && a.Clip() == b.Clip() &&
-         a.Effect() == b.Effect();
-}
-
-inline bool operator!=(const PropertyTreeState& a, const PropertyTreeState& b) {
-  return !(a == b);
-}
-
 PLATFORM_EXPORT std::ostream& operator<<(std::ostream&,
                                          const PropertyTreeState&);
 
diff --git a/third_party/blink/renderer/platform/graphics/paint/property_tree_state_test.cc b/third_party/blink/renderer/platform/graphics/paint/property_tree_state_test.cc
deleted file mode 100644
index 6fbb0f1..0000000
--- a/third_party/blink/renderer/platform/graphics/paint/property_tree_state_test.cc
+++ /dev/null
@@ -1,13 +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/platform/graphics/paint/property_tree_state.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace blink {
-
-class PropertyTreeStateTest : public testing::Test {};
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc
index 6a7e251..986026c 100644
--- a/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc
+++ b/third_party/blink/renderer/platform/graphics/paint/raster_invalidator_test.cc
@@ -61,7 +61,7 @@
       [](const IntRect& rect) {};
 };
 
-INSTANTIATE_PAINT_TEST_CASE_P(RasterInvalidatorTest);
+INSTANTIATE_PAINT_TEST_SUITE_P(RasterInvalidatorTest);
 
 #define EXPECT_CHUNK_INVALIDATION_CUSTOM(                               \
     invalidations, index, chunk, expected_reason, layer_offset, mapper) \
diff --git a/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.cc b/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.cc
deleted file mode 100644
index fe89049..0000000
--- a/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.cc
+++ /dev/null
@@ -1,20 +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/platform/graphics/paint/ref_counted_property_tree_state.h"
-
-#include <memory>
-
-namespace blink {
-
-const RefCountedPropertyTreeState& RefCountedPropertyTreeState::Root() {
-  DEFINE_STATIC_LOCAL(
-      std::unique_ptr<RefCountedPropertyTreeState>, root,
-      (std::make_unique<RefCountedPropertyTreeState>(
-          &TransformPaintPropertyNode::Root(), &ClipPaintPropertyNode::Root(),
-          &EffectPaintPropertyNode::Root())));
-  return *root;
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h b/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h
index 35174a78..387089a 100644
--- a/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h
+++ b/third_party/blink/renderer/platform/graphics/paint/ref_counted_property_tree_state.h
@@ -15,11 +15,6 @@
   USING_FAST_MALLOC(RefCountedPropertyTreeState);
 
  public:
-  RefCountedPropertyTreeState(const TransformPaintPropertyNode* transform,
-                              const ClipPaintPropertyNode* clip,
-                              const EffectPaintPropertyNode* effect)
-      : transform_(transform), clip_(clip), effect_(effect) {}
-
   RefCountedPropertyTreeState(const PropertyTreeState& property_tree_state)
       : transform_(property_tree_state.Transform()),
         clip_(property_tree_state.Clip()),
@@ -30,21 +25,8 @@
   const TransformPaintPropertyNode* Transform() const {
     return transform_.get();
   }
-  void SetTransform(scoped_refptr<const TransformPaintPropertyNode> node) {
-    transform_ = std::move(node);
-  }
-
   const ClipPaintPropertyNode* Clip() const { return clip_.get(); }
-  void SetClip(scoped_refptr<const ClipPaintPropertyNode> node) {
-    clip_ = std::move(node);
-  }
-
   const EffectPaintPropertyNode* Effect() const { return effect_.get(); }
-  void SetEffect(scoped_refptr<const EffectPaintPropertyNode> node) {
-    effect_ = std::move(node);
-  }
-
-  static const RefCountedPropertyTreeState& Root();
 
   PropertyTreeState GetPropertyTreeState() const {
     return PropertyTreeState(transform_.get(), clip_.get(), effect_.get());
diff --git a/third_party/blink/renderer/platform/heap/DEPS b/third_party/blink/renderer/platform/heap/DEPS
index 3d29fd0..9822448 100644
--- a/third_party/blink/renderer/platform/heap/DEPS
+++ b/third_party/blink/renderer/platform/heap/DEPS
@@ -15,7 +15,7 @@
     "+third_party/blink/renderer/platform/cross_thread_functional.h",
     "+third_party/blink/renderer/platform/histogram.h",
     "+third_party/blink/renderer/platform/instrumentation",
-    "+third_party/blink/renderer/platform/memory_coordinator.h",
+    "+third_party/blink/renderer/platform/memory_pressure_listener.h",
     "+third_party/blink/renderer/platform/platform_export.h",
     "+third_party/blink/renderer/platform/runtime_enabled_features.h",
     "+third_party/blink/renderer/platform/scheduler/public",
diff --git a/third_party/blink/renderer/platform/heap/heap_page.cc b/third_party/blink/renderer/platform/heap/heap_page.cc
index ad4452c..6aa0768 100644
--- a/third_party/blink/renderer/platform/heap/heap_page.cc
+++ b/third_party/blink/renderer/platform/heap/heap_page.cc
@@ -47,7 +47,7 @@
 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_memory_allocator_dump.h"
 #include "third_party/blink/renderer/platform/instrumentation/tracing/web_process_memory_dump.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 #include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
 #include "third_party/blink/renderer/platform/wtf/assertions.h"
 #include "third_party/blink/renderer/platform/wtf/container_annotations.h"
@@ -1353,7 +1353,7 @@
 #if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER)
       // Discarding pages increases page faults and may regress performance.
       // So we enable this only on low-RAM devices.
-      if (MemoryCoordinator::IsLowEndDevice())
+      if (MemoryPressureListenerRegistry::IsLowEndDevice())
         DiscardPages(start_of_gap + sizeof(FreeListEntry), header_address);
 #endif
     }
@@ -1369,7 +1369,7 @@
   if (start_of_gap != Payload() && start_of_gap != PayloadEnd()) {
     page_arena->AddToFreeList(start_of_gap, PayloadEnd() - start_of_gap);
 #if !DCHECK_IS_ON() && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER)
-    if (MemoryCoordinator::IsLowEndDevice())
+    if (MemoryPressureListenerRegistry::IsLowEndDevice())
       DiscardPages(start_of_gap + sizeof(FreeListEntry), PayloadEnd());
 #endif
   }
diff --git a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
index 734ee90..2e7ac03 100644
--- a/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
+++ b/third_party/blink/renderer/platform/image-decoders/jpeg/jpeg_image_decoder_test.cc
@@ -535,8 +535,8 @@
     // any samples.
     {"cs-uma-two-channels-jfif-marker.jpg", false}};
 
-INSTANTIATE_TEST_CASE_P(JPEGImageDecoderTest,
-                        ColorSpaceUMATest,
-                        ::testing::ValuesIn(kColorSpaceUMATestParams));
+INSTANTIATE_TEST_SUITE_P(JPEGImageDecoderTest,
+                         ColorSpaceUMATest,
+                         ::testing::ValuesIn(kColorSpaceUMATestParams));
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/BUILD.gn b/third_party/blink/renderer/platform/loader/BUILD.gn
index eec708b..90af658 100644
--- a/third_party/blink/renderer/platform/loader/BUILD.gn
+++ b/third_party/blink/renderer/platform/loader/BUILD.gn
@@ -20,6 +20,8 @@
     "cors/cors.h",
     "cors/cors_error_string.cc",
     "cors/cors_error_string.h",
+    "fetch/buffering_bytes_consumer.cc",
+    "fetch/buffering_bytes_consumer.h",
     "fetch/buffering_data_pipe_writer.cc",
     "fetch/buffering_data_pipe_writer.h",
     "fetch/bytes_consumer.cc",
@@ -130,6 +132,7 @@
 
   # Source files for blink_platform_unittests.
   sources = [
+    "fetch/buffering_bytes_consumer_test.cc",
     "fetch/buffering_data_pipe_writer_test.cc",
     "fetch/bytes_consumer_for_data_consumer_handle_test.cc",
     "fetch/bytes_consumer_test.cc",
diff --git a/third_party/blink/renderer/platform/loader/DEPS b/third_party/blink/renderer/platform/loader/DEPS
index 1986e2a..7108dfe 100644
--- a/third_party/blink/renderer/platform/loader/DEPS
+++ b/third_party/blink/renderer/platform/loader/DEPS
@@ -13,6 +13,7 @@
     "+services/network/public",  # for Fetch API and CORS
     "+third_party/blink/renderer/platform/bindings/parkable_string.h",
     "+third_party/blink/renderer/platform/bindings/script_forbidden_scope.h",
+    "+third_party/blink/renderer/platform/bindings/trace_wrapper_member.h",
     "+third_party/blink/renderer/platform/blob/blob_data.h",
     "+third_party/blink/renderer/platform/cross_origin_attribute_value.h",
     "+third_party/blink/renderer/platform/cross_thread_copier.h",
@@ -24,7 +25,7 @@
     "+third_party/blink/renderer/platform/instance_counters.h",
     "+third_party/blink/renderer/platform/instrumentation",
     "+third_party/blink/renderer/platform/language.h",
-    "+third_party/blink/renderer/platform/memory_coordinator.h",
+    "+third_party/blink/renderer/platform/memory_pressure_listener.h",
     "+third_party/blink/renderer/platform/mhtml",
     "+third_party/blink/renderer/platform/network",
     "+third_party/blink/renderer/platform/platform_export.h",
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc
new file mode 100644
index 0000000..5aa3b6c
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.cc
@@ -0,0 +1,144 @@
+// 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/platform/loader/fetch/buffering_bytes_consumer.h"
+
+#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
+
+namespace blink {
+
+BufferingBytesConsumer::BufferingBytesConsumer(BytesConsumer* bytes_consumer)
+    : bytes_consumer_(bytes_consumer) {
+  bytes_consumer_->SetClient(this);
+}
+
+BufferingBytesConsumer::~BufferingBytesConsumer() = default;
+
+BytesConsumer::Result BufferingBytesConsumer::BeginRead(const char** buffer,
+                                                        size_t* available) {
+  if (buffer_.IsEmpty()) {
+    if (has_seen_error_)
+      return Result::kError;
+
+    if (has_seen_end_of_data_) {
+      ClearClient();
+      return Result::kDone;
+    }
+
+    BufferData();
+
+    if (has_seen_error_)
+      return Result::kError;
+
+    if (buffer_.IsEmpty())
+      return has_seen_end_of_data_ ? Result::kDone : Result::kShouldWait;
+  }
+
+  DCHECK_LT(offset_for_first_chunk_, buffer_[0].size());
+  *buffer = buffer_[0].data() + offset_for_first_chunk_;
+  *available = buffer_[0].size() - offset_for_first_chunk_;
+  return Result::kOk;
+}
+
+BytesConsumer::Result BufferingBytesConsumer::EndRead(size_t read_size) {
+  if (buffer_.IsEmpty()) {
+    DCHECK(has_seen_error_);
+    return Result::kError;
+  }
+
+  DCHECK_LE(offset_for_first_chunk_ + read_size, buffer_[0].size());
+  offset_for_first_chunk_ += read_size;
+
+  if (offset_for_first_chunk_ == buffer_[0].size()) {
+    offset_for_first_chunk_ = 0;
+    buffer_.pop_front();
+  }
+
+  if (buffer_.IsEmpty() && has_seen_end_of_data_) {
+    ClearClient();
+    return Result::kDone;
+  }
+  return Result::kOk;
+}
+
+scoped_refptr<BlobDataHandle> BufferingBytesConsumer::DrainAsBlobDataHandle(
+    BlobSizePolicy policy) {
+  return bytes_consumer_->DrainAsBlobDataHandle(policy);
+}
+
+scoped_refptr<EncodedFormData> BufferingBytesConsumer::DrainAsFormData() {
+  return bytes_consumer_->DrainAsFormData();
+}
+
+mojo::ScopedDataPipeConsumerHandle BufferingBytesConsumer::DrainAsDataPipe() {
+  // We intentionally return an empty handle here, because returning a DataPipe
+  // may activate back pressure.
+  return {};
+}
+
+void BufferingBytesConsumer::SetClient(BytesConsumer::Client* client) {
+  client_ = client;
+}
+
+void BufferingBytesConsumer::ClearClient() {
+  client_ = nullptr;
+}
+
+void BufferingBytesConsumer::Cancel() {
+  ClearClient();
+  bytes_consumer_->Cancel();
+}
+
+BytesConsumer::PublicState BufferingBytesConsumer::GetPublicState() const {
+  if (buffer_.IsEmpty())
+    return bytes_consumer_->GetPublicState();
+  return PublicState::kReadableOrWaiting;
+}
+
+BytesConsumer::Error BufferingBytesConsumer::GetError() const {
+  return bytes_consumer_->GetError();
+}
+
+void BufferingBytesConsumer::Trace(Visitor* visitor) {
+  visitor->Trace(bytes_consumer_);
+  visitor->Trace(client_);
+  BytesConsumer::Trace(visitor);
+  BytesConsumer::Client::Trace(visitor);
+}
+
+void BufferingBytesConsumer::OnStateChange() {
+  BytesConsumer::Client* client = client_;
+  BufferData();
+  if (client)
+    client->OnStateChange();
+}
+
+void BufferingBytesConsumer::BufferData() {
+  while (true) {
+    const char* p = nullptr;
+    size_t available = 0;
+    auto result = bytes_consumer_->BeginRead(&p, &available);
+    if (result == Result::kShouldWait)
+      return;
+    if (result == Result::kOk) {
+      Vector<char> chunk;
+      chunk.Append(p, SafeCast<wtf_size_t>(available));
+      buffer_.push_back(std::move(chunk));
+      result = bytes_consumer_->EndRead(available);
+    }
+    if (result == Result::kDone) {
+      has_seen_end_of_data_ = true;
+      ClearClient();
+      return;
+    }
+    if (result != Result::kOk) {
+      buffer_.clear();
+      has_seen_error_ = true;
+      ClearClient();
+      return;
+    }
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h
new file mode 100644
index 0000000..f7ac1ff
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h
@@ -0,0 +1,70 @@
+// 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_PLATFORM_LOADER_FETCH_BUFFERING_BYTES_CONSUMER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BUFFERING_BYTES_CONSUMER_H_
+
+#include <memory>
+
+#include "base/memory/scoped_refptr.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/platform_export.h"
+#include "third_party/blink/renderer/platform/wtf/deque.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+#include "third_party/blink/renderer/platform/wtf/vector.h"
+
+namespace blink {
+
+// BufferingBytesConsumer is a BytesConsumer. It takes a BytesConsumer
+// ("the original BytesConsumer") as a constructor parameter, and results read
+// from the BufferingBytesConsumer are as same as results which would be read
+// from the original BytesConsumer.
+// BufferingBytesConsumer buffers reads chunks from the original BytesConsumer
+// and store it until they are read, before read requests are issued from the
+// client.
+class PLATFORM_EXPORT BufferingBytesConsumer final
+    : public BytesConsumer,
+      private BytesConsumer::Client {
+  USING_GARBAGE_COLLECTED_MIXIN(BufferingBytesConsumer);
+
+ public:
+  // Creates a BufferingBytesConsumer. |bytes_consumer| is the original
+  // BytesConsumer.
+  // |bytes_consumer| must not have a client.
+  explicit BufferingBytesConsumer(BytesConsumer* bytes_consumer);
+  ~BufferingBytesConsumer() override;
+
+  // BufferingBytesConsumer
+  Result BeginRead(const char** buffer, size_t* available) override;
+  Result EndRead(size_t read_size) override;
+  scoped_refptr<BlobDataHandle> DrainAsBlobDataHandle(BlobSizePolicy) override;
+  scoped_refptr<EncodedFormData> DrainAsFormData() override;
+  mojo::ScopedDataPipeConsumerHandle DrainAsDataPipe() override;
+  void SetClient(BytesConsumer::Client*) override;
+  void ClearClient() override;
+  void Cancel() override;
+  PublicState GetPublicState() const override;
+  Error GetError() const override;
+  String DebugName() const override { return "BufferingBytesConsumer"; }
+
+  void Trace(blink::Visitor*) override;
+
+ private:
+  // BufferingBytesConsumer::Client
+  void OnStateChange() override;
+  void BufferData();
+
+  const TraceWrapperMember<BytesConsumer> bytes_consumer_;
+  Deque<Vector<char>> buffer_;
+  size_t offset_for_first_chunk_ = 0;
+  bool has_seen_end_of_data_ = false;
+  bool has_seen_error_ = false;
+  Member<BytesConsumer::Client> client_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_LOADER_FETCH_BUFFERING_BYTES_CONSUMER_H_
diff --git a/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc
new file mode 100644
index 0000000..abab5d06
--- /dev/null
+++ b/third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer_test.cc
@@ -0,0 +1,50 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/loader/fetch/buffering_bytes_consumer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h"
+#include "third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h"
+#include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
+
+namespace blink {
+namespace {
+
+class BufferingBytesConsumerTest : public testing::Test {
+ public:
+  using Command = ReplayingBytesConsumer::Command;
+  using Result = BytesConsumer::Result;
+  using PublicState = BytesConsumer::PublicState;
+};
+
+TEST_F(BufferingBytesConsumerTest, Read) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* replaying_bytes_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+
+  replaying_bytes_consumer->Add(Command(Command::kWait));
+  replaying_bytes_consumer->Add(Command(Command::kData, "1"));
+  replaying_bytes_consumer->Add(Command(Command::kWait));
+  replaying_bytes_consumer->Add(Command(Command::kWait));
+  replaying_bytes_consumer->Add(Command(Command::kData, "23"));
+  replaying_bytes_consumer->Add(Command(Command::kData, "4"));
+  replaying_bytes_consumer->Add(Command(Command::kData, "567"));
+  replaying_bytes_consumer->Add(Command(Command::kData, "8"));
+  replaying_bytes_consumer->Add(Command(Command::kDone));
+
+  auto* bytes_consumer =
+      MakeGarbageCollected<BufferingBytesConsumer>(replaying_bytes_consumer);
+
+  EXPECT_EQ(PublicState::kReadableOrWaiting, bytes_consumer->GetPublicState());
+  auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(bytes_consumer);
+  auto result = reader->Run(task_runner.get());
+
+  EXPECT_EQ(PublicState::kClosed, bytes_consumer->GetPublicState());
+  ASSERT_EQ(result.first, Result::kDone);
+  EXPECT_EQ("12345678", String(result.second.data(), result.second.size()));
+}
+
+}  // namespace
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
index b79e782d..d9f21543 100644
--- a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences.cc
@@ -25,7 +25,7 @@
        i < static_cast<int>(mojom::WebClientHintsType::kMaxValue) + 1; ++i) {
     enabled_hints.SetIsEnabled(
         static_cast<mojom::WebClientHintsType>(i),
-        accept_client_hints_header.Contains(kClientHintsHeaderMapping[i]));
+        accept_client_hints_header.Contains(kClientHintsNameMapping[i]));
   }
 
   enabled_hints.SetIsEnabled(
@@ -43,13 +43,18 @@
   enabled_hints.SetIsEnabled(
       mojom::WebClientHintsType::kEct,
       enabled_hints.IsEnabled(mojom::WebClientHintsType::kEct));
+
+  enabled_hints.SetIsEnabled(
+      mojom::WebClientHintsType::kLang,
+      enabled_hints.IsEnabled(mojom::WebClientHintsType::kLang) &&
+          RuntimeEnabledFeatures::LangClientHintHeaderEnabled());
 }
 
 }  // namespace
 
 ClientHintsPreferences::ClientHintsPreferences() {
   DCHECK_EQ(static_cast<size_t>(mojom::WebClientHintsType::kMaxValue) + 1,
-            kClientHintsHeaderMappingCount);
+            kClientHintsMappingsCount);
 }
 
 void ClientHintsPreferences::UpdateFrom(
diff --git a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc
index 67dc2fa..dea2544 100644
--- a/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/client_hints_preferences_test.cc
@@ -24,16 +24,20 @@
     bool expectation_rtt;
     bool expectation_downlink;
     bool expectation_ect;
+    bool expectation_lang;
   } cases[] = {
-      {"width, dpr, viewportWidth", true, true, false, false, false, false},
-      {"WiDtH, dPr, viewport-width, rtt, downlink, ect", true, true, true, true,
-       true, true},
+      {"width, dpr, viewportWidth", true, true, false, false, false, false,
+       false},
+      {"WiDtH, dPr, viewport-width, rtt, downlink, ect, lang", true, true, true,
+       true, true, true, true},
       {"WiDtH, dPr, viewport-width, rtt, downlink, effective-connection-type",
-       true, true, true, true, true, false},
-      {"WIDTH, DPR, VIWEPROT-Width", true, true, false, false, false, false},
-      {"VIewporT-Width, wutwut, width", true, false, true, false, false, false},
-      {"dprw", false, false, false, false, false, false},
-      {"DPRW", false, false, false, false, false, false},
+       true, true, true, true, true, false, false},
+      {"WIDTH, DPR, VIWEPROT-Width", true, true, false, false, false, false,
+       false},
+      {"VIewporT-Width, wutwut, width", true, false, true, false, false, false,
+       false},
+      {"dprw", false, false, false, false, false, false, false},
+      {"DPRW", false, false, false, false, false, false, false},
   };
 
   for (const auto& test_case : cases) {
@@ -55,6 +59,8 @@
               preferences.ShouldSend(mojom::WebClientHintsType::kDownlink));
     EXPECT_EQ(test_case.expectation_ect,
               preferences.ShouldSend(mojom::WebClientHintsType::kEct));
+    EXPECT_EQ(test_case.expectation_lang,
+              preferences.ShouldSend(mojom::WebClientHintsType::kLang));
 
     // Calling UpdateFromAcceptClientHintsHeader with empty header should have
     // no impact on client hint preferences.
@@ -98,6 +104,7 @@
   EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kRtt));
   EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kDownlink));
   EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kEct));
+  EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kLang));
 
   // Calling UpdateFromAcceptClientHintsHeader with empty header should have
   // no impact on client hint preferences.
@@ -108,6 +115,7 @@
   EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kRtt));
   EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kDownlink));
   EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kEct));
+  EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kLang));
 
   // Calling UpdateFromAcceptClientHintsHeader with an invalid header should
   // have no impact on client hint preferences.
@@ -117,7 +125,7 @@
       preferences.ShouldSend(mojom::WebClientHintsType::kResourceWidth));
   EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kRtt));
   EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kDownlink));
-  EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kEct));
+  EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kLang));
 
   // Calling UpdateFromAcceptClientHintsHeader with "width" header should
   // have no impact on already enabled client hint preferences.
@@ -128,6 +136,7 @@
   EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kRtt));
   EXPECT_TRUE(preferences.ShouldSend(mojom::WebClientHintsType::kDownlink));
   EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kEct));
+  EXPECT_FALSE(preferences.ShouldSend(mojom::WebClientHintsType::kLang));
 
   preferences.UpdateFromAcceptClientHintsLifetimeHeader("1000", kurl, nullptr);
   EXPECT_EQ(base::TimeDelta::FromSeconds(1000),
@@ -160,20 +169,21 @@
     bool expect_rtt;
     bool expect_downlink;
     bool expect_ect;
+    bool expect_lang;
   } test_cases[] = {
-      {"width, dpr, viewportWidth", "", 0, false, true, true, false, false,
-       false, false},
+      {"width, dpr, viewportWidth, lang", "", 0, false, true, true, false,
+       false, false, false, true},
       {"width, dpr, viewportWidth", "-1000", 0, false, true, true, false, false,
-       false, false},
-      {"width, dpr, viewportWidth", "1000s", 0, false, true, true, false, false,
-       false, false},
-      {"width, dpr, viewportWidth", "1000.5", 0, false, true, true, false,
        false, false, false},
+      {"width, dpr, viewportWidth", "1000s", 0, false, true, true, false, false,
+       false, false, false},
+      {"width, dpr, viewportWidth", "1000.5", 0, false, true, true, false,
+       false, false, false, false},
       {"width, dpr, rtt, downlink, ect", "1000", 1000, false, true, true, false,
-       true, true, true},
+       true, true, true, false},
       {"device-memory", "-1000", 0, true, false, false, false, false, false,
-       false},
-      {"dpr rtt", "1000", 1000, false, false, false, false, false, false,
+       false, false},
+      {"dpr rtt", "1000", 1000, false, false, false, false, false, false, false,
        false},
   };
 
@@ -191,6 +201,7 @@
     EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kRtt));
     EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink));
     EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kEct));
+    EXPECT_FALSE(enabled_types.IsEnabled(mojom::WebClientHintsType::kLang));
     TimeDelta persist_duration = preferences.GetPersistDuration();
     EXPECT_EQ(base::TimeDelta(), persist_duration);
 
@@ -223,6 +234,8 @@
               enabled_types.IsEnabled(mojom::WebClientHintsType::kDownlink));
     EXPECT_EQ(test.expect_ect,
               enabled_types.IsEnabled(mojom::WebClientHintsType::kEct));
+    EXPECT_EQ(test.expect_lang,
+              enabled_types.IsEnabled(mojom::WebClientHintsType::kLang));
   }
 }
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc b/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
index 15b85ba6..ef241aa 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache.cc
@@ -88,8 +88,8 @@
       size_(0),
       task_runner_(std::move(task_runner)) {
   MemoryCacheDumpProvider::Instance()->SetMemoryCache(this);
-  if (MemoryCoordinator::IsLowEndDevice())
-    MemoryCoordinator::Instance().RegisterClient(this);
+  if (MemoryPressureListenerRegistry::IsLowEndDevice())
+    MemoryPressureListenerRegistry::Instance().RegisterClient(this);
 }
 
 MemoryCache* MemoryCache::Create(
@@ -102,7 +102,7 @@
 void MemoryCache::Trace(blink::Visitor* visitor) {
   visitor->Trace(resource_maps_);
   MemoryCacheDumpClient::Trace(visitor);
-  MemoryCoordinatorClient::Trace(visitor);
+  MemoryPressureListener::Trace(visitor);
 }
 
 KURL MemoryCache::RemoveFragmentIdentifierIfNeeded(const KURL& original_url) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/memory_cache.h b/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
index 4db0a270..e0669f5 100644
--- a/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
+++ b/third_party/blink/renderer/platform/loader/fetch/memory_cache.h
@@ -28,7 +28,7 @@
 
 #include "third_party/blink/renderer/platform/instrumentation/tracing/memory_cache_dump_provider.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 #include "third_party/blink/renderer/platform/wtf/allocator.h"
@@ -71,7 +71,7 @@
 class PLATFORM_EXPORT MemoryCache final
     : public GarbageCollectedFinalized<MemoryCache>,
       public MemoryCacheDumpClient,
-      public MemoryCoordinatorClient {
+      public MemoryPressureListener {
   USING_GARBAGE_COLLECTED_MIXIN(MemoryCache);
   WTF_MAKE_NONCOPYABLE(MemoryCache);
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.cc b/third_party/blink/renderer/platform/loader/fetch/resource.cc
index af9a3700..91c9c669 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.cc
@@ -142,7 +142,7 @@
   InstanceCounters::IncrementCounter(InstanceCounters::kResourceCounter);
 
   if (IsMainThread())
-    MemoryCoordinator::Instance().RegisterClient(this);
+    MemoryPressureListenerRegistry::Instance().RegisterClient(this);
 }
 
 Resource::~Resource() {
@@ -156,7 +156,7 @@
   visitor->Trace(clients_awaiting_callback_);
   visitor->Trace(finished_clients_);
   visitor->Trace(finish_observers_);
-  MemoryCoordinatorClient::Trace(visitor);
+  MemoryPressureListener::Trace(visitor);
 }
 
 void Resource::SetLoader(ResourceLoader* loader) {
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource.h b/third_party/blink/renderer/platform/loader/fetch/resource.h
index 07e2364..f8ce800 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource.h
@@ -44,7 +44,7 @@
 #include "third_party/blink/renderer/platform/loader/fetch/resource_status.h"
 #include "third_party/blink/renderer/platform/loader/fetch/text_resource_decoder_options.h"
 #include "third_party/blink/renderer/platform/loader/subresource_integrity.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 #include "third_party/blink/renderer/platform/platform_export.h"
 #include "third_party/blink/renderer/platform/scheduler/public/post_cancellable_task.h"
 #include "third_party/blink/renderer/platform/shared_buffer.h"
@@ -92,7 +92,7 @@
 // requested data has arrived. This class also does the actual communication
 // with the loader to obtain the resource from the network.
 class PLATFORM_EXPORT Resource : public GarbageCollectedFinalized<Resource>,
-                                 public MemoryCoordinatorClient {
+                                 public MemoryPressureListener {
   USING_GARBAGE_COLLECTED_MIXIN(Resource);
   WTF_MAKE_NONCOPYABLE(Resource);
 
@@ -513,7 +513,7 @@
 
   String ReasonNotDeletable() const;
 
-  // MemoryCoordinatorClient overrides:
+  // MemoryPressureListener overrides:
   void OnPurgeMemory() override;
 
   void CheckResourceIntegrity();
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 0253184e..f88467c 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.cc
@@ -443,6 +443,10 @@
   DidFail(ResourceError::Failure(resource_->Url()), 0, 0, 0);
 }
 
+void ResourceLoader::DidCancelLoadingBody() {
+  Cancel();
+}
+
 void ResourceLoader::StartWith(const ResourceRequest& request) {
   DCHECK_NE(ResourceLoadScheduler::kInvalidClientId, scheduler_client_id_);
   DCHECK(loader_);
diff --git a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
index 7f7baff..daf267a9 100644
--- a/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
+++ b/third_party/blink/renderer/platform/loader/fetch/resource_loader.h
@@ -169,6 +169,7 @@
   void DidReceiveData(base::span<const char> data) override;
   void DidFinishLoadingBody() override;
   void DidFailLoadingBody() override;
+  void DidCancelLoadingBody() override;
 
   bool ShouldFetchCodeCache();
   void StartWith(const ResourceRequest&);
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
index 3133fb37..ff926caa 100644
--- a/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.cc
@@ -6,6 +6,7 @@
 
 #include <algorithm>
 #include <utility>
+#include "base/auto_reset.h"
 #include "third_party/blink/renderer/platform/loader/fetch/fetch_context.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource.h"
 #include "third_party/blink/renderer/platform/loader/fetch/resource_loader.h"
@@ -15,6 +16,243 @@
 
 constexpr size_t ResponseBodyLoader::kMaxNumConsumedBytesInTask;
 
+class ResponseBodyLoader::DelegatingBytesConsumer final
+    : public BytesConsumer,
+      public BytesConsumer::Client {
+  USING_GARBAGE_COLLECTED_MIXIN(DelegatingBytesConsumer);
+
+ public:
+  DelegatingBytesConsumer(
+      BytesConsumer& bytes_consumer,
+      ResponseBodyLoader& loader,
+      scoped_refptr<base::SingleThreadTaskRunner> task_runner)
+      : bytes_consumer_(bytes_consumer),
+        loader_(loader),
+        task_runner_(std::move(task_runner)) {}
+
+  Result BeginRead(const char** buffer, size_t* available) override {
+    *buffer = nullptr;
+    *available = 0;
+    if (loader_->IsAborted()) {
+      return Result::kError;
+    }
+    if (loader_->IsSuspended()) {
+      return Result::kShouldWait;
+    }
+    auto result = bytes_consumer_->BeginRead(buffer, available);
+    if (result == Result::kOk) {
+      *available = std::min(*available, lookahead_bytes_);
+      if (*available == 0) {
+        *buffer = nullptr;
+        result = Result::kShouldWait;
+        task_runner_->PostTask(
+            FROM_HERE, base::BindOnce(&DelegatingBytesConsumer::OnStateChange,
+                                      WrapPersistent(this)));
+      }
+    }
+    HandleResult(result);
+    return result;
+  }
+  Result EndRead(size_t read_size) override {
+    DCHECK_LE(read_size, lookahead_bytes_);
+    lookahead_bytes_ -= read_size;
+    auto result = bytes_consumer_->EndRead(read_size);
+    if (loader_->IsAborted()) {
+      return Result::kError;
+    }
+    HandleResult(result);
+    return result;
+  }
+  scoped_refptr<BlobDataHandle> DrainAsBlobDataHandle(
+      BlobSizePolicy policy) override {
+    if (loader_->IsAborted()) {
+      return nullptr;
+    }
+    auto handle = bytes_consumer_->DrainAsBlobDataHandle(policy);
+    if (handle) {
+      HandleResult(Result::kDone);
+    }
+    return handle;
+  }
+  scoped_refptr<EncodedFormData> DrainAsFormData() override {
+    if (loader_->IsAborted()) {
+      return nullptr;
+    }
+    auto form_data = bytes_consumer_->DrainAsFormData();
+    if (form_data) {
+      HandleResult(Result::kDone);
+    }
+    return form_data;
+  }
+  mojo::ScopedDataPipeConsumerHandle DrainAsDataPipe() override {
+    if (loader_->IsAborted()) {
+      return {};
+    }
+    auto handle = bytes_consumer_->DrainAsDataPipe();
+    if (handle) {
+      HandleResult(Result::kDone);
+    }
+    return handle;
+  }
+  void SetClient(BytesConsumer::Client* client) override {
+    DCHECK(!bytes_consumer_client_);
+    DCHECK(client);
+    if (state_ != State::kLoading) {
+      return;
+    }
+    bytes_consumer_client_ = client;
+  }
+  void ClearClient() override { bytes_consumer_client_ = nullptr; }
+  void Cancel() override {
+    if (state_ != State::kLoading) {
+      return;
+    }
+
+    state_ = State::kCancelled;
+    bytes_consumer_->Cancel();
+
+    if (in_on_state_change_) {
+      has_pending_signal_ = true;
+      return;
+    }
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&ResponseBodyLoader::DidCancelLoadingBody,
+                                  WrapWeakPersistent(loader_.Get())));
+  }
+  PublicState GetPublicState() const override {
+    if (loader_->IsAborted())
+      return PublicState::kErrored;
+    return bytes_consumer_->GetPublicState();
+  }
+  Error GetError() const override { return bytes_consumer_->GetError(); }
+  String DebugName() const override { return "DelegatingBytesConsumer"; }
+
+  void Abort() {
+    if (state_ != State::kLoading) {
+      return;
+    }
+    if (bytes_consumer_client_) {
+      bytes_consumer_client_->OnStateChange();
+    }
+  }
+
+  void OnStateChange() override {
+    DCHECK(!in_on_state_change_);
+    base::AutoReset<bool> auto_reset(&in_on_state_change_, true);
+
+    if (loader_->IsAborted() || loader_->IsSuspended() ||
+        state_ == State::kCancelled) {
+      return;
+    }
+
+    // Peek available bytes from |bytes_consumer_| and report them to
+    // |loader_|.
+    const char* buffer = nullptr;
+    size_t available = 0;
+    // Poissible state change caused by BeginRead will be realized by the
+    // following logic, so we don't need to worry about it here.
+    auto result = bytes_consumer_->BeginRead(&buffer, &available);
+    if (result == Result::kOk) {
+      if (lookahead_bytes_ < available) {
+        loader_->DidReceiveData(base::make_span(buffer + lookahead_bytes_,
+                                                available - lookahead_bytes_));
+        lookahead_bytes_ = available;
+      }
+      // Poissible state change caused by EndRead will be realized by the
+      // following logic, so we don't need to worry about it here.
+      Result unused = bytes_consumer_->EndRead(0);
+      ALLOW_UNUSED_LOCAL(unused);
+    }
+
+    if (bytes_consumer_client_) {
+      bytes_consumer_client_->OnStateChange();
+    }
+
+    switch (GetPublicState()) {
+      case PublicState::kReadableOrWaiting:
+        break;
+      case PublicState::kClosed:
+        HandleResult(Result::kDone);
+        break;
+      case PublicState::kErrored:
+        HandleResult(Result::kError);
+        break;
+    }
+
+    if (has_pending_signal_) {
+      has_pending_signal_ = false;
+      switch (state_) {
+        case State::kLoading:
+          NOTREACHED();
+          break;
+        case State::kDone:
+          loader_->DidFinishLoadingBody();
+          break;
+        case State::kErrored:
+          loader_->DidFailLoadingBody();
+          break;
+        case State::kCancelled:
+          loader_->DidCancelLoadingBody();
+          break;
+      }
+    }
+  }
+
+  void Trace(Visitor* visitor) override {
+    visitor->Trace(bytes_consumer_);
+    visitor->Trace(loader_);
+    visitor->Trace(bytes_consumer_client_);
+    BytesConsumer::Trace(visitor);
+  }
+
+ private:
+  enum class State {
+    kLoading,
+    kDone,
+    kErrored,
+    kCancelled,
+  };
+
+  void HandleResult(Result result) {
+    if (state_ != State::kLoading) {
+      return;
+    }
+
+    if (result == Result::kDone) {
+      state_ = State::kDone;
+      if (in_on_state_change_) {
+        has_pending_signal_ = true;
+      } else {
+        task_runner_->PostTask(
+            FROM_HERE, base::BindOnce(&ResponseBodyLoader::DidFinishLoadingBody,
+                                      WrapWeakPersistent(loader_.Get())));
+      }
+    }
+
+    if (result == Result::kError) {
+      state_ = State::kErrored;
+      if (in_on_state_change_) {
+        has_pending_signal_ = true;
+      } else {
+        task_runner_->PostTask(
+            FROM_HERE, base::BindOnce(&ResponseBodyLoader::DidFailLoadingBody,
+                                      WrapWeakPersistent(loader_.Get())));
+      }
+    }
+  }
+
+  const Member<BytesConsumer> bytes_consumer_;
+  const Member<ResponseBodyLoader> loader_;
+  Member<BytesConsumer::Client> bytes_consumer_client_;
+  const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+
+  // The size of body which has been reported to |loader_|.
+  size_t lookahead_bytes_ = 0;
+  State state_ = State::kLoading;
+  bool in_on_state_change_ = false;
+  bool has_pending_signal_ = false;
+};
+
 ResponseBodyLoader::ResponseBodyLoader(
     BytesConsumer& bytes_consumer,
     ResponseBodyLoaderClient& client,
@@ -28,24 +266,38 @@
 mojo::ScopedDataPipeConsumerHandle ResponseBodyLoader::DrainAsDataPipe(
     ResponseBodyLoaderClient** client) {
   DCHECK(!started_);
+  DCHECK(!drained_);
+  DCHECK(!aborted_);
 
   *client = nullptr;
-  if (drained_ || aborted_) {
-    return {};
-  }
-
   DCHECK(bytes_consumer_);
   auto data_pipe = bytes_consumer_->DrainAsDataPipe();
-
-  if (data_pipe) {
-    drained_ = true;
-    bytes_consumer_ = nullptr;
-    *client = this;
+  if (!data_pipe) {
+    return data_pipe;
   }
 
+  drained_ = true;
+  bytes_consumer_ = nullptr;
+  *client = this;
   return data_pipe;
 }
 
+BytesConsumer& ResponseBodyLoader::DrainAsBytesConsumer() {
+  DCHECK(!started_);
+  DCHECK(!drained_);
+  DCHECK(!aborted_);
+  DCHECK(bytes_consumer_);
+  DCHECK(!delegating_bytes_consumer_);
+
+  delegating_bytes_consumer_ = MakeGarbageCollected<DelegatingBytesConsumer>(
+      *bytes_consumer_, *this, task_runner_);
+  bytes_consumer_->ClearClient();
+  bytes_consumer_->SetClient(delegating_bytes_consumer_);
+  bytes_consumer_ = nullptr;
+  drained_ = true;
+  return *delegating_bytes_consumer_;
+}
+
 void ResponseBodyLoader::DidReceiveData(base::span<const char> data) {
   if (aborted_)
     return;
@@ -79,6 +331,19 @@
   client_->DidFailLoadingBody();
 }
 
+void ResponseBodyLoader::DidCancelLoadingBody() {
+  if (aborted_)
+    return;
+
+  if (suspended_) {
+    cancel_signal_is_pending_ = true;
+    return;
+  }
+
+  cancel_signal_is_pending_ = false;
+  client_->DidCancelLoadingBody();
+}
+
 void ResponseBodyLoader::Start() {
   DCHECK(!started_);
   DCHECK(!drained_);
@@ -88,13 +353,19 @@
 }
 
 void ResponseBodyLoader::Abort() {
+  DCHECK(!suspended_);
   if (aborted_)
     return;
 
   aborted_ = true;
 
-  if (bytes_consumer_ && !in_two_phase_read_)
+  if (bytes_consumer_ && !in_two_phase_read_) {
     bytes_consumer_->Cancel();
+  }
+
+  if (delegating_bytes_consumer_) {
+    delegating_bytes_consumer_->Abort();
+  }
 }
 
 void ResponseBodyLoader::Suspend() {
@@ -120,6 +391,10 @@
     task_runner_->PostTask(
         FROM_HERE, base::BindOnce(&ResponseBodyLoader::DidFailLoadingBody,
                                   WrapPersistent(this)));
+  } else if (cancel_signal_is_pending_) {
+    task_runner_->PostTask(
+        FROM_HERE, base::BindOnce(&ResponseBodyLoader::DidCancelLoadingBody,
+                                  WrapPersistent(this)));
   } else {
     task_runner_->PostTask(FROM_HERE,
                            base::BindOnce(&ResponseBodyLoader::OnStateChange,
@@ -178,6 +453,7 @@
 
 void ResponseBodyLoader::Trace(Visitor* visitor) {
   visitor->Trace(bytes_consumer_);
+  visitor->Trace(delegating_bytes_consumer_);
   visitor->Trace(client_);
   ResponseBodyLoaderDrainableInterface::Trace(visitor);
   ResponseBodyLoaderClient::Trace(visitor);
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h
index 08a2fba..ce64c55 100644
--- a/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader.h
@@ -24,22 +24,35 @@
 class ResponseBodyLoader;
 
 // See ResponseBodyLoader for details. This is a virtual interface to expose
-// only DrainAsDataPipe function.
+// only Drain functions.
 class PLATFORM_EXPORT ResponseBodyLoaderDrainableInterface
     : public GarbageCollectedFinalized<ResponseBodyLoaderDrainableInterface> {
  public:
   virtual ~ResponseBodyLoaderDrainableInterface() = default;
 
-  // Drains the response body and returns it. This function must be called
-  // before calling Start(). This function may return an invalid handle when
-  // it is unable to convert the body to a data pipe, even when the body itself
-  // is valid. In that case, this function is no-op.
-  // If this function returns a valid handle, the caller is responsible for
-  // reading the body and providing the information to the client this
-  // function provides.
+  // Drains the response body and returns it. This function must not be called
+  // when the load has already been started or aborted, or the body has already
+  // been drained. This function may return an invalid handle when it is
+  // unable to convert the body to a data pipe, even when the body itself is
+  // valid. In that case, this function is no-op. If this function returns a
+  // valid handle, the caller is responsible for reading the body and providing
+  // the information to the client this function provides.
+  // Note that the notification from the client is *synchronously* propergated
+  // to the original client ResponseBodyLoader owns,  e.g., when the caller
+  // calls ResponseBodyLoaderClient::DidCancelLoadingBody, it synchronously
+  // cancels the resource loading (if |this| is associated with
+  // blink::ResourceLoader). A user of this function should ensure that calling
+  // the client's method doesn't lead to a reentrant problem.
   virtual mojo::ScopedDataPipeConsumerHandle DrainAsDataPipe(
       ResponseBodyLoaderClient** client) = 0;
 
+  // Drains the response body and returns it. This function must not be called
+  // when the load has already been started or aborted, or the body has already
+  // been drained. Unlike DrainAsDataPipe, this function always succeeds.
+  // This ResponseBodyLoader will still monitor the loading signals, and report
+  // them back to the associated client asynchronously.
+  virtual BytesConsumer& DrainAsBytesConsumer() = 0;
+
   virtual void Trace(Visitor*) {}
 };
 
@@ -50,6 +63,8 @@
 //  - By calling DrainAsDataPipe, a user can "drain" the contents from
 //    ResponseBodyLoader. The caller is responsible for reading the body and
 //    providing the information to the client this function provides.
+//  - By calling DrainBytesConsumer, a user can "drain" the contents from
+//    ResponseBodyLoader.
 // A ResponseBodyLoader is bound to the thread on which it is created.
 class PLATFORM_EXPORT ResponseBodyLoader final
     : public ResponseBodyLoaderDrainableInterface,
@@ -65,13 +80,14 @@
   // ResponseBodyLoaderDrainableInterface implementation.
   mojo::ScopedDataPipeConsumerHandle DrainAsDataPipe(
       ResponseBodyLoaderClient**) override;
+  BytesConsumer& DrainAsBytesConsumer() override;
 
   // Starts loading.
   void Start();
 
   // Aborts loading. This is expected to be called from the client's side, and
   // does not report the failure to the client. This doesn't affect a
-  // drained data pipe.
+  // drained data pipe. This function cannot be called when suspended.
   void Abort();
 
   // Suspendes loading.
@@ -94,16 +110,20 @@
   static constexpr size_t kMaxNumConsumedBytesInTask = 64 * 1024;
 
  private:
+  class DelegatingBytesConsumer;
+
   // ResponseBodyLoaderClient implementation.
   void DidReceiveData(base::span<const char> data) override;
   void DidFinishLoadingBody() override;
   void DidFailLoadingBody() override;
+  void DidCancelLoadingBody() override;
 
   // BytesConsumer::Client implementation.
   void OnStateChange() override;
   String DebugName() const override { return "ResponseBodyLoader"; }
 
   Member<BytesConsumer> bytes_consumer_;
+  Member<DelegatingBytesConsumer> delegating_bytes_consumer_;
   const Member<ResponseBodyLoaderClient> client_;
   const scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
   bool started_ = false;
@@ -112,6 +132,7 @@
   bool drained_ = false;
   bool finish_signal_is_pending_ = false;
   bool fail_signal_is_pending_ = false;
+  bool cancel_signal_is_pending_ = false;
   bool in_two_phase_read_ = false;
 };
 
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h
index 0710ea62..15384b8 100644
--- a/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_client.h
@@ -22,6 +22,9 @@
   // Called when seeing an error while reading the body. This must be the last
   // signal.
   virtual void DidFailLoadingBody() = 0;
+
+  // Called when the loader cancelled loading the body.
+  virtual void DidCancelLoadingBody() = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
index 5a974f4..c5fe311 100644
--- a/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
+++ b/third_party/blink/renderer/platform/loader/fetch/response_body_loader_test.cc
@@ -10,6 +10,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/renderer/platform/heap/persistent.h"
 #include "third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h"
+#include "third_party/blink/renderer/platform/loader/testing/bytes_consumer_test_reader.h"
 #include "third_party/blink/renderer/platform/loader/testing/replaying_bytes_consumer.h"
 #include "third_party/blink/renderer/platform/scheduler/test/fake_task_runner.h"
 
@@ -20,6 +21,8 @@
 class ResponseBodyLoaderTest : public testing::Test {
  protected:
   using Command = ReplayingBytesConsumer::Command;
+  using PublicState = BytesConsumer::PublicState;
+  using Result = BytesConsumer::Result;
   class TestClient final : public GarbageCollectedFinalized<TestClient>,
                            public ResponseBodyLoaderClient {
     USING_GARBAGE_COLLECTED_MIXIN(TestClient);
@@ -36,8 +39,9 @@
     virtual ~TestClient() {}
 
     String GetData() const { return data_; }
-    bool LoadingIsFinished() { return finished_; }
-    bool LoadingIsFailed() { return failed_; }
+    bool LoadingIsFinished() const { return finished_; }
+    bool LoadingIsFailed() const { return failed_; }
+    bool LoadingIsCancelled() const { return cancelled_; }
 
     void DidReceiveData(base::span<const char> data) override {
       DCHECK(!finished_);
@@ -64,6 +68,11 @@
       DCHECK(!failed_);
       failed_ = true;
     }
+    void DidCancelLoadingBody() override {
+      DCHECK(!finished_);
+      DCHECK(!failed_);
+      cancelled_ = true;
+    }
 
     void SetLoader(ResponseBodyLoader& loader) { loader_ = loader; }
     void Trace(Visitor* visitor) override { visitor->Trace(loader_); }
@@ -74,9 +83,65 @@
     String data_;
     bool finished_ = false;
     bool failed_ = false;
+    bool cancelled_ = false;
+  };
+
+  class ReadingClient final : public GarbageCollectedFinalized<ReadingClient>,
+                              public BytesConsumer::Client {
+    USING_GARBAGE_COLLECTED_MIXIN(ReadingClient);
+
+   public:
+    ReadingClient(BytesConsumer& bytes_consumer,
+                  TestClient& test_response_body_loader_client)
+        : bytes_consumer_(bytes_consumer),
+          test_response_body_loader_client_(test_response_body_loader_client) {}
+
+    void OnStateChangeInternal() {
+      while (true) {
+        const char* buffer = nullptr;
+        size_t available = 0;
+        Result result = bytes_consumer_->BeginRead(&buffer, &available);
+        if (result == Result::kShouldWait)
+          return;
+        if (result == Result::kOk) {
+          result = bytes_consumer_->EndRead(available);
+        }
+        if (result != Result::kOk)
+          return;
+      }
+    }
+
+    // BytesConsumer::Client implementation
+    void OnStateChange() override {
+      on_state_change_called_ = true;
+      OnStateChangeInternal();
+      // Notification is done asynchronously.
+      EXPECT_FALSE(test_response_body_loader_client_->LoadingIsCancelled());
+      EXPECT_FALSE(test_response_body_loader_client_->LoadingIsFinished());
+      EXPECT_FALSE(test_response_body_loader_client_->LoadingIsFailed());
+    }
+    String DebugName() const override { return "ReadingClient"; }
+    void Trace(Visitor* visitor) override {
+      visitor->Trace(bytes_consumer_);
+      visitor->Trace(test_response_body_loader_client_);
+      BytesConsumer::Client::Trace(visitor);
+    }
+
+    bool IsOnStateChangeCalled() const { return on_state_change_called_; }
+
+   private:
+    bool on_state_change_called_ = false;
+    const Member<BytesConsumer> bytes_consumer_;
+    const Member<TestClient> test_response_body_loader_client_;
   };
 };
 
+class ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest
+    : public ResponseBodyLoaderTest {};
+
+class ResponseBodyLoaderDrainedBytesConsumerNotificationInOnStateChangeTest
+    : public ResponseBodyLoaderTest {};
+
 TEST_F(ResponseBodyLoaderTest, Load) {
   auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
   auto* consumer = MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
@@ -407,6 +472,491 @@
   EXPECT_EQ("xyzabc", client->GetData());
 }
 
+TEST_F(ResponseBodyLoaderTest, DrainAsBytesConsumer) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kData, "he"));
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kData, "l"));
+  original_consumer->Add(Command(Command::kData, "lo"));
+  original_consumer->Add(Command(Command::kDone));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+
+  EXPECT_TRUE(body_loader->IsDrained());
+  EXPECT_NE(&consumer, original_consumer);
+
+  auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(&consumer);
+
+  auto result = reader->Run(task_runner.get());
+  EXPECT_EQ(result.first, BytesConsumer::Result::kDone);
+  EXPECT_EQ(String(result.second.data(), result.second.size()), "hello");
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  EXPECT_EQ("hello", client->GetData());
+}
+
+TEST_F(ResponseBodyLoaderTest, CancelDrainedBytesConsumer) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kData, "he"));
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kData, "llo"));
+  original_consumer->Add(Command(Command::kDone));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+
+  EXPECT_TRUE(body_loader->IsDrained());
+  EXPECT_NE(&consumer, original_consumer);
+  consumer.Cancel();
+
+  auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(&consumer);
+
+  auto result = reader->Run(task_runner.get());
+  EXPECT_EQ(result.first, BytesConsumer::Result::kDone);
+  EXPECT_EQ(String(result.second.data(), result.second.size()), String());
+
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_TRUE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderTest, DrainAsBytesConsumerWithError) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kData, "he"));
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kData, "llo"));
+  original_consumer->Add(Command(Command::kError));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+
+  EXPECT_TRUE(body_loader->IsDrained());
+  EXPECT_NE(&consumer, original_consumer);
+
+  auto* reader = MakeGarbageCollected<BytesConsumerTestReader>(&consumer);
+
+  auto result = reader->Run(task_runner.get());
+  EXPECT_EQ(result.first, BytesConsumer::Result::kError);
+  EXPECT_EQ(String(result.second.data(), result.second.size()), "hello");
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_TRUE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderTest, AbortAfterBytesConsumerIsDrained) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kData, "he"));
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kData, "llo"));
+  original_consumer->Add(Command(Command::kDone));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+  auto* bytes_consumer_client =
+      MakeGarbageCollected<ReadingClient>(consumer, *client);
+  consumer.SetClient(bytes_consumer_client);
+
+  EXPECT_TRUE(body_loader->IsDrained());
+  EXPECT_NE(&consumer, original_consumer);
+
+  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer.GetPublicState());
+  EXPECT_FALSE(bytes_consumer_client->IsOnStateChangeCalled());
+  body_loader->Abort();
+  EXPECT_EQ(PublicState::kErrored, consumer.GetPublicState());
+  EXPECT_TRUE(bytes_consumer_client->IsOnStateChangeCalled());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderTest, AbortAfterBytesConsumerIsDrainedIsNotified) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+
+  EXPECT_TRUE(body_loader->IsDrained());
+  EXPECT_NE(&consumer, original_consumer);
+
+  EXPECT_EQ(PublicState::kReadableOrWaiting, consumer.GetPublicState());
+  body_loader->Abort();
+  EXPECT_EQ(PublicState::kErrored, consumer.GetPublicState());
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest,
+       BeginReadAndDone) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kDataAndDone, "hello"));
+  original_consumer->Add(Command(Command::kDone));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+
+  const char* buffer = nullptr;
+  size_t available = 0;
+  Result result = consumer.BeginRead(&buffer, &available);
+
+  EXPECT_EQ(result, Result::kShouldWait);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  task_runner->RunUntilIdle();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  result = consumer.BeginRead(&buffer, &available);
+  EXPECT_EQ(result, Result::kOk);
+  ASSERT_EQ(available, 5u);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  result = consumer.EndRead(available);
+  EXPECT_EQ(result, Result::kDone);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  task_runner->RunUntilIdle();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest,
+       BeginReadAndError) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kData, "hello"));
+  original_consumer->Add(Command(Command::kError));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+
+  const char* buffer = nullptr;
+  size_t available = 0;
+  Result result = consumer.BeginRead(&buffer, &available);
+
+  EXPECT_EQ(result, Result::kShouldWait);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  task_runner->RunUntilIdle();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  result = consumer.BeginRead(&buffer, &available);
+  EXPECT_EQ(result, Result::kOk);
+  ASSERT_EQ(available, 5u);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  result = consumer.EndRead(available);
+  EXPECT_EQ(result, Result::kOk);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  result = consumer.BeginRead(&buffer, &available);
+  EXPECT_EQ(result, Result::kError);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  task_runner->RunUntilIdle();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_TRUE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest,
+       EndReadAndDone) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kDataAndDone, "hello"));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+
+  const char* buffer = nullptr;
+  size_t available = 0;
+  Result result = consumer.BeginRead(&buffer, &available);
+
+  EXPECT_EQ(result, Result::kShouldWait);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  task_runner->RunUntilIdle();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  result = consumer.BeginRead(&buffer, &available);
+  EXPECT_EQ(result, Result::kOk);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+  ASSERT_EQ(5u, available);
+  EXPECT_EQ(String(buffer, available), "hello");
+
+  task_runner->RunUntilIdle();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  result = consumer.EndRead(available);
+  EXPECT_EQ(result, Result::kDone);
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  task_runner->RunUntilIdle();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest,
+       DrainAsDataPipe) {
+  mojo::ScopedDataPipeConsumerHandle consumer_end;
+  mojo::ScopedDataPipeProducerHandle producer_end;
+  auto result = mojo::CreateDataPipe(nullptr, &producer_end, &consumer_end);
+
+  ASSERT_EQ(result, MOJO_RESULT_OK);
+
+  DataPipeBytesConsumer::CompletionNotifier* completion_notifier = nullptr;
+
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer = MakeGarbageCollected<DataPipeBytesConsumer>(
+      task_runner, std::move(consumer_end), &completion_notifier);
+  auto* client = MakeGarbageCollected<TestClient>();
+
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+
+  EXPECT_TRUE(consumer.DrainAsDataPipe());
+
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  task_runner->RunUntilIdle();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationOutOfOnStateChangeTest,
+       Cancel) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kWait));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+
+  task_runner->RunUntilIdle();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  consumer.Cancel();
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+
+  task_runner->RunUntilIdle();
+  EXPECT_TRUE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationInOnStateChangeTest,
+       BeginReadAndDone) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kDone));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+  auto* reading_client = MakeGarbageCollected<ReadingClient>(consumer, *client);
+  consumer.SetClient(reading_client);
+
+  const char* buffer = nullptr;
+  size_t available = 0;
+  // This BeginRead posts a task which calls OnStateChange.
+  Result result = consumer.BeginRead(&buffer, &available);
+  EXPECT_EQ(result, Result::kShouldWait);
+
+  // We'll see the change without waiting for another task.
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(
+                            [](TestClient* client) {
+                              EXPECT_FALSE(client->LoadingIsCancelled());
+                              EXPECT_TRUE(client->LoadingIsFinished());
+                              EXPECT_FALSE(client->LoadingIsFailed());
+                            },
+                            WrapPersistent(client)));
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_TRUE(reading_client->IsOnStateChangeCalled());
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationInOnStateChangeTest,
+       BeginReadAndError) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kError));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+  auto* reading_client = MakeGarbageCollected<ReadingClient>(consumer, *client);
+  consumer.SetClient(reading_client);
+
+  const char* buffer = nullptr;
+  size_t available = 0;
+  // This BeginRead posts a task which calls OnStateChange.
+  Result result = consumer.BeginRead(&buffer, &available);
+  EXPECT_EQ(result, Result::kShouldWait);
+
+  // We'll see the change without waiting for another task.
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(
+                            [](TestClient* client) {
+                              EXPECT_FALSE(client->LoadingIsCancelled());
+                              EXPECT_FALSE(client->LoadingIsFinished());
+                              EXPECT_TRUE(client->LoadingIsFailed());
+                            },
+                            WrapPersistent(client)));
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_TRUE(reading_client->IsOnStateChangeCalled());
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_FALSE(client->LoadingIsFinished());
+  EXPECT_TRUE(client->LoadingIsFailed());
+}
+
+TEST_F(ResponseBodyLoaderDrainedBytesConsumerNotificationInOnStateChangeTest,
+       EndReadAndDone) {
+  auto task_runner = base::MakeRefCounted<scheduler::FakeTaskRunner>();
+  auto* original_consumer =
+      MakeGarbageCollected<ReplayingBytesConsumer>(task_runner);
+  original_consumer->Add(Command(Command::kWait));
+  original_consumer->Add(Command(Command::kDataAndDone, "hahaha"));
+
+  auto* client = MakeGarbageCollected<TestClient>();
+  auto* body_loader = MakeGarbageCollected<ResponseBodyLoader>(
+      *original_consumer, *client, task_runner);
+
+  BytesConsumer& consumer = body_loader->DrainAsBytesConsumer();
+  auto* reading_client = MakeGarbageCollected<ReadingClient>(consumer, *client);
+  consumer.SetClient(reading_client);
+
+  const char* buffer = nullptr;
+  size_t available = 0;
+  // This BeginRead posts a task which calls OnStateChange.
+  Result result = consumer.BeginRead(&buffer, &available);
+  EXPECT_EQ(result, Result::kShouldWait);
+
+  // We'll see the change without waiting for another task.
+  task_runner->PostTask(FROM_HERE,
+                        base::BindOnce(
+                            [](TestClient* client) {
+                              EXPECT_FALSE(client->LoadingIsCancelled());
+                              EXPECT_TRUE(client->LoadingIsFinished());
+                              EXPECT_FALSE(client->LoadingIsFailed());
+                            },
+                            WrapPersistent(client)));
+
+  task_runner->RunUntilIdle();
+
+  EXPECT_TRUE(reading_client->IsOnStateChangeCalled());
+  EXPECT_FALSE(client->LoadingIsCancelled());
+  EXPECT_TRUE(client->LoadingIsFinished());
+  EXPECT_FALSE(client->LoadingIsFailed());
+}
+
 }  // namespace
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/loader/link_header_test.cc b/third_party/blink/renderer/platform/loader/link_header_test.cc
index a82f7b89..c81e18d8 100644
--- a/third_party/blink/renderer/platform/loader/link_header_test.cc
+++ b/third_party/blink/renderer/platform/loader/link_header_test.cc
@@ -173,9 +173,9 @@
   }
 }
 
-INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
-                        SingleLinkHeaderTest,
-                        testing::ValuesIn(g_single_test_cases));
+INSTANTIATE_TEST_SUITE_P(LinkHeaderTest,
+                         SingleLinkHeaderTest,
+                         testing::ValuesIn(g_single_test_cases));
 
 struct DoubleTestCase {
   const char* header_value;
@@ -217,9 +217,9 @@
   EXPECT_EQ(test_case.valid2, header2.Valid());
 }
 
-INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
-                        DoubleLinkHeaderTest,
-                        testing::ValuesIn(g_double_test_cases));
+INSTANTIATE_TEST_SUITE_P(LinkHeaderTest,
+                         DoubleLinkHeaderTest,
+                         testing::ValuesIn(g_double_test_cases));
 
 struct CrossOriginTestCase {
   const char* header_value;
@@ -279,9 +279,9 @@
     EXPECT_STREQ(test_case.crossorigin, header.CrossOrigin().Ascii().data());
 }
 
-INSTANTIATE_TEST_CASE_P(LinkHeaderTest,
-                        CrossOriginLinkHeaderTest,
-                        testing::ValuesIn(g_cross_origin_test_cases));
+INSTANTIATE_TEST_SUITE_P(LinkHeaderTest,
+                         CrossOriginLinkHeaderTest,
+                         testing::ValuesIn(g_cross_origin_test_cases));
 
 }  // namespace
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/memory_coordinator.cc b/third_party/blink/renderer/platform/memory_coordinator.cc
deleted file mode 100644
index 54ed9a59..0000000
--- a/third_party/blink/renderer/platform/memory_coordinator.cc
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
-
-#include "base/system/sys_info.h"
-#include "build/build_config.h"
-#include "third_party/blink/public/common/device_memory/approximated_device_memory.h"
-#include "third_party/blink/public/web/blink.h"
-#include "third_party/blink/renderer/platform/cross_thread_functional.h"
-#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
-#include "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
-#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
-#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
-
-#if defined(OS_ANDROID)
-#include "base/android/sys_utils.h"
-#endif
-
-namespace blink {
-
-// Wrapper function defined in WebKit.h
-void DecommitFreeableMemory() {
-  WTF::Partitions::DecommitFreeableMemory();
-}
-
-// static
-bool MemoryCoordinator::is_low_end_device_ = false;
-
-// static
-bool MemoryCoordinator::IsLowEndDevice() {
-  return is_low_end_device_;
-}
-
-// static
-bool MemoryCoordinator::IsCurrentlyLowMemory() {
-#if defined(OS_ANDROID)
-  return base::android::SysUtils::IsCurrentlyLowMemory();
-#else
-  return false;
-#endif
-}
-
-// static
-void MemoryCoordinator::Initialize() {
-  is_low_end_device_ = ::base::SysInfo::IsLowEndDevice();
-  ApproximatedDeviceMemory::Initialize();
-}
-
-// static
-void MemoryCoordinator::SetIsLowEndDeviceForTesting(bool is_low_end_device) {
-  is_low_end_device_ = is_low_end_device;
-}
-
-// static
-MemoryCoordinator& MemoryCoordinator::Instance() {
-  DEFINE_THREAD_SAFE_STATIC_LOCAL(CrossThreadPersistent<MemoryCoordinator>,
-                                  external,
-                                  (MakeGarbageCollected<MemoryCoordinator>()));
-  return *external.Get();
-}
-
-void MemoryCoordinator::RegisterThread(Thread* thread) {
-  MutexLocker lock(threads_mutex_);
-  threads_.insert(thread);
-}
-
-void MemoryCoordinator::UnregisterThread(Thread* thread) {
-  MutexLocker lock(threads_mutex_);
-  threads_.erase(thread);
-}
-
-MemoryCoordinator::MemoryCoordinator() = default;
-
-void MemoryCoordinator::RegisterClient(MemoryCoordinatorClient* client) {
-  DCHECK(IsMainThread());
-  DCHECK(client);
-  DCHECK(!clients_.Contains(client));
-  clients_.insert(client);
-}
-
-void MemoryCoordinator::UnregisterClient(MemoryCoordinatorClient* client) {
-  DCHECK(IsMainThread());
-  clients_.erase(client);
-}
-
-void MemoryCoordinator::OnMemoryPressure(WebMemoryPressureLevel level) {
-  TRACE_EVENT0("blink", "MemoryCoordinator::onMemoryPressure");
-  for (auto& client : clients_)
-    client->OnMemoryPressure(level);
-  if (level == kWebMemoryPressureLevelCritical)
-    ClearMemory();
-  WTF::Partitions::DecommitFreeableMemory();
-}
-
-void MemoryCoordinator::OnMemoryStateChange(MemoryState state) {
-  for (auto& client : clients_)
-    client->OnMemoryStateChange(state);
-}
-
-void MemoryCoordinator::OnPurgeMemory() {
-  for (auto& client : clients_)
-    client->OnPurgeMemory();
-  // Don't call clearMemory() because font cache invalidation always causes full
-  // layout. This increases tab switching cost significantly (e.g.
-  // en.wikipedia.org/wiki/Wikipedia). So we should not invalidate the font
-  // cache in purge+throttle.
-  ImageDecodingStore::Instance().Clear();
-  WTF::Partitions::DecommitFreeableMemory();
-
-  // Thread-specific data never issues a layout, so we are safe here.
-  MutexLocker lock(threads_mutex_);
-  for (auto* thread : threads_) {
-    if (!thread->GetTaskRunner())
-      continue;
-
-    PostCrossThreadTask(
-        *thread->GetTaskRunner(), FROM_HERE,
-        CrossThreadBind(MemoryCoordinator::ClearThreadSpecificMemory));
-  }
-}
-
-void MemoryCoordinator::ClearMemory() {
-  // TODO(tasak|bashi): Make FontCache a MemoryCoordinatorClient rather than
-  // clearing caches here.
-  FontGlobalContext::ClearMemory();
-}
-
-void MemoryCoordinator::ClearThreadSpecificMemory() {
-  FontGlobalContext::ClearMemory();
-}
-
-void MemoryCoordinator::Trace(blink::Visitor* visitor) {
-  visitor->Trace(clients_);
-}
-
-}  // namespace blink
diff --git a/third_party/blink/renderer/platform/memory_coordinator.h b/third_party/blink/renderer/platform/memory_coordinator.h
deleted file mode 100644
index 7ea494e7..0000000
--- a/third_party/blink/renderer/platform/memory_coordinator.h
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2015 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEMORY_COORDINATOR_H_
-#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEMORY_COORDINATOR_H_
-
-#include "third_party/blink/public/platform/web_memory_pressure_level.h"
-#include "third_party/blink/public/platform/web_memory_state.h"
-#include "third_party/blink/renderer/platform/heap/handle.h"
-#include "third_party/blink/renderer/platform/platform_export.h"
-#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
-#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
-#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
-
-namespace blink {
-
-class PLATFORM_EXPORT MemoryCoordinatorClient : public GarbageCollectedMixin {
- public:
-  virtual ~MemoryCoordinatorClient() = default;
-
-  // TODO(bashi): Deprecating. Remove this when MemoryPressureListener is
-  // gone.
-  virtual void OnMemoryPressure(WebMemoryPressureLevel) {}
-
-  virtual void OnMemoryStateChange(MemoryState) {}
-
-  virtual void OnPurgeMemory() {}
-};
-
-// MemoryCoordinator listens to some events which could be opportunities
-// for reducing memory consumption and notifies its clients.
-class PLATFORM_EXPORT MemoryCoordinator final
-    : public GarbageCollectedFinalized<MemoryCoordinator> {
-  WTF_MAKE_NONCOPYABLE(MemoryCoordinator);
-
- public:
-  static MemoryCoordinator& Instance();
-
-  // Whether the device Blink runs on is a low-end device.
-  // Can be overridden in web tests via internals.
-  static bool IsLowEndDevice();
-
-  // Returns true when available memory is low.
-  // This is not cheap and should not be called repeatedly.
-  // TODO(keishi): Remove when MemoryState is ready.
-  static bool IsCurrentlyLowMemory();
-
-  // Caches whether this device is a low-end device and the device physical
-  // memory in static members. instance() is not used as it's a heap allocated
-  // object - meaning it's not thread-safe as well as might break tests counting
-  // the heap size.
-  static void Initialize();
-
-  MemoryCoordinator();
-
-  void RegisterThread(Thread*) LOCKS_EXCLUDED(threads_mutex_);
-  void UnregisterThread(Thread*) LOCKS_EXCLUDED(threads_mutex_);
-
-  void RegisterClient(MemoryCoordinatorClient*);
-  void UnregisterClient(MemoryCoordinatorClient*);
-
-  // TODO(bashi): Deprecating. Remove this when MemoryPressureListener is
-  // gone.
-  void OnMemoryPressure(WebMemoryPressureLevel);
-
-  void OnMemoryStateChange(MemoryState);
-
-  void OnPurgeMemory();
-
-  void Trace(blink::Visitor*);
-
- private:
-  friend class Internals;
-
-  static void SetIsLowEndDeviceForTesting(bool);
-
-  void ClearMemory();
-  static void ClearThreadSpecificMemory();
-
-  static bool is_low_end_device_;
-
-  HeapHashSet<WeakMember<MemoryCoordinatorClient>> clients_;
-  HashSet<Thread*> threads_;
-  Mutex threads_mutex_;
-};
-
-}  // namespace blink
-
-#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEMORY_COORDINATOR_H_
diff --git a/third_party/blink/renderer/platform/memory_pressure_listener.cc b/third_party/blink/renderer/platform/memory_pressure_listener.cc
new file mode 100644
index 0000000..9a2a8e5c
--- /dev/null
+++ b/third_party/blink/renderer/platform/memory_pressure_listener.cc
@@ -0,0 +1,140 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
+
+#include "base/system/sys_info.h"
+#include "build/build_config.h"
+#include "third_party/blink/public/common/device_memory/approximated_device_memory.h"
+#include "third_party/blink/public/web/blink.h"
+#include "third_party/blink/renderer/platform/cross_thread_functional.h"
+#include "third_party/blink/renderer/platform/fonts/font_global_context.h"
+#include "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
+#include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
+#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/wtf/allocator/partitions.h"
+
+#if defined(OS_ANDROID)
+#include "base/android/sys_utils.h"
+#endif
+
+namespace blink {
+
+// Wrapper function defined in WebKit.h
+void DecommitFreeableMemory() {
+  WTF::Partitions::DecommitFreeableMemory();
+}
+
+// static
+bool MemoryPressureListenerRegistry::is_low_end_device_ = false;
+
+// static
+bool MemoryPressureListenerRegistry::IsLowEndDevice() {
+  return is_low_end_device_;
+}
+
+// static
+bool MemoryPressureListenerRegistry::IsCurrentlyLowMemory() {
+#if defined(OS_ANDROID)
+  return base::android::SysUtils::IsCurrentlyLowMemory();
+#else
+  return false;
+#endif
+}
+
+// static
+void MemoryPressureListenerRegistry::Initialize() {
+  is_low_end_device_ = ::base::SysInfo::IsLowEndDevice();
+  ApproximatedDeviceMemory::Initialize();
+}
+
+// static
+void MemoryPressureListenerRegistry::SetIsLowEndDeviceForTesting(
+    bool is_low_end_device) {
+  is_low_end_device_ = is_low_end_device;
+}
+
+// static
+MemoryPressureListenerRegistry& MemoryPressureListenerRegistry::Instance() {
+  DEFINE_THREAD_SAFE_STATIC_LOCAL(
+      CrossThreadPersistent<MemoryPressureListenerRegistry>, external,
+      (MakeGarbageCollected<MemoryPressureListenerRegistry>()));
+  return *external.Get();
+}
+
+void MemoryPressureListenerRegistry::RegisterThread(Thread* thread) {
+  MutexLocker lock(threads_mutex_);
+  threads_.insert(thread);
+}
+
+void MemoryPressureListenerRegistry::UnregisterThread(Thread* thread) {
+  MutexLocker lock(threads_mutex_);
+  threads_.erase(thread);
+}
+
+MemoryPressureListenerRegistry::MemoryPressureListenerRegistry() = default;
+
+void MemoryPressureListenerRegistry::RegisterClient(
+    MemoryPressureListener* client) {
+  DCHECK(IsMainThread());
+  DCHECK(client);
+  DCHECK(!clients_.Contains(client));
+  clients_.insert(client);
+}
+
+void MemoryPressureListenerRegistry::UnregisterClient(
+    MemoryPressureListener* client) {
+  DCHECK(IsMainThread());
+  clients_.erase(client);
+}
+
+void MemoryPressureListenerRegistry::OnMemoryPressure(
+    WebMemoryPressureLevel level) {
+  TRACE_EVENT0("blink", "MemoryPressureListenerRegistry::onMemoryPressure");
+  for (auto& client : clients_)
+    client->OnMemoryPressure(level);
+  if (level == kWebMemoryPressureLevelCritical)
+    ClearMemory();
+  WTF::Partitions::DecommitFreeableMemory();
+}
+
+void MemoryPressureListenerRegistry::OnPurgeMemory() {
+  for (auto& client : clients_)
+    client->OnPurgeMemory();
+  // Don't call clearMemory() because font cache invalidation always causes full
+  // layout. This increases tab switching cost significantly (e.g.
+  // en.wikipedia.org/wiki/Wikipedia). So we should not invalidate the font
+  // cache in purge+throttle.
+  ImageDecodingStore::Instance().Clear();
+  WTF::Partitions::DecommitFreeableMemory();
+
+  // Thread-specific data never issues a layout, so we are safe here.
+  MutexLocker lock(threads_mutex_);
+  for (auto* thread : threads_) {
+    if (!thread->GetTaskRunner())
+      continue;
+
+    PostCrossThreadTask(
+        *thread->GetTaskRunner(), FROM_HERE,
+        CrossThreadBind(
+            MemoryPressureListenerRegistry::ClearThreadSpecificMemory));
+  }
+}
+
+void MemoryPressureListenerRegistry::ClearMemory() {
+  // TODO(tasak|bashi): Make FontCache a MemoryPressureListener rather than
+  // clearing caches here.
+  FontGlobalContext::ClearMemory();
+}
+
+void MemoryPressureListenerRegistry::ClearThreadSpecificMemory() {
+  FontGlobalContext::ClearMemory();
+}
+
+void MemoryPressureListenerRegistry::Trace(blink::Visitor* visitor) {
+  visitor->Trace(clients_);
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/memory_pressure_listener.h b/third_party/blink/renderer/platform/memory_pressure_listener.h
new file mode 100644
index 0000000..63b8aa9
--- /dev/null
+++ b/third_party/blink/renderer/platform/memory_pressure_listener.h
@@ -0,0 +1,80 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEMORY_PRESSURE_LISTENER_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEMORY_PRESSURE_LISTENER_H_
+
+#include "third_party/blink/public/platform/web_memory_pressure_level.h"
+#include "third_party/blink/renderer/platform/heap/handle.h"
+#include "third_party/blink/renderer/platform/platform_export.h"
+#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
+#include "third_party/blink/renderer/platform/wtf/noncopyable.h"
+#include "third_party/blink/renderer/platform/wtf/threading_primitives.h"
+
+namespace blink {
+
+class PLATFORM_EXPORT MemoryPressureListener : public GarbageCollectedMixin {
+ public:
+  virtual ~MemoryPressureListener() = default;
+
+  virtual void OnMemoryPressure(WebMemoryPressureLevel) {}
+
+  virtual void OnPurgeMemory() {}
+};
+
+// MemoryPressureListenerRegistry listens to some events which could be
+// opportunities for reducing memory consumption and notifies its clients.
+class PLATFORM_EXPORT MemoryPressureListenerRegistry final
+    : public GarbageCollectedFinalized<MemoryPressureListenerRegistry> {
+  WTF_MAKE_NONCOPYABLE(MemoryPressureListenerRegistry);
+
+ public:
+  static MemoryPressureListenerRegistry& Instance();
+
+  // Whether the device Blink runs on is a low-end device.
+  // Can be overridden in web tests via internals.
+  static bool IsLowEndDevice();
+
+  // Returns true when available memory is low.
+  // This is not cheap and should not be called repeatedly.
+  static bool IsCurrentlyLowMemory();
+
+  // Caches whether this device is a low-end device and the device physical
+  // memory in static members. instance() is not used as it's a heap allocated
+  // object - meaning it's not thread-safe as well as might break tests counting
+  // the heap size.
+  static void Initialize();
+
+  MemoryPressureListenerRegistry();
+
+  void RegisterThread(Thread*) LOCKS_EXCLUDED(threads_mutex_);
+  void UnregisterThread(Thread*) LOCKS_EXCLUDED(threads_mutex_);
+
+  void RegisterClient(MemoryPressureListener*);
+  void UnregisterClient(MemoryPressureListener*);
+
+  void OnMemoryPressure(WebMemoryPressureLevel);
+
+  void OnPurgeMemory();
+
+  void Trace(blink::Visitor*);
+
+ private:
+  friend class Internals;
+
+  static void SetIsLowEndDeviceForTesting(bool);
+
+  void ClearMemory();
+  static void ClearThreadSpecificMemory();
+
+  static bool is_low_end_device_;
+
+  HeapHashSet<WeakMember<MemoryPressureListener>> clients_;
+  HashSet<Thread*> threads_;
+  Mutex threads_mutex_;
+};
+
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_MEMORY_PRESSURE_LISTENER_H_
diff --git a/third_party/blink/renderer/platform/network/http_names.json5 b/third_party/blink/renderer/platform/network/http_names.json5
index 9d797a1..27db4b1d3 100644
--- a/third_party/blink/renderer/platform/network/http_names.json5
+++ b/third_party/blink/renderer/platform/network/http_names.json5
@@ -58,6 +58,7 @@
     "Refresh",
     "Resource-Freshness",
     "Save-Data",
+    "Sec-CH-Lang",
     "Sec-Required-CSP",
     "Server-Timing",
     "SourceMap",
diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5
index d2b2f56..a3aa06fc 100644
--- a/third_party/blink/renderer/platform/runtime_enabled_features.json5
+++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5
@@ -451,6 +451,13 @@
       origin_trial_feature_name: "ExperimentalHardwareEchoCancellation2",
       status: "experimental",
     },
+    // Enables navigator.scheduling.isInputPending, allowing long-running JS to
+    // be able to yield itself when user input is queued (crbug.com/910421).
+    {
+      name: "ExperimentalIsInputPending",
+      origin_trial_feature_name: "ExperimentalIsInputPending",
+      status: "experimental"
+    },
     // Enables a set of features intended to help improve web developer
     // productivity, by restricting the use of potentially problematic web-
     // platform behaviors, as well as adding new high-level APIs for common
@@ -590,6 +597,7 @@
     },
     {
       name: "HeapUnifiedGarbageCollection",
+      status: "stable",
     },
     {
       name: "HrefTranslate",
@@ -685,6 +693,10 @@
       name: "LangAttributeAwareFormControlUI",
     },
     {
+      name: "LangClientHintHeader",
+      status: "experimental",
+    },
+    {
       name: "LayeredAPI",
       implied_by: ["ExperimentalProductivityFeatures"],
     },
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index 3257835..71b4d17 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -83,6 +83,8 @@
     "main_thread/page_scheduler_impl.h",
     "main_thread/page_visibility_state.cc",
     "main_thread/page_visibility_state.h",
+    "main_thread/pending_user_input.cc",
+    "main_thread/pending_user_input.h",
     "main_thread/prioritize_compositing_after_input_experiment.cc",
     "main_thread/prioritize_compositing_after_input_experiment.h",
     "main_thread/queueing_time_estimator.cc",
@@ -105,6 +107,7 @@
     "public/frame_status.h",
     "public/page_lifecycle_state.h",
     "public/page_scheduler.h",
+    "public/pending_user_input_type.h",
     "public/post_cancellable_task.h",
     "public/post_cross_thread_task.h",
     "public/scheduling_lifecycle_state.h",
@@ -194,6 +197,7 @@
     "main_thread/main_thread_scheduler_impl_unittest.cc",
     "main_thread/main_thread_unittest.cc",
     "main_thread/page_scheduler_impl_unittest.cc",
+    "main_thread/pending_user_input_unittest.cc",
     "main_thread/queueing_time_estimator_unittest.cc",
     "main_thread/render_widget_signals_unittest.cc",
     "main_thread/user_model_unittest.cc",
diff --git a/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
index c0756751..00850c8 100644
--- a/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/common/web_thread_scheduler.cc
@@ -113,6 +113,16 @@
   NOTREACHED();
 }
 
+void WebThreadScheduler::WillPostInputEventToMainThread(
+    WebInputEvent::Type web_input_event_type) {
+  NOTREACHED();
+}
+
+void WebThreadScheduler::WillHandleInputEventOnMainThread(
+    WebInputEvent::Type web_input_event_type) {
+  NOTREACHED();
+}
+
 void WebThreadScheduler::DidHandleInputEventOnMainThread(
     const WebInputEvent& web_input_event,
     WebInputEventResult result) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.cc b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.cc
index 1009395..a4c8f51f 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.cc
@@ -26,7 +26,6 @@
     : task_starvation_count_(0),
       max_task_starvation_count_(0),
       can_advance_virtual_time_(true),
-      observer_(nullptr),
       helper_(helper),
       now_ticks_(initial_time_ticks),
       initial_time_ticks_(initial_time_ticks),
@@ -118,10 +117,6 @@
     RequestDoWork();
 }
 
-void AutoAdvancingVirtualTimeDomain::SetObserver(Observer* observer) {
-  observer_ = observer;
-}
-
 void AutoAdvancingVirtualTimeDomain::SetCanAdvanceVirtualTime(
     bool can_advance_virtual_time) {
   can_advance_virtual_time_ = can_advance_virtual_time;
@@ -162,9 +157,6 @@
     now_ticks_ = new_virtual_time;
   }
 
-  if (observer_)
-    observer_->OnVirtualTimeAdvanced();
-
   return true;
 }
 
@@ -209,9 +201,5 @@
   return AutoAdvancingVirtualTimeDomain::g_time_domain_->Date();
 }
 
-AutoAdvancingVirtualTimeDomain::Observer::Observer() = default;
-
-AutoAdvancingVirtualTimeDomain::Observer::~Observer() = default;
-
 }  // namespace scheduler
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h
index bd68b93..c5b2af5 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h
@@ -36,19 +36,6 @@
                                  BaseTimeOverridePolicy policy);
   ~AutoAdvancingVirtualTimeDomain() override;
 
-  class PLATFORM_EXPORT Observer {
-   public:
-    Observer();
-    virtual ~Observer();
-
-    // Notification received when the virtual time advances.
-    virtual void OnVirtualTimeAdvanced() = 0;
-  };
-
-  // Note its assumed that |observer| will either remove itself or last at least
-  // as long as this AutoAdvancingVirtualTimeDomain.
-  void SetObserver(Observer* observer);
-
   // Controls whether or not virtual time is allowed to advance, when the
   // SequenceManager runs out of immediate work to do.
   void SetCanAdvanceVirtualTime(bool can_advance_virtual_time);
@@ -102,7 +89,6 @@
   int max_task_starvation_count_;
 
   bool can_advance_virtual_time_;
-  Observer* observer_;       // NOT OWNED
   SchedulerHelper* helper_;  // NOT OWNED
 
   // VirtualTime is usually doled out in 100ms intervals using fences and this
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc
index 2c3ba99..96faab2b 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain_unittest.cc
@@ -67,55 +67,8 @@
   *task_run = true;
 }
 
-class MockObserver : public AutoAdvancingVirtualTimeDomain::Observer {
- public:
-  MOCK_METHOD0(OnVirtualTimeAdvanced, void());
-};
-
 }  // namespace
 
-TEST_F(AutoAdvancingVirtualTimeDomainTest, VirtualTimeAdvances) {
-  MockObserver mock_observer;
-  auto_advancing_time_domain_->SetObserver(&mock_observer);
-
-  base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
-  bool task_run = false;
-  task_queue_->task_runner()->PostDelayedTask(
-      FROM_HERE, base::BindOnce(NopTask, &task_run), delay);
-
-  EXPECT_CALL(mock_observer, OnVirtualTimeAdvanced());
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(initial_time_ticks_, test_task_runner_->NowTicks());
-  EXPECT_EQ(initial_time_ticks_ + delay,
-            auto_advancing_time_domain_->CreateLazyNow().Now());
-  EXPECT_TRUE(task_run);
-
-  auto_advancing_time_domain_->SetObserver(nullptr);
-}
-
-TEST_F(AutoAdvancingVirtualTimeDomainTest, VirtualTimeDoesNotAdvance) {
-  MockObserver mock_observer;
-  auto_advancing_time_domain_->SetObserver(&mock_observer);
-
-  base::TimeDelta delay = base::TimeDelta::FromMilliseconds(10);
-  bool task_run = false;
-  task_queue_->task_runner()->PostDelayedTask(
-      FROM_HERE, base::BindOnce(NopTask, &task_run), delay);
-
-  auto_advancing_time_domain_->SetCanAdvanceVirtualTime(false);
-
-  EXPECT_CALL(mock_observer, OnVirtualTimeAdvanced()).Times(0);
-  base::RunLoop().RunUntilIdle();
-
-  EXPECT_EQ(initial_time_ticks_, test_task_runner_->NowTicks());
-  EXPECT_EQ(initial_time_ticks_,
-            auto_advancing_time_domain_->CreateLazyNow().Now());
-  EXPECT_FALSE(task_run);
-
-  auto_advancing_time_domain_->SetObserver(nullptr);
-}
-
 namespace {
 void RepostingTask(scoped_refptr<base::sequence_manager::TaskQueue> task_queue,
                    int max_count,
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
index 07ceb23..34148a3 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc
@@ -1180,6 +1180,20 @@
   GetCompositorThreadOnly().last_input_type = type;
 }
 
+void MainThreadSchedulerImpl::WillPostInputEventToMainThread(
+    WebInputEvent::Type web_input_event_type) {
+  base::AutoLock lock(any_thread_lock_);
+  any_thread().pending_input_monitor.OnEnqueue(web_input_event_type);
+}
+
+void MainThreadSchedulerImpl::WillHandleInputEventOnMainThread(
+    WebInputEvent::Type web_input_event_type) {
+  helper_.CheckOnValidThread();
+
+  base::AutoLock lock(any_thread_lock_);
+  any_thread().pending_input_monitor.OnDequeue(web_input_event_type);
+}
+
 void MainThreadSchedulerImpl::DidHandleInputEventOnMainThread(
     const WebInputEvent& web_input_event,
     WebInputEventResult result) {
@@ -1669,7 +1683,6 @@
           main_thread_only().initial_virtual_time_offset,
       &helper_, policy));
   RegisterTimeDomain(virtual_time_domain_.get());
-  virtual_time_domain_->SetObserver(this);
 
   DCHECK(!virtual_time_control_task_queue_);
   virtual_time_control_task_queue_ =
@@ -1743,10 +1756,6 @@
       pair.first->InsertFence(TaskQueue::InsertFencePosition::kNow);
     }
   }
-  for (auto& observer : main_thread_only().virtual_time_observers) {
-    observer.OnVirtualTimePaused(virtual_time_domain_->Now() -
-                                 main_thread_only().initial_virtual_time_ticks);
-  }
 }
 
 void MainThreadSchedulerImpl::VirtualTimeResumed() {
@@ -1798,24 +1807,6 @@
   main_thread_only().initial_virtual_time_offset = offset;
 }
 
-void MainThreadSchedulerImpl::AddVirtualTimeObserver(
-    VirtualTimeObserver* observer) {
-  main_thread_only().virtual_time_observers.AddObserver(observer);
-}
-
-void MainThreadSchedulerImpl::RemoveVirtualTimeObserver(
-    VirtualTimeObserver* observer) {
-  main_thread_only().virtual_time_observers.RemoveObserver(observer);
-}
-
-void MainThreadSchedulerImpl::OnVirtualTimeAdvanced() {
-  for (auto& observer : main_thread_only().virtual_time_observers) {
-    observer.OnVirtualTimeAdvanced(
-        virtual_time_domain_->Now() -
-        main_thread_only().initial_virtual_time_ticks);
-  }
-}
-
 void MainThreadSchedulerImpl::ApplyVirtualTimePolicy() {
   switch (main_thread_only().virtual_time_policy) {
     case VirtualTimePolicy::kAdvance:
@@ -2197,6 +2188,11 @@
                                     WebString(WTF::String(name)));
 }
 
+PendingUserInputInfo MainThreadSchedulerImpl::GetPendingUserInputInfo() const {
+  base::AutoLock lock(any_thread_lock_);
+  return any_thread().pending_input_monitor.Info();
+}
+
 void MainThreadSchedulerImpl::RunIdleTask(Thread::IdleTask task,
                                           base::TimeTicks deadline) {
   std::move(task).Run(deadline);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
index ee86d05..7540bb6 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h
@@ -38,6 +38,7 @@
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_helper.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_task_queue.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h"
+#include "third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/prioritize_compositing_after_input_experiment.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/queueing_time_estimator.h"
 #include "third_party/blink/renderer/platform/scheduler/main_thread/render_widget_signals.h"
@@ -69,8 +70,7 @@
       public MainThreadSchedulerHelper::Observer,
       public RenderWidgetSignals::Observer,
       public QueueingTimeEstimator::Client,
-      public base::trace_event::TraceLog::AsyncEnabledStateObserver,
-      public AutoAdvancingVirtualTimeDomain::Observer {
+      public base::trace_event::TraceLog::AsyncEnabledStateObserver {
  public:
   // Don't use except for tracing.
   struct TaskDescriptionForTracing {
@@ -168,6 +168,10 @@
   void DidHandleInputEventOnCompositorThread(
       const WebInputEvent& web_input_event,
       InputEventState event_state) override;
+  void WillPostInputEventToMainThread(
+      WebInputEvent::Type web_input_event_type) override;
+  void WillHandleInputEventOnMainThread(
+      WebInputEvent::Type web_input_event_type) override;
   void DidHandleInputEventOnMainThread(const WebInputEvent& web_input_event,
                                        WebInputEventResult result) override;
   void DidAnimateForInputOnCompositorThread() override;
@@ -196,6 +200,7 @@
   WebScopedVirtualTimePauser CreateWebScopedVirtualTimePauser(
       const char* name,
       WebScopedVirtualTimePauser::VirtualTaskDuration duration) override;
+  PendingUserInputInfo GetPendingUserInputInfo() const override;
 
   // ThreadScheduler implementation:
   void PostIdleTask(const base::Location&, Thread::IdleTask) override;
@@ -226,9 +231,6 @@
   //
   // TODO(yutak): Reduce the overlaps and simplify.
 
-  // AutoAdvancingVirtualTimeDomain::Observer implementation:
-  void OnVirtualTimeAdvanced() override;
-
   // RenderWidgetSignals::Observer implementation:
   void SetAllRenderWidgetsHidden(bool hidden) override;
   void SetHasVisibleRenderWidgetWithTouchHandler(
@@ -274,7 +276,6 @@
       FrameSchedulerImpl* frame_scheduler);
 
   using VirtualTimePolicy = PageScheduler::VirtualTimePolicy;
-  using VirtualTimeObserver = PageScheduler::VirtualTimeObserver;
 
   using BaseTimeOverridePolicy =
       AutoAdvancingVirtualTimeDomain::BaseTimeOverridePolicy;
@@ -297,8 +298,6 @@
   void SetInitialVirtualTime(base::Time time);
   void SetInitialVirtualTimeOffset(base::TimeDelta offset);
   void SetMaxVirtualTimeTaskStarvationCount(int max_task_starvation_count);
-  void AddVirtualTimeObserver(VirtualTimeObserver*);
-  void RemoveVirtualTimeObserver(VirtualTimeObserver*);
   base::TimeTicks IncrementVirtualTimePauseCount();
   void DecrementVirtualTimePauseCount();
   void MaybeAdvanceVirtualTime(base::TimeTicks new_virtual_time);
@@ -850,7 +849,6 @@
         base::Optional<base::sequence_manager::TaskQueue::QueuePriority>,
         TracingCategoryName::kInfo>
         task_priority_for_tracing;  // Only used for tracing.
-    base::ObserverList<VirtualTimeObserver>::Unchecked virtual_time_observers;
     base::Time initial_virtual_time;
     base::TimeTicks initial_virtual_time_ticks;
 
@@ -889,6 +887,7 @@
     explicit AnyThread(MainThreadSchedulerImpl* main_thread_scheduler_impl);
     ~AnyThread();
 
+    PendingUserInput::Monitor pending_input_monitor;
     base::TimeTicks last_idle_period_end_time;
     base::TimeTicks fling_compositor_escalation_deadline;
     UserModel user_model;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
index 0d34d93..e044c7d 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.cc
@@ -345,15 +345,6 @@
       main_thread_scheduler_->GetVirtualTimeDomain()->Now() + budget);
 }
 
-void PageSchedulerImpl::AddVirtualTimeObserver(VirtualTimeObserver* observer) {
-  main_thread_scheduler_->AddVirtualTimeObserver(observer);
-}
-
-void PageSchedulerImpl::RemoveVirtualTimeObserver(
-    VirtualTimeObserver* observer) {
-  main_thread_scheduler_->RemoveVirtualTimeObserver(observer);
-}
-
 void PageSchedulerImpl::AudioStateChanged(bool is_audio_playing) {
   if (is_audio_playing) {
     audio_state_ = AudioState::kAudible;
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
index 7b277b0..630cd7b 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl.h
@@ -76,8 +76,6 @@
   bool IsExemptFromBudgetBasedThrottling() const override;
   bool HasActiveConnectionForTest() const override;
   bool RequestBeginMainFrameNotExpected(bool new_state) override;
-  void AddVirtualTimeObserver(VirtualTimeObserver*) override;
-  void RemoveVirtualTimeObserver(VirtualTimeObserver*) override;
 
   // Virtual for testing.
   virtual void ReportIntervention(const std::string& message);
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
index db2c12fc..ea609fb 100644
--- a/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/page_scheduler_impl_unittest.cc
@@ -916,68 +916,6 @@
 }
 
 namespace {
-class MockObserver : public PageScheduler::VirtualTimeObserver {
- public:
-  ~MockObserver() override = default;
-
-  void OnVirtualTimeAdvanced(base::TimeDelta virtual_time_offset) override {
-    virtual_time_log_.push_back(base::StringPrintf(
-        "Advanced to %dms",
-        static_cast<int>(virtual_time_offset.InMilliseconds())));
-  }
-
-  void OnVirtualTimePaused(base::TimeDelta virtual_time_offset) override {
-    virtual_time_log_.push_back(base::StringPrintf(
-        "Paused at %dms",
-        static_cast<int>(virtual_time_offset.InMilliseconds())));
-  }
-
-  const std::vector<std::string>& virtual_time_log() const {
-    return virtual_time_log_;
-  }
-
- private:
-  std::vector<std::string> virtual_time_log_;
-};
-
-void NopTask() {}
-}  // namespace
-
-TEST_F(PageSchedulerImplTest, VirtualTimeObserver) {
-  MockObserver mock_observer;
-  page_scheduler_->AddVirtualTimeObserver(&mock_observer);
-  page_scheduler_->EnableVirtualTime();
-
-  ThrottleableTaskQueue()->task_runner()->PostDelayedTask(
-      FROM_HERE, base::BindOnce(&NopTask),
-      base::TimeDelta::FromMilliseconds(200));
-
-  ThrottleableTaskQueue()->task_runner()->PostDelayedTask(
-      FROM_HERE, base::BindOnce(&NopTask),
-      base::TimeDelta::FromMilliseconds(20));
-
-  ThrottleableTaskQueue()->task_runner()->PostDelayedTask(
-      FROM_HERE, base::BindOnce(&NopTask),
-      base::TimeDelta::FromMilliseconds(2));
-
-  page_scheduler_->GrantVirtualTimeBudget(
-      base::TimeDelta::FromMilliseconds(1000),
-      base::BindOnce(
-          [](PageScheduler* scheduler) {
-            scheduler->SetVirtualTimePolicy(VirtualTimePolicy::kPause);
-          },
-          base::Unretained(page_scheduler_.get())));
-
-  test_task_runner_->FastForwardUntilNoTasksRemain();
-
-  EXPECT_THAT(
-      mock_observer.virtual_time_log(),
-      ElementsAre("Advanced to 2ms", "Advanced to 20ms", "Advanced to 200ms",
-                  "Advanced to 1000ms", "Paused at 1000ms"));
-  page_scheduler_->RemoveVirtualTimeObserver(&mock_observer);
-}
-
-namespace {
 void RepostingTask(scoped_refptr<TaskQueue> task_queue,
                    int max_count,
                    int* count) {
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.cc b/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.cc
new file mode 100644
index 0000000..b25f49e1
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.cc
@@ -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.
+
+#include "third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h"
+#include "third_party/blink/renderer/platform/wtf/hash_map.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string_hash.h"
+#include "third_party/blink/renderer/platform/wtf/text/string_hash.h"
+
+namespace blink {
+namespace scheduler {
+
+typedef HashMap<AtomicString, PendingUserInputType> PendingInputTypeMap;
+
+namespace {
+
+void PopulatePendingInputTypeMap(PendingInputTypeMap& map) {
+  map = {
+      {"click", PendingUserInputType::kClick},
+      {"dblclick", PendingUserInputType::kDblClick},
+      {"mousedown", PendingUserInputType::kMouseDown},
+      {"mouseenter", PendingUserInputType::kMouseEnter},
+      {"mouseleave", PendingUserInputType::kMouseLeave},
+      {"mousemove", PendingUserInputType::kMouseMove},
+      {"mouseout", PendingUserInputType::kMouseOut},
+      {"mouseover", PendingUserInputType::kMouseOver},
+      {"mouseup", PendingUserInputType::kMouseUp},
+      {"wheel", PendingUserInputType::kWheel},
+      {"keydown", PendingUserInputType::kKeyDown},
+      {"keyup", PendingUserInputType::kKeyUp},
+      {"touchstart", PendingUserInputType::kTouchStart},
+      {"touchend", PendingUserInputType::kTouchEnd},
+      {"touchmove", PendingUserInputType::kTouchMove},
+      {"touchcancel", PendingUserInputType::kTouchCancel},
+  };
+}
+
+}  // namespace
+
+void PendingUserInput::Monitor::OnEnqueue(WebInputEvent::Type type) {
+  DCHECK_NE(type, WebInputEvent::kUndefined);
+  DCHECK_LE(type, WebInputEvent::kTypeLast);
+
+  size_t& counter = counters_[type];
+  counter++;
+}
+
+void PendingUserInput::Monitor::OnDequeue(WebInputEvent::Type type) {
+  DCHECK_NE(type, WebInputEvent::kUndefined);
+  DCHECK_LE(type, WebInputEvent::kTypeLast);
+
+  size_t& counter = counters_[type];
+  DCHECK_GT(counter, size_t{0});
+  counter--;
+}
+
+PendingUserInputInfo PendingUserInput::Monitor::Info() const {
+  PendingUserInputType mask = PendingUserInputType::kNone;
+  for (int type = WebInputEvent::kTypeFirst + 1;
+       type <= WebInputEvent::kTypeLast; type++) {
+    const size_t& counter = counters_[type];
+    DCHECK_GE(counter, size_t{0});
+    if (counter == 0)
+      continue;
+
+    mask = mask |
+           TypeFromWebInputEventType(static_cast<WebInputEvent::Type>(type));
+  }
+  return PendingUserInputInfo(mask);
+}
+
+// static
+PendingUserInputType PendingUserInput::TypeFromString(
+    const AtomicString& pending_user_input_type) {
+  DEFINE_STATIC_LOCAL(PendingInputTypeMap, kPendingInputTypeMap, ());
+  if (kPendingInputTypeMap.IsEmpty()) {
+    PopulatePendingInputTypeMap(kPendingInputTypeMap);
+    DCHECK(!kPendingInputTypeMap.IsEmpty());
+  }
+
+  const auto type_iter = kPendingInputTypeMap.find(pending_user_input_type);
+  if (type_iter != kPendingInputTypeMap.end())
+    return type_iter->value;
+  return PendingUserInputType::kNone;
+}
+
+// static
+PendingUserInputType PendingUserInput::TypeFromWebInputEventType(
+    WebInputEvent::Type type) {
+  using Type = PendingUserInputType;
+  switch (type) {
+    case WebInputEvent::Type::kMouseDown:
+      return Type::kMouseDown;
+    case WebInputEvent::Type::kMouseUp:
+      return Type::kClick | Type::kDblClick | Type::kMouseUp;
+    case WebInputEvent::Type::kMouseMove:
+      return Type::kMouseMove;
+    case WebInputEvent::Type::kMouseEnter:
+      return Type::kMouseEnter;
+    case WebInputEvent::Type::kMouseLeave:
+      return Type::kMouseLeave;
+    case WebInputEvent::Type::kMouseWheel:
+      return Type::kWheel;
+
+    case WebInputEvent::Type::kKeyDown:
+    case WebInputEvent::Type::kRawKeyDown:
+    case WebInputEvent::Type::kChar:
+      return Type::kKeyDown;
+    case WebInputEvent::Type::kKeyUp:
+      return Type::kKeyUp;
+
+    case WebInputEvent::Type::kTouchStart:
+    case WebInputEvent::Type::kGestureTapDown:
+      return Type::kTouchStart | Type::kMouseDown;
+    case WebInputEvent::Type::kTouchMove:
+      return Type::kTouchMove;
+    case WebInputEvent::Type::kTouchEnd:
+    case WebInputEvent::Type::kGestureTap:
+      return Type::kTouchEnd | Type::kClick | Type::kDblClick | Type::kMouseUp;
+    case WebInputEvent::Type::kTouchCancel:
+    case WebInputEvent::Type::kGestureTapCancel:
+      return Type::kTouchCancel;
+
+    default:
+      return Type::kNone;
+  }
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h b/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h
new file mode 100644
index 0000000..46bf3336
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input.h
@@ -0,0 +1,46 @@
+// 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_PLATFORM_SCHEDULER_MAIN_THREAD_PENDING_USER_INPUT_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PENDING_USER_INPUT_H_
+
+#include <array>
+
+#include "third_party/blink/public/platform/web_input_event.h"
+#include "third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h"
+#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
+
+namespace blink {
+namespace scheduler {
+
+class PLATFORM_EXPORT PendingUserInput {
+ public:
+  // Handles the dispatch of WebInputEvents from the main thread scheduler,
+  // keeping track of the set of in-flight input events.
+  class PLATFORM_EXPORT Monitor {
+   public:
+    Monitor() { this->counters_.fill(0); }
+    void OnEnqueue(WebInputEvent::Type);
+    void OnDequeue(WebInputEvent::Type);
+
+    PendingUserInputInfo Info() const;
+
+   private:
+    std::array<size_t, WebInputEvent::kTypeLast + 1> counters_;
+
+    DISALLOW_COPY_AND_ASSIGN(Monitor);
+  };
+
+  PendingUserInput() = delete;
+
+  static PendingUserInputType TypeFromString(const AtomicString&);
+  static PendingUserInputType TypeFromWebInputEventType(WebInputEvent::Type);
+
+  DISALLOW_COPY_AND_ASSIGN(PendingUserInput);
+};
+
+}  // namespace scheduler
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_MAIN_THREAD_PENDING_USER_INPUT_H_
diff --git a/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input_unittest.cc b/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input_unittest.cc
new file mode 100644
index 0000000..8d07214
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/main_thread/pending_user_input_unittest.cc
@@ -0,0 +1,100 @@
+// 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/platform/scheduler/main_thread/pending_user_input.h"
+#include "third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace blink {
+namespace scheduler {
+
+class PendingUserInputMonitorTest : public testing::Test {
+ public:
+  PendingUserInput::Monitor monitor_;
+};
+
+// Tests that a single event type is tracked.
+TEST_F(PendingUserInputMonitorTest, TestCounterBasic) {
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_FALSE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnEnqueue(WebInputEvent::kMouseDown);
+  EXPECT_TRUE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_TRUE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnDequeue(WebInputEvent::kMouseDown);
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_FALSE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+}
+
+// Tests that enqueuing multiple identical event types is tracked correctly.
+TEST_F(PendingUserInputMonitorTest, TestCounterNested) {
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_FALSE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnEnqueue(WebInputEvent::kMouseDown);
+  EXPECT_TRUE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_TRUE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnEnqueue(WebInputEvent::kMouseDown);
+  EXPECT_TRUE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_TRUE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnDequeue(WebInputEvent::kMouseDown);
+  EXPECT_TRUE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_TRUE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnDequeue(WebInputEvent::kMouseDown);
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_FALSE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+}
+
+// Tests that non-overlapping input types are tracked independently.
+TEST_F(PendingUserInputMonitorTest, TestCounterDisjoint) {
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseUp));
+  EXPECT_FALSE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnEnqueue(WebInputEvent::kMouseDown);
+  EXPECT_TRUE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseUp));
+  EXPECT_TRUE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnEnqueue(WebInputEvent::kMouseUp);
+  EXPECT_TRUE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_TRUE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseUp));
+  EXPECT_TRUE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnDequeue(WebInputEvent::kMouseDown);
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_TRUE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseUp));
+  EXPECT_TRUE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+
+  monitor_.OnDequeue(WebInputEvent::kMouseUp);
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseDown));
+  EXPECT_FALSE(
+      monitor_.Info().HasPendingInputType(PendingUserInputType::kMouseUp));
+  EXPECT_FALSE(monitor_.Info().HasPendingInputType(PendingUserInputType::kAny));
+}
+
+}  // namespace scheduler
+}  // namespace blink
diff --git a/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
index e32f97c..3c6a263 100644
--- a/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/page_scheduler.h
@@ -103,26 +103,6 @@
   // FrameSchedulers.
   virtual void SetVirtualTimePolicy(VirtualTimePolicy) = 0;
 
-  class PLATFORM_EXPORT VirtualTimeObserver {
-   public:
-    virtual ~VirtualTimeObserver() = default;
-
-    // Called when virtual time advances. |virtual_time_offset| is the offset
-    // between the current virtual time and the initial virtual time when
-    // EnableVirtualTime() was called.
-    virtual void OnVirtualTimeAdvanced(base::TimeDelta virtual_time_offset) = 0;
-
-    // Called when virtual time pauses for any reason. |virtual_time_offset| is
-    // the offset between the current virtual time and the initial virtual time
-    // when EnableVirtualTime() was called.
-    virtual void OnVirtualTimePaused(base::TimeDelta virtual_time_offset) = 0;
-  };
-
-  // Adds a VirtualTimeObserver instance to be notified when virtual time has
-  // been paused.
-  virtual void AddVirtualTimeObserver(VirtualTimeObserver*) = 0;
-  virtual void RemoveVirtualTimeObserver(VirtualTimeObserver*) = 0;
-
   // Set the remaining virtual time budget to |budget|. Once the budget runs
   // out, |budget_exhausted_callback| is called. Note that the virtual time
   // policy is not affected when the budget expires.
diff --git a/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h b/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h
new file mode 100644
index 0000000..a2e54de7
--- /dev/null
+++ b/third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h
@@ -0,0 +1,75 @@
+// 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_PLATFORM_SCHEDULER_PUBLIC_PENDING_USER_INPUT_TYPE_H_
+#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_PENDING_USER_INPUT_TYPE_H_
+
+namespace blink {
+namespace scheduler {
+
+// A subset of DOM input events that should be supported by
+// hasPendingUserInput. Each one of these maps to one or more
+// WebInputEvent::Type items.
+enum class PendingUserInputType {
+  kNone = 0,
+
+  kClick = 1 << 1,
+  kDblClick = 1 << 2,
+  kMouseDown = 1 << 3,
+  kMouseEnter = 1 << 4,
+  kMouseLeave = 1 << 5,
+  kMouseMove = 1 << 6,
+  kMouseOut = 1 << 7,
+  kMouseOver = 1 << 8,
+  kMouseUp = 1 << 9,
+
+  kWheel = 1 << 10,
+
+  kKeyDown = 1 << 11,
+  kKeyUp = 1 << 12,
+
+  kTouchStart = 1 << 13,
+  kTouchEnd = 1 << 14,
+  kTouchMove = 1 << 15,
+  kTouchCancel = 1 << 16,
+
+  kAny = ~0  // Wildcard input event type
+};
+
+inline constexpr PendingUserInputType operator&(PendingUserInputType a,
+                                                PendingUserInputType b) {
+  return static_cast<PendingUserInputType>(static_cast<int>(a) &
+                                           static_cast<int>(b));
+}
+
+inline constexpr PendingUserInputType operator|(PendingUserInputType a,
+                                                PendingUserInputType b) {
+  return static_cast<PendingUserInputType>(static_cast<int>(a) |
+                                           static_cast<int>(b));
+}
+
+inline constexpr bool operator==(PendingUserInputType a,
+                                 PendingUserInputType b) {
+  return static_cast<int>(a) == static_cast<int>(b);
+}
+
+// A wrapper around a set of input types that have been flagged as pending.
+class PLATFORM_EXPORT PendingUserInputInfo {
+ public:
+  constexpr explicit PendingUserInputInfo(PendingUserInputType mask)
+      : mask_(mask) {}
+  constexpr PendingUserInputInfo() : mask_(PendingUserInputType::kNone) {}
+
+  constexpr bool HasPendingInputType(PendingUserInputType type) const {
+    return (mask_ & type) != PendingUserInputType::kNone;
+  }
+
+ private:
+  const PendingUserInputType mask_;
+};
+
+}  // namespace scheduler
+}  // namespace blink
+
+#endif  // THIRD_PARTY_BLINK_RENDERER_PLATFORM_SCHEDULER_PUBLIC_PENDING_USER_INPUT_TYPE_H_
diff --git a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
index c0f808b9..075c0647 100644
--- a/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h
@@ -11,7 +11,9 @@
 #include "base/single_thread_task_runner.h"
 #include "base/time/time.h"
 #include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
+#include "third_party/blink/public/platform/web_input_event.h"
 #include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
+#include "third_party/blink/renderer/platform/scheduler/public/pending_user_input_type.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread.h"
 
 namespace blink {
@@ -111,6 +113,10 @@
   virtual void RemoveTaskObserver(
       base::MessageLoop::TaskObserver* task_observer) = 0;
 
+  virtual scheduler::PendingUserInputInfo GetPendingUserInputInfo() const {
+    return scheduler::PendingUserInputInfo();
+  }
+
   // Test helpers.
 
   // Return a reference to an underlying main thread WebThreadScheduler object.
diff --git a/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h b/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h
index 029081f..85368ccb 100644
--- a/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h
+++ b/third_party/blink/renderer/platform/scheduler/test/fake_page_scheduler.h
@@ -65,8 +65,6 @@
   void DisableVirtualTimeForTesting() override {}
   bool VirtualTimeAllowedToAdvance() const override { return false; }
   void SetVirtualTimePolicy(VirtualTimePolicy policy) override {}
-  void AddVirtualTimeObserver(VirtualTimeObserver* observer) override {}
-  void RemoveVirtualTimeObserver(VirtualTimeObserver* observer) override {}
   void SetInitialVirtualTime(base::Time time) override {}
   void SetInitialVirtualTimeOffset(base::TimeDelta offset) override {}
   void GrantVirtualTimeBudget(base::TimeDelta budget,
diff --git a/third_party/blink/renderer/platform/scheduler/test/web_fake_thread_scheduler.cc b/third_party/blink/renderer/platform/scheduler/test/web_fake_thread_scheduler.cc
index d54ca01..59711a5f 100644
--- a/third_party/blink/renderer/platform/scheduler/test/web_fake_thread_scheduler.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/web_fake_thread_scheduler.cc
@@ -59,6 +59,12 @@
     const blink::WebInputEvent& web_input_event,
     InputEventState event_state) {}
 
+void WebFakeThreadScheduler::WillPostInputEventToMainThread(
+    WebInputEvent::Type web_input_event_type) {}
+
+void WebFakeThreadScheduler::WillHandleInputEventOnMainThread(
+    WebInputEvent::Type web_input_event_type) {}
+
 void WebFakeThreadScheduler::DidHandleInputEventOnMainThread(
     const blink::WebInputEvent& web_input_event,
     WebInputEventResult result) {}
diff --git a/third_party/blink/renderer/platform/testing/paint_test_configurations.h b/third_party/blink/renderer/platform/testing/paint_test_configurations.h
index 2d7e53cc..614813a 100644
--- a/third_party/blink/renderer/platform/testing/paint_test_configurations.h
+++ b/third_party/blink/renderer/platform/testing/paint_test_configurations.h
@@ -34,18 +34,18 @@
   }
 };
 
-#define INSTANTIATE_PAINT_TEST_CASE_P(test_class) \
-  INSTANTIATE_TEST_CASE_P(                        \
-      All, test_class,                            \
+#define INSTANTIATE_PAINT_TEST_SUITE_P(test_class) \
+  INSTANTIATE_TEST_SUITE_P(                        \
+      All, test_class,                             \
       ::testing::Values(0, kBlinkGenPropertyTrees, kCompositeAfterPaint))
 
-#define INSTANTIATE_CAP_TEST_CASE_P(test_class) \
-  INSTANTIATE_TEST_CASE_P(All, test_class,      \
-                          ::testing::Values(kCompositeAfterPaint))
+#define INSTANTIATE_CAP_TEST_SUITE_P(test_class) \
+  INSTANTIATE_TEST_SUITE_P(All, test_class,      \
+                           ::testing::Values(kCompositeAfterPaint))
 
-#define INSTANTIATE_LAYER_LIST_TEST_CASE_P(test_class) \
-  INSTANTIATE_TEST_CASE_P(                             \
-      All, test_class,                                 \
+#define INSTANTIATE_LAYER_LIST_TEST_SUITE_P(test_class) \
+  INSTANTIATE_TEST_SUITE_P(                             \
+      All, test_class,                                  \
       ::testing::Values(kBlinkGenPropertyTrees, kCompositeAfterPaint))
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc b/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
index 008e697d..ce5241c 100644
--- a/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
+++ b/third_party/blink/renderer/platform/testing/shape_result_perf_test.cc
@@ -145,9 +145,9 @@
                          timer.LapsPerSecond(), "runs/s", true);
 }
 
-INSTANTIATE_TEST_CASE_P(OffsetForPosition,
-                        OffsetForPositionPerfTest,
-                        testing::Values(0, 10, 60, 100, 200, 350));
+INSTANTIATE_TEST_SUITE_P(OffsetForPosition,
+                         OffsetForPositionPerfTest,
+                         testing::Values(0, 10, 60, 100, 200, 350));
 
 TEST_P(CharacterRangePerfTest, LTRCharacterForPosition) {
   TextRun run = SetupFont(ahem, "FURACOLO", true);
@@ -163,8 +163,8 @@
                          timer.LapsPerSecond(), "runs/s", true);
 }
 
-INSTANTIATE_TEST_CASE_P(CharacterRange,
-                        CharacterRangePerfTest,
-                        testing::Values(0, 1, 2, 4, 8));
+INSTANTIATE_TEST_SUITE_P(CharacterRange,
+                         CharacterRangePerfTest,
+                         testing::Values(0, 1, 2, 4, 8));
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/platform/text/capitalize_test.cc b/third_party/blink/renderer/platform/text/capitalize_test.cc
index ffa15f97..68e4906 100644
--- a/third_party/blink/renderer/platform/text/capitalize_test.cc
+++ b/third_party/blink/renderer/platform/text/capitalize_test.cc
@@ -20,12 +20,12 @@
                        public testing::WithParamInterface<CapitalizeTestData> {
 };
 
-INSTANTIATE_TEST_CASE_P(CapitalizeTest,
-                        CapitalizeTest,
-                        testing::Values(CapitalizeTestData{String(), String()},
-                                        CapitalizeTestData{"", ""},
-                                        CapitalizeTestData{"hello, world",
-                                                           "Hello, World"}));
+INSTANTIATE_TEST_SUITE_P(CapitalizeTest,
+                         CapitalizeTest,
+                         testing::Values(CapitalizeTestData{String(), String()},
+                                         CapitalizeTestData{"", ""},
+                                         CapitalizeTestData{"hello, world",
+                                                            "Hello, World"}));
 
 TEST_P(CapitalizeTest, Data) {
   const auto& data = GetParam();
diff --git a/third_party/blink/renderer/platform/text/text_break_iterator_test.cc b/third_party/blink/renderer/platform/text/text_break_iterator_test.cc
index cd52e2e..c5f2ee5 100644
--- a/third_party/blink/renderer/platform/text/text_break_iterator_test.cc
+++ b/third_party/blink/renderer/platform/text/text_break_iterator_test.cc
@@ -90,9 +90,9 @@
 class BreakTypeTest : public TextBreakIteratorTest,
                       public testing::WithParamInterface<LineBreakType> {};
 
-INSTANTIATE_TEST_CASE_P(TextBreakIteratorTest,
-                        BreakTypeTest,
-                        testing::ValuesIn(all_break_types));
+INSTANTIATE_TEST_SUITE_P(TextBreakIteratorTest,
+                         BreakTypeTest,
+                         testing::ValuesIn(all_break_types));
 
 TEST_P(BreakTypeTest, EmptyString) {
   LazyLineBreakIterator iterator(g_empty_string);
diff --git a/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc b/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc
index f32cb5a..f02ea5f9 100644
--- a/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc
+++ b/third_party/blink/renderer/platform/transforms/rotate_transform_operation.cc
@@ -48,6 +48,9 @@
     const TransformOperation* from,
     double progress,
     bool blend_to_identity) {
+  if (from && !IsMatchingOperationType(from->GetType()))
+    return this;
+
   if (blend_to_identity)
     return RotateTransformOperation::Create(
         Rotation(Axis(), Angle() * (1 - progress)), type_);
diff --git a/third_party/blink/renderer/platform/web_thread_supporting_gc.cc b/third_party/blink/renderer/platform/web_thread_supporting_gc.cc
index e3e214f..5ad0322 100644
--- a/third_party/blink/renderer/platform/web_thread_supporting_gc.cc
+++ b/third_party/blink/renderer/platform/web_thread_supporting_gc.cc
@@ -7,7 +7,7 @@
 #include <memory>
 
 #include "base/memory/ptr_util.h"
-#include "third_party/blink/renderer/platform/memory_coordinator.h"
+#include "third_party/blink/renderer/platform/memory_pressure_listener.h"
 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
 #include "third_party/blink/renderer/platform/wtf/threading.h"
 
@@ -31,7 +31,7 @@
       thread_ = Thread::CreateThread(params);
     }
   }
-  MemoryCoordinator::Instance().RegisterThread(thread_.get());
+  MemoryPressureListenerRegistry::Instance().RegisterThread(thread_.get());
 }
 
 WebThreadSupportingGC::~WebThreadSupportingGC() {
@@ -39,7 +39,7 @@
   Thread* thread_pointer = thread_.get();
   // blink::Thread's destructor blocks until all the tasks are processed.
   thread_.reset();
-  MemoryCoordinator::Instance().UnregisterThread(thread_pointer);
+  MemoryPressureListenerRegistry::Instance().UnregisterThread(thread_pointer);
 }
 
 void WebThreadSupportingGC::InitializeOnThread() {
diff --git a/third_party/blink/renderer/platform/weborigin/kurl_test.cc b/third_party/blink/renderer/platform/weborigin/kurl_test.cc
index 2a047b0..b92f67a 100644
--- a/third_party/blink/renderer/platform/weborigin/kurl_test.cc
+++ b/third_party/blink/renderer/platform/weborigin/kurl_test.cc
@@ -992,6 +992,6 @@
   }
 }
 
-INSTANTIATE_TEST_CASE_P(, KURLPortTest, ::testing::ValuesIn(port_test_cases));
+INSTANTIATE_TEST_SUITE_P(, KURLPortTest, ::testing::ValuesIn(port_test_cases));
 
 }  // namespace blink
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer.py b/third_party/blink/tools/blinkpy/w3c/test_importer.py
index 74c094ff..e4676e4 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer.py
@@ -385,11 +385,19 @@
         first ensures if upstream deletes some files, we also delete them.
         """
         _log.info('Cleaning out tests from %s.', self.dest_path)
+
+        # TODO(crbug.com/927187): Temporarily prevent the external/wpt/webdriver folder from deletion.
+        # Will delete once starting the two-way sync phase on webdriver/tests.
+        webdriver_dir_path = self.fs.join(self.dest_path, 'webdriver')
+
         should_remove = lambda fs, dirname, basename: (
             is_file_exportable(fs.relpath(fs.join(dirname, basename), self.finder.chromium_base())))
         files_to_delete = self.fs.files_under(self.dest_path, file_filter=should_remove)
         for subpath in files_to_delete:
-            self.remove(self.finder.path_from_web_tests('external', subpath))
+            remove_path = self.finder.path_from_web_tests('external', subpath)
+            if remove_path.startswith(webdriver_dir_path):
+                continue
+            self.remove(remove_path)
 
     def _commit_changes(self, commit_message):
         _log.info('Committing changes.')
diff --git a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
index 0347a88..31647c5 100644
--- a/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
+++ b/third_party/blink/tools/blinkpy/w3c/test_importer_unittest.py
@@ -547,6 +547,10 @@
         host.filesystem.write_text_file(dest_path + '/foo-test-expected.txt', '')
         host.filesystem.write_text_file(dest_path + '/OWNERS', '')
         host.filesystem.write_text_file(dest_path + '/bar/baz/OWNERS', '')
+        # TODO(crbug.com/927187): Delete it once webdrive/tests manual import is done.
+        host.filesystem.write_text_file(dest_path + '/webdriver/tests/A.txt', '')
+        host.filesystem.write_text_file(dest_path + '/webdriver/tests/test/assert.py', '')
+        host.filesystem.write_text_file(dest_path + '/webdriver/META.yml', '')
         # When the destination path is cleared, OWNERS files and baselines
         # are kept.
         importer._clear_out_dest_path()
@@ -554,3 +558,7 @@
         self.assertTrue(host.filesystem.exists(dest_path + '/foo-test-expected.txt'))
         self.assertTrue(host.filesystem.exists(dest_path + '/OWNERS'))
         self.assertTrue(host.filesystem.exists(dest_path + '/bar/baz/OWNERS'))
+
+        self.assertTrue(host.filesystem.exists(dest_path + '/webdriver/tests/A.txt'))
+        self.assertTrue(host.filesystem.exists(dest_path + '/webdriver/tests/test/assert.py'))
+        self.assertTrue(host.filesystem.exists(dest_path + '/webdriver/META.yml'))
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 47dc540..ce8dd06 100644
--- a/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
+++ b/third_party/blink/web_tests/FlagExpectations/enable-blink-features=LayoutNG
@@ -261,11 +261,12 @@
 crbug.com/591099 external/wpt/fetch/api/redirect/redirect-count.any.worker.html [ Pass ]
 crbug.com/591099 external/wpt/fetch/api/request/request-keepalive-quota.html?include=slow-2 [ Pass ]
 crbug.com/591099 external/wpt/fullscreen/api/element-ready-check-containing-iframe-manual.html [ Pass ]
-crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure ]
+crbug.com/591099 external/wpt/geolocation-API/PositionOptions.https.html [ Failure Pass ]
 crbug.com/591099 external/wpt/html/browsers/origin/cross-origin-objects/cross-origin-objects.html [ Pass ]
 crbug.com/591099 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_parent [ Pass ]
 crbug.com/591099 external/wpt/html/browsers/the-window-object/window-open-noopener.html?_top [ Pass ]
 crbug.com/591099 external/wpt/html/semantics/embedded-content/the-embed-element/embed-represent-nothing-02.html [ Pass ]
+crbug.com/591099 external/wpt/html/semantics/links/links-created-by-a-and-area-elements/target_blank_implicit_noopener.tentative.html [ Pass Timeout ]
 crbug.com/845902 external/wpt/quirks/line-height-trailing-collapsable-whitespace.html [ Pass ]
 crbug.com/591099 external/wpt/wasm/jsapi/constructor/instantiate.any.html [ Failure ]
 crbug.com/591099 external/wpt/wasm/jsapi/constructor/instantiate.any.worker.html [ Failure ]
@@ -277,7 +278,7 @@
 crbug.com/591099 external/wpt/webmessaging/without-ports/018.html [ Pass ]
 crbug.com/591099 fast/backgrounds/quirks-mode-line-box-backgrounds.html [ Failure ]
 crbug.com/591099 fast/borders/inline-mask-overlay-image-outset-vertical-rl.html [ Failure ]
-crbug.com/591099 fast/canvas/OffscreenCanvas-copyImage.html [ Failure Pass ]
+crbug.com/591099 fast/canvas/OffscreenCanvas-copyImage.html [ Pass ]
 crbug.com/591099 fast/css-intrinsic-dimensions/height-css-tables-collapsed.html [ Failure Pass ]
 crbug.com/591099 fast/css-intrinsic-dimensions/height-positioned.html [ Pass ]
 crbug.com/591099 fast/css/absolute-inline-alignment-2.html [ Pass ]
@@ -316,7 +317,6 @@
 crbug.com/591099 paint/invalidation/flexbox/scrollbars-changed.html [ Failure ]
 crbug.com/835484 paint/invalidation/outline/inline-focus.html [ Failure ]
 crbug.com/591099 paint/invalidation/overflow/opacity-change-on-overflow-float.html [ Failure ]
-crbug.com/591099 paint/invalidation/scroll/repaint-composited-child-in-scrolled-container.html [ Failure ]
 crbug.com/591099 paint/invalidation/svg/svg-background-partial-redraw.html [ Failure ]
 crbug.com/591099 paint/invalidation/svg/text-selection-update.svg [ Failure ]
 crbug.com/591099 paint/invalidation/svg/transform-focus-ring-repaint.html [ Failure ]
@@ -324,12 +324,13 @@
 crbug.com/591099 scrollbars/auto-scrollbar-fit-content.html [ Failure ]
 crbug.com/927467 svg/dom/parent-view-layout-crash.html [ Crash Pass ]
 crbug.com/591099 svg/zoom/page/zoom-svg-float-border-padding.xml [ Pass ]
-crbug.com/927467 tables/mozilla/bugs/bug113235-3.html [ Crash Pass ]
+crbug.com/927467 tables/mozilla/bugs/bug113235-3.html [ Crash ]
 crbug.com/591099 tables/mozilla/bugs/bug14159-1.html [ Pass ]
 crbug.com/591099 virtual/android/rootscroller/set-root-scroller.html [ Pass ]
 crbug.com/591099 virtual/android/rootscroller/set-rootscroller-before-load.html [ Pass ]
 crbug.com/916511 virtual/composite-after-paint/paint/background/scrolling-background-with-negative-z-child.html [ Crash ]
 crbug.com/591099 virtual/composite-after-paint/paint/invalidation/box/margin.html [ Failure Pass ]
+crbug.com/591099 virtual/display-lock/display-lock/lock-after-append/measure-forced-layout-after-commit.html [ Failure ]
 crbug.com/591099 virtual/display-lock/display-lock/lock-after-append/measure-forced-layout.html [ Failure ]
 crbug.com/591099 virtual/display-lock/display-lock/lock-after-append/measure-updated-layout.html [ Failure ]
 crbug.com/926276 virtual/display-lock/display-lock/lock-after-append/nested-update-and-commit.html [ Timeout ]
diff --git a/third_party/blink/web_tests/NeverFixTests b/third_party/blink/web_tests/NeverFixTests
index c9303fd8..a155b6f0 100644
--- a/third_party/blink/web_tests/NeverFixTests
+++ b/third_party/blink/web_tests/NeverFixTests
@@ -2146,6 +2146,8 @@
 # See crbug.com/865839.
 virtual/disabled-service-worker-servicification/external/wpt/service-workers/service-worker/update-after-navigation-redirect.https.html [ WontFix ]
 
+# MathML is not implemented.
+crbug.com/6606 external/wpt/mathml [ WontFix ]
 
 # ====== Tests incompatible with the default Site Isolation from here ======
 # See also third_party/blink/web_tests/virtual/not-site-per-process/README.md
diff --git a/third_party/blink/web_tests/SlowTests b/third_party/blink/web_tests/SlowTests
index ca85451..bb2fca52 100644
--- a/third_party/blink/web_tests/SlowTests
+++ b/third_party/blink/web_tests/SlowTests
@@ -876,7 +876,6 @@
 crbug.com/874695 fast/table/multiple-captions-crash3.html [ Slow ]
 crbug.com/874695 fast/table/multiple-captions-crash4.html [ Slow ]
 crbug.com/874695 fast/webgl/canvas-toDataURL-crash.html [ Slow ]
-crbug.com/874695 fast/webgl/OffscreenCanvas-webgl-preserveDrawingBuffer.html [ Slow ]
 crbug.com/874695 fast/webgl/texImage-imageBitmap-from-blob-resize.html [ Slow ]
 crbug.com/874695 fast/webgl/texImage-imageBitmap-from-canvas-resize.html [ Slow ]
 crbug.com/874695 fast/webgl/texImage-imageBitmap-from-imageData-resize.html [ Slow ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 3913580..29c95536 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -252,9 +252,6 @@
 crbug.com/862483 virtual/prefer_compositing_to_lcd_text/compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html [ Failure ]
 crbug.com/862483 virtual/stable/compositing/overflow/scrollbar-layer-placement-negative-z-index-child.html [ Failure ]
 
-crbug.com/805134 http/tests/devtools/tracing/scroll-invalidations.js [ Failure ]
-crbug.com/805134 virtual/threaded/http/tests/devtools/tracing/scroll-invalidations.js [ Failure ]
-
 crbug.com/807395 fast/multicol/mixed-opacity-test.html [ Failure ]
 
 ########## Ref tests can't be rebaselined ##########
@@ -529,6 +526,7 @@
 crbug.com/174167 external/wpt/css/css-tables/visibility-collapse-colspan-003.html [ Failure ]
 crbug.com/174167 external/wpt/css/css-tables/visibility-collapse-rowcol-001.html [ Failure ]
 crbug.com/174167 external/wpt/css/css-tables/visibility-collapse-rowcol-002.html [ Failure ]
+crbug.com/377847 external/wpt/css/css-tables/subpixel-table-cell-height-001.html [ Failure ]
 crbug.com/377847 external/wpt/css/css-tables/subpixel-table-cell-width-001.html [ Failure ]
 crbug.com/377847 external/wpt/css/css-tables/subpixel-table-cell-width-002.html [ Failure ]
 crbug.com/377847 external/wpt/css/css-tables/subpixel-table-width-001.html [ Failure ]
@@ -994,9 +992,11 @@
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-006.html [ Failure Crash ]
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-007.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-009.html [ Failure ]
+crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-008.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-010.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-block-sibling-003.xht [ Failure ]
 crbug.com/924142 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-010.html [ Crash Pass ]
+crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-012.html [ Failure Crash ]
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-list-item-002.html [ Failure ]
 crbug.com/829028 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-list-item-001.html [ Failure ]
 crbug.com/591099 virtual/layout_ng_experimental/external/wpt/css/css-multicol/multicol-span-all-margin-001.xht [ Failure ]
@@ -1778,6 +1778,7 @@
 
 ### virtual/layout_ng_experimental/fast/forms/fieldset/
 crbug.com/875235 virtual/layout_ng_experimental/fast/forms/fieldset/fieldset-align.html [ Failure ]
+crbug.com/875235 virtual/layout_ng_experimental/fast/forms/fieldset/fieldset-legend-change.html [ Crash ]
 crbug.com/875235 virtual/layout_ng_experimental/fast/forms/fieldset/legend-after-margin-with-before-border-horizontal-mode.html [ Failure ]
 crbug.com/875235 virtual/layout_ng_experimental/fast/forms/fieldset/legend-small-after-margin-before-border-horizontal-mode.html [ Failure ]
 
@@ -1964,13 +1965,16 @@
 crbug.com/787615 [ Win ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow.html [ Failure Pass ]
 crbug.com/787615 [ Linux ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow.html [ Failure Pass ]
 crbug.com/787615 [ Fuchsia ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow.html [ Failure Pass ]
-crbug.com/787615 [ Win ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-out-slow.html [ Failure Pass ]
-crbug.com/787615 [ Linux ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-out-slow.html [ Failure Pass ]
-crbug.com/787615 [ Fuchsia ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-out-slow.html [ Failure Pass ]
 crbug.com/787615 [ Win ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen.html [ Failure Pass ]
 crbug.com/787615 [ Linux ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen.html [ Failure Pass ]
 crbug.com/787615 [ Fuchsia ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen.html [ Failure Pass ]
 
+# Flakily fail on Linux Tests (dbg) and Win7.
+crbug.com/922508 [ Linux Debug ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Failure Pass ]
+crbug.com/922508 [ Win7 ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Failure Pass ]
+crbug.com/922508 [ Linux Debug ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad.html [ Failure Pass ]
+crbug.com/922508 [ Win7 ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad.html [ Failure Pass ]
+
 crbug.com/871139 [ Android ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow.html [ Failure ]
 crbug.com/871139 [ Android ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-out-slow.html [ Failure ]
 crbug.com/871139 [ Android ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen.html [ Failure ]
@@ -1980,12 +1984,8 @@
 crbug.com/613672 [ Mac ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-out-slow.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow-desktop.html [ Skip ]
-crbug.com/613672 [ Mac ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-out-slow-desktop.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-desktop.html [ Skip ]
 
-# Fails on Linux Tests (dbg) bot.
-crbug.com/922508 [ Linux Debug ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html [ Failure Pass ]
-
 crbug.com/522648 fast/events/touch/compositor-touch-hit-rects-iframes.html [ Crash Failure Pass ]
 crbug.com/522648 virtual/mouseevent_fractional/fast/events/touch/compositor-touch-hit-rects-iframes.html [ Crash Failure Pass ]
 crbug.com/522648 virtual/scroll_customization/fast/events/touch/compositor-touch-hit-rects-iframes.html [ Skip ]
@@ -2002,8 +2002,8 @@
 
 crbug.com/771233 [ Win10 ] http/tests/devtools/audits2/ [ Skip ]
 
-crbug.com/865477 [ Linux Mac ] virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Failure ]
-crbug.com/865477 [ Linux Mac ] fast/canvas/OffscreenCanvas-copyImage.html [ Failure ]
+crbug.com/923269 virtual/gpu/fast/canvas/OffscreenCanvas-copyImage.html [ Failure ]
+crbug.com/923269 fast/canvas/OffscreenCanvas-copyImage.html [ Failure ]
 
 crbug.com/410974 fast/scroll-behavior/scroll-customization/scrollstate-basic.html [ Pass Failure ]
 crbug.com/410974 fast/scroll-behavior/scroll-customization/scrollstate-consume-deltas.html [ Pass Failure ]
@@ -3075,6 +3075,9 @@
 crbug.com/918664 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/sizing/block-size-with-min-or-max-content-table-1a.html [ Failure Pass ]
 
 # ====== New tests from wpt-importer added here ======
+crbug.com/626703 external/wpt/css/css-values/calc-positive-fraction-001.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-values/ch-unit-011.html [ Failure ]
+crbug.com/626703 external/wpt/css/css-values/attr-invalid-type-008.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-grid/abspos/descendant-static-position-003.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-grid/abspos/descendant-static-position-002.html [ Failure ]
 crbug.com/626703 external/wpt/css/css-grid/abspos/descendant-static-position-001.html [ Failure ]
@@ -3498,7 +3501,6 @@
 crbug.com/626703 [ Mac10.12 ] external/wpt/webxr/xrSession_requestAnimationFrame_getViewerPose.https.html [ Failure Timeout ]
 crbug.com/626703 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-painting-order.html [ Failure ]
 crbug.com/626703 external/wpt/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/tasks.window.html [ Timeout ]
-crbug.com/626703 external/wpt/css/css-transitions/event-dispatch.tentative.html [ Timeout ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-complex-002.svg [ Failure ]
 crbug.com/875411 external/wpt/svg/text/reftests/text-shape-inside-001.svg [ Failure ]
 crbug.com/626703 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/flexbox/flexbox-align-self-vert-002.xhtml [ Failure ]
@@ -4183,6 +4185,7 @@
 crbug.com/915204 external/wpt/css/css-multicol/multicol-span-all-006.html [ Failure ]
 crbug.com/915204 external/wpt/css/css-multicol/multicol-span-all-007.html [ Failure ]
 crbug.com/915204 external/wpt/css/css-multicol/multicol-span-all-009.html [ Failure ]
+crbug.com/915204 external/wpt/css/css-multicol/multicol-span-all-008.html [ Failure ]
 crbug.com/926685 external/wpt/css/css-multicol/multicol-span-all-010.html [ Failure ]
 crbug.com/906385 external/wpt/css/css-multicol/multicol-span-all-list-item-002.html [ Failure ]
 crbug.com/892817 external/wpt/css/css-multicol/multicol-span-all-margin-bottom-001.xht [ Failure ]
@@ -4378,6 +4381,7 @@
 
 #crbug.com/765738 [ Linux Win Mac ] http/tests/wasm/wasm_remote_postMessage_test.https.html [ Pass Timeout ]
 crbug.com/892212 http/tests/wasm/wasm_remote_postMessage_test.https.html [ Pass Failure Timeout ]
+crbug.com/928458 http/tests/wasm/wasm_worker_termination_while_compiling.html [ Pass Crash ]
 
 # ====== Random order flaky tests from here ======
 # These tests are flaky when run in random order, which is the default on Linux & Mac since since 2016-12-16.
@@ -5319,10 +5323,7 @@
 crbug.com/850964 [ Win ] virtual/video-surface-layer/external/wpt/feature-policy/experimental-features/vertical-scroll-touch-block-manual.tentative.html [ Pass Failure ]
 
 # Sheriff 2018-07-05
-crbug.com/831796 virtual/user-activation-v2/fast/events/autoscroll-in-textfield.html [ Failure Pass ]
-
 crbug.com/860731 fast/scroll-snap/animate-fling-to-snap-points.html [ Failure Pass ]
-
 crbug.com/861682 [ Win ] external/wpt/css/mediaqueries/device-aspect-ratio-003.html [ Failure Pass ]
 
 # Other timeouts
@@ -5985,7 +5986,6 @@
 
 # Sheriff 2019-01-18
 crbug.com/922970 external/wpt/css/css-transitions/CSSTransition-startTime.tentative.html [ Failure Pass ]
-crbug.com/923318 [ Win7 ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad.html [ Failure Pass ]
 
 crbug.com/v8/8319 external/wpt/wasm/jsapi/memory/grow.any.html [ Pass Failure ]
 crbug.com/v8/8319 external/wpt/wasm/jsapi/memory/grow.any.worker.html [ Pass Failure ]
@@ -6030,9 +6030,16 @@
 crbug.com/v8/8319 external/wpt/wasm/jsapi/module/customSections.any.html [ Pass Failure ]
 crbug.com/v8/8319 external/wpt/wasm/jsapi/module/customSections.any.worker.html [ Pass Failure ]
 
-crbug.com/927296 virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js [ Pass Failure ]
-
-crbug.com/927334 [ Win ] fast/dom/document-contentType-data-uri.html [ Pass Failure ]
-
 # Sheriff 2019-02-01
-crbug.com/927862 [ Win10 ] virtual/user-activation-v2/fast/events/mousemove-to-scrollbar-changes-cursor.html [ Skip ]
\ No newline at end of file
+# These are crashy on Win10. The first is also flaky on other platforms, but apparently
+# this file doesn't support having two lines referring to the same test with nested specificity.
+crbug.com/927862 virtual/user-activation-v2/fast/events/autoscroll-in-textfield.html [ Skip ]
+crbug.com/927862 [ Win10 ] virtual/user-activation-v2/fast/events/mousemove-to-scrollbar-changes-cursor.html [ Skip ]
+crbug.com/927862 [ Win10 ] virtual/user-activation-v2/fast/events/offsetX-offsetY-svg.html [ Skip ]
+crbug.com/927862 [ Win10 ] virtual/user-activation-v2/fast/events/mutation-during-replace-child-2.html [ Skip ]
+crbug.com/927862 [ Win10 ] virtual/user-activation-v2/fast/events/overflow-scroll-fake-mouse-move.html [ Skip ]
+crbug.com/927862 [ Win10 ] virtual/user-activation-v2/fast/events/event-view-toString.html [ Skip ]
+crbug.com/927862 [ Win10 ] virtual/user-activation-v2/fast/events/no-blur-on-page-leave.html [ Skip ]
+
+# Recently became flaky on multiple platforms (Linux and Windows primarily)
+crbug.com/927769 fast/webgl/OffscreenCanvas-webgl-preserveDrawingBuffer.html [ Skip ]
diff --git a/third_party/blink/web_tests/VirtualTestSuites b/third_party/blink/web_tests/VirtualTestSuites
index d3e1231..6d9323f61 100644
--- a/third_party/blink/web_tests/VirtualTestSuites
+++ b/third_party/blink/web_tests/VirtualTestSuites
@@ -672,17 +672,17 @@
   {
     "prefix": "omt-worker-fetch",
     "base": "external/wpt/workers",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch,NetworkService"]
   },
   {
     "prefix": "omt-worker-fetch",
     "base": "fast/workers",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch,NetworkService"]
   },
   {
     "prefix": "omt-worker-fetch",
     "base": "http/tests/workers",
-    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch"]
+    "args": ["--enable-features=OffMainThreadDedicatedWorkerScriptFetch,OffMainThreadSharedWorkerScriptFetch,NetworkService"]
   },
   {
     "prefix": "webrtc-wpt-plan-b",
diff --git a/third_party/blink/web_tests/W3CImportExpectations b/third_party/blink/web_tests/W3CImportExpectations
index 96288da..c8feb17 100644
--- a/third_party/blink/web_tests/W3CImportExpectations
+++ b/third_party/blink/web_tests/W3CImportExpectations
@@ -115,12 +115,10 @@
 external/wpt/docs [ Skip ]
 external/wpt/dpub-aam [ Skip ]
 external/wpt/dpub-aria [ Skip ]
-external/wpt/fonts/math [ Skip ]
 external/wpt/html-longdesc [ Skip ]
 external/wpt/infrastructure/metadata/infrastructure/browsers/firefox [ Skip ]
 external/wpt/infrastructure/browsers/firefox [ Skip ]
 external/wpt/js [ Skip ]
-external/wpt/mathml [ Skip ]
 external/wpt/old-tests [ Skip ]
 external/wpt/proximity [ Skip ]
 external/wpt/resources/test [ Skip ]
diff --git a/third_party/blink/web_tests/animations/interpolation/border-image-width-interpolation-expected.txt b/third_party/blink/web_tests/animations/interpolation/border-image-width-interpolation-expected.txt
deleted file mode 100644
index 28c4a0b7..0000000
--- a/third_party/blink/web_tests/animations/interpolation/border-image-width-interpolation-expected.txt
+++ /dev/null
@@ -1,464 +0,0 @@
-This is a testharness.js-based test.
-PASS This test uses interpolation-test.js.
-PASS CSS Transitions: property <border-image-width> from neutral to [20px] at (-0.3) is [7px]
-PASS CSS Transitions: property <border-image-width> from neutral to [20px] at (0) is [10px]
-PASS CSS Transitions: property <border-image-width> from neutral to [20px] at (0.3) is [13px]
-PASS CSS Transitions: property <border-image-width> from neutral to [20px] at (0.6) is [16px]
-PASS CSS Transitions: property <border-image-width> from neutral to [20px] at (1) is [20px]
-PASS CSS Transitions: property <border-image-width> from neutral to [20px] at (1.5) is [25px]
-PASS CSS Transitions: property <border-image-width> from neutral to [20px] at (5) is [60px]
-PASS CSS Transitions: property <border-image-width> from neutral to [20px] at (10) is [110px]
-PASS CSS Transitions: property <border-image-width> from [initial] to [20px] at (-0.3) is [20px]
-PASS CSS Transitions: property <border-image-width> from [initial] to [20px] at (0) is [20px]
-PASS CSS Transitions: property <border-image-width> from [initial] to [20px] at (0.3) is [20px]
-PASS CSS Transitions: property <border-image-width> from [initial] to [20px] at (0.5) is [20px]
-PASS CSS Transitions: property <border-image-width> from [initial] to [20px] at (0.6) is [20px]
-PASS CSS Transitions: property <border-image-width> from [initial] to [20px] at (1) is [20px]
-PASS CSS Transitions: property <border-image-width> from [initial] to [20px] at (1.5) is [20px]
-PASS CSS Transitions: property <border-image-width> from [inherit] to [20px] at (-0.3) is [124px]
-PASS CSS Transitions: property <border-image-width> from [inherit] to [20px] at (0) is [100px]
-PASS CSS Transitions: property <border-image-width> from [inherit] to [20px] at (0.3) is [76px]
-PASS CSS Transitions: property <border-image-width> from [inherit] to [20px] at (0.6) is [52px]
-PASS CSS Transitions: property <border-image-width> from [inherit] to [20px] at (1) is [20px]
-PASS CSS Transitions: property <border-image-width> from [inherit] to [20px] at (1.5) is [0px]
-PASS CSS Transitions: property <border-image-width> from [inherit] to [20px] at (5) is [0px]
-PASS CSS Transitions: property <border-image-width> from [inherit] to [20px] at (10) is [0px]
-PASS CSS Transitions: property <border-image-width> from [unset] to [20px] at (-0.3) is [20px]
-PASS CSS Transitions: property <border-image-width> from [unset] to [20px] at (0) is [20px]
-PASS CSS Transitions: property <border-image-width> from [unset] to [20px] at (0.3) is [20px]
-PASS CSS Transitions: property <border-image-width> from [unset] to [20px] at (0.5) is [20px]
-PASS CSS Transitions: property <border-image-width> from [unset] to [20px] at (0.6) is [20px]
-PASS CSS Transitions: property <border-image-width> from [unset] to [20px] at (1) is [20px]
-PASS CSS Transitions: property <border-image-width> from [unset] to [20px] at (1.5) is [20px]
-PASS CSS Transitions: property <border-image-width> from [0px] to [20px] at (-0.3) is [0px]
-PASS CSS Transitions: property <border-image-width> from [0px] to [20px] at (0) is [0px]
-PASS CSS Transitions: property <border-image-width> from [0px] to [20px] at (0.3) is [6px]
-PASS CSS Transitions: property <border-image-width> from [0px] to [20px] at (0.6) is [12px]
-PASS CSS Transitions: property <border-image-width> from [0px] to [20px] at (1) is [20px]
-PASS CSS Transitions: property <border-image-width> from [0px] to [20px] at (1.5) is [30px]
-PASS CSS Transitions: property <border-image-width> from [0px] to [20px] at (5) is [100px]
-PASS CSS Transitions: property <border-image-width> from [0px] to [20px] at (10) is [200px]
-PASS CSS Transitions: property <border-image-width> from [0%] to [20%] at (-0.3) is [0%]
-PASS CSS Transitions: property <border-image-width> from [0%] to [20%] at (0) is [0%]
-PASS CSS Transitions: property <border-image-width> from [0%] to [20%] at (0.3) is [6%]
-PASS CSS Transitions: property <border-image-width> from [0%] to [20%] at (0.6) is [12%]
-PASS CSS Transitions: property <border-image-width> from [0%] to [20%] at (1) is [20%]
-PASS CSS Transitions: property <border-image-width> from [0%] to [20%] at (1.5) is [30%]
-PASS CSS Transitions: property <border-image-width> from [0%] to [20%] at (5) is [100%]
-PASS CSS Transitions: property <border-image-width> from [0%] to [20%] at (10) is [200%]
-PASS CSS Transitions: property <border-image-width> from [0] to [20] at (-0.3) is [0]
-PASS CSS Transitions: property <border-image-width> from [0] to [20] at (0) is [0]
-PASS CSS Transitions: property <border-image-width> from [0] to [20] at (0.3) is [6]
-PASS CSS Transitions: property <border-image-width> from [0] to [20] at (0.6) is [12]
-PASS CSS Transitions: property <border-image-width> from [0] to [20] at (1) is [20]
-PASS CSS Transitions: property <border-image-width> from [0] to [20] at (1.5) is [30]
-PASS CSS Transitions: property <border-image-width> from [0] to [20] at (5) is [100]
-PASS CSS Transitions: property <border-image-width> from [0] to [20] at (10) is [200]
-PASS CSS Transitions: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (-0.3) is [0px 5% 21 37px]
-PASS CSS Transitions: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0) is [10px 20% 30 40px]
-PASS CSS Transitions: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0.3) is [31px 35% 39 43px]
-PASS CSS Transitions: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0.6) is [52px 50% 48 46px]
-PASS CSS Transitions: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (1) is [80px 70% 60 50px]
-PASS CSS Transitions: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (1.5) is [115px 95% 75 55px]
-PASS CSS Transitions: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (5) is [360px 270% 180 90px]
-PASS CSS Transitions: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (10) is [710px 520% 330 140px]
-FAIL CSS Transitions: property <border-image-width> from [10%] to [20px] at (-0.3) is [calc(13% + -6px)] assert_equals: expected "7px " but got "calc ( 13 % + - 6px ) "
-PASS CSS Transitions: property <border-image-width> from [10%] to [20px] at (0) is [10%]
-FAIL CSS Transitions: property <border-image-width> from [10%] to [20px] at (0.3) is [calc(7% + 6px)] assert_equals: expected "13px " but got "calc ( 7 % + 6px ) "
-FAIL CSS Transitions: property <border-image-width> from [10%] to [20px] at (0.6) is [calc(4% + 12px)] assert_equals: expected "16px " but got "calc ( 4 % + 12px ) "
-PASS CSS Transitions: property <border-image-width> from [10%] to [20px] at (1) is [20px]
-FAIL CSS Transitions: property <border-image-width> from [10%] to [20px] at (1.5) is [calc(-5% + 30px)] assert_equals: expected "25px " but got "calc ( - 5 % + 30px ) "
-FAIL CSS Transitions: property <border-image-width> from [10px] to [20%] at (-0.3) is [calc(-6% + 13px)] assert_equals: expected "7px " but got "calc ( - 6 % + 13px ) "
-PASS CSS Transitions: property <border-image-width> from [10px] to [20%] at (0) is [10px]
-FAIL CSS Transitions: property <border-image-width> from [10px] to [20%] at (0.3) is [calc(6% + 7px)] assert_equals: expected "13px " but got "calc ( 6 % + 7px ) "
-FAIL CSS Transitions: property <border-image-width> from [10px] to [20%] at (0.6) is [calc(12% + 4px)] assert_equals: expected "16px " but got "calc ( 12 % + 4px ) "
-PASS CSS Transitions: property <border-image-width> from [10px] to [20%] at (1) is [20%]
-FAIL CSS Transitions: property <border-image-width> from [10px] to [20%] at (1.5) is [calc(30% + -5px)] assert_equals: expected "25px " but got "calc ( 30 % + - 5px ) "
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (-0.3) is [0px auto auto 0]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0) is [10px auto auto 20]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0.3) is [40px auto auto 50]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0.6) is [70px auto auto 80]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (1) is [110px auto auto 120]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (1.5) is [160px auto auto 170]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (-0.3) is [110px auto 120]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0) is [110px auto 120]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.3) is [110px auto 120]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.5) is [110px auto 120]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.6) is [110px auto 120]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (1) is [110px auto 120]
-PASS CSS Transitions: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (1.5) is [110px auto 120]
-PASS CSS Transitions: property <border-image-width> from [10px] to [20] at (-0.3) is [20]
-PASS CSS Transitions: property <border-image-width> from [10px] to [20] at (0) is [20]
-PASS CSS Transitions: property <border-image-width> from [10px] to [20] at (0.3) is [20]
-PASS CSS Transitions: property <border-image-width> from [10px] to [20] at (0.5) is [20]
-PASS CSS Transitions: property <border-image-width> from [10px] to [20] at (0.6) is [20]
-PASS CSS Transitions: property <border-image-width> from [10px] to [20] at (1) is [20]
-PASS CSS Transitions: property <border-image-width> from [10px] to [20] at (1.5) is [20]
-PASS CSS Transitions: property <border-image-width> from [10] to [20px] at (-0.3) is [20px]
-PASS CSS Transitions: property <border-image-width> from [10] to [20px] at (0) is [20px]
-PASS CSS Transitions: property <border-image-width> from [10] to [20px] at (0.3) is [20px]
-PASS CSS Transitions: property <border-image-width> from [10] to [20px] at (0.5) is [20px]
-PASS CSS Transitions: property <border-image-width> from [10] to [20px] at (0.6) is [20px]
-PASS CSS Transitions: property <border-image-width> from [10] to [20px] at (1) is [20px]
-PASS CSS Transitions: property <border-image-width> from [10] to [20px] at (1.5) is [20px]
-PASS CSS Transitions: property <border-image-width> from [10%] to [20] at (-0.3) is [20]
-PASS CSS Transitions: property <border-image-width> from [10%] to [20] at (0) is [20]
-PASS CSS Transitions: property <border-image-width> from [10%] to [20] at (0.3) is [20]
-PASS CSS Transitions: property <border-image-width> from [10%] to [20] at (0.5) is [20]
-PASS CSS Transitions: property <border-image-width> from [10%] to [20] at (0.6) is [20]
-PASS CSS Transitions: property <border-image-width> from [10%] to [20] at (1) is [20]
-PASS CSS Transitions: property <border-image-width> from [10%] to [20] at (1.5) is [20]
-PASS CSS Transitions: property <border-image-width> from [10] to [20%] at (-0.3) is [20%]
-PASS CSS Transitions: property <border-image-width> from [10] to [20%] at (0) is [20%]
-PASS CSS Transitions: property <border-image-width> from [10] to [20%] at (0.3) is [20%]
-PASS CSS Transitions: property <border-image-width> from [10] to [20%] at (0.5) is [20%]
-PASS CSS Transitions: property <border-image-width> from [10] to [20%] at (0.6) is [20%]
-PASS CSS Transitions: property <border-image-width> from [10] to [20%] at (1) is [20%]
-PASS CSS Transitions: property <border-image-width> from [10] to [20%] at (1.5) is [20%]
-PASS CSS Transitions with transition: all: property <border-image-width> from neutral to [20px] at (-0.3) is [7px]
-PASS CSS Transitions with transition: all: property <border-image-width> from neutral to [20px] at (0) is [10px]
-PASS CSS Transitions with transition: all: property <border-image-width> from neutral to [20px] at (0.3) is [13px]
-PASS CSS Transitions with transition: all: property <border-image-width> from neutral to [20px] at (0.6) is [16px]
-PASS CSS Transitions with transition: all: property <border-image-width> from neutral to [20px] at (1) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from neutral to [20px] at (1.5) is [25px]
-PASS CSS Transitions with transition: all: property <border-image-width> from neutral to [20px] at (5) is [60px]
-PASS CSS Transitions with transition: all: property <border-image-width> from neutral to [20px] at (10) is [110px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [initial] to [20px] at (-0.3) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [initial] to [20px] at (0) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [initial] to [20px] at (0.3) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [initial] to [20px] at (0.5) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [initial] to [20px] at (0.6) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [initial] to [20px] at (1) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [initial] to [20px] at (1.5) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [inherit] to [20px] at (-0.3) is [124px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [inherit] to [20px] at (0) is [100px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [inherit] to [20px] at (0.3) is [76px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [inherit] to [20px] at (0.6) is [52px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [inherit] to [20px] at (1) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [inherit] to [20px] at (1.5) is [0px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [inherit] to [20px] at (5) is [0px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [inherit] to [20px] at (10) is [0px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [unset] to [20px] at (-0.3) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [unset] to [20px] at (0) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [unset] to [20px] at (0.3) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [unset] to [20px] at (0.5) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [unset] to [20px] at (0.6) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [unset] to [20px] at (1) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [unset] to [20px] at (1.5) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0px] to [20px] at (-0.3) is [0px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0px] to [20px] at (0) is [0px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0px] to [20px] at (0.3) is [6px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0px] to [20px] at (0.6) is [12px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0px] to [20px] at (1) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0px] to [20px] at (1.5) is [30px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0px] to [20px] at (5) is [100px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0px] to [20px] at (10) is [200px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0%] to [20%] at (-0.3) is [0%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0%] to [20%] at (0) is [0%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0%] to [20%] at (0.3) is [6%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0%] to [20%] at (0.6) is [12%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0%] to [20%] at (1) is [20%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0%] to [20%] at (1.5) is [30%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0%] to [20%] at (5) is [100%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0%] to [20%] at (10) is [200%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0] to [20] at (-0.3) is [0]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0] to [20] at (0) is [0]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0] to [20] at (0.3) is [6]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0] to [20] at (0.6) is [12]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0] to [20] at (1) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0] to [20] at (1.5) is [30]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0] to [20] at (5) is [100]
-PASS CSS Transitions with transition: all: property <border-image-width> from [0] to [20] at (10) is [200]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (-0.3) is [0px 5% 21 37px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0) is [10px 20% 30 40px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0.3) is [31px 35% 39 43px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0.6) is [52px 50% 48 46px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (1) is [80px 70% 60 50px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (1.5) is [115px 95% 75 55px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (5) is [360px 270% 180 90px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (10) is [710px 520% 330 140px]
-FAIL CSS Transitions with transition: all: property <border-image-width> from [10%] to [20px] at (-0.3) is [calc(13% + -6px)] assert_equals: expected "7px " but got "calc ( 13 % + - 6px ) "
-PASS CSS Transitions with transition: all: property <border-image-width> from [10%] to [20px] at (0) is [10%]
-FAIL CSS Transitions with transition: all: property <border-image-width> from [10%] to [20px] at (0.3) is [calc(7% + 6px)] assert_equals: expected "13px " but got "calc ( 7 % + 6px ) "
-FAIL CSS Transitions with transition: all: property <border-image-width> from [10%] to [20px] at (0.6) is [calc(4% + 12px)] assert_equals: expected "16px " but got "calc ( 4 % + 12px ) "
-PASS CSS Transitions with transition: all: property <border-image-width> from [10%] to [20px] at (1) is [20px]
-FAIL CSS Transitions with transition: all: property <border-image-width> from [10%] to [20px] at (1.5) is [calc(-5% + 30px)] assert_equals: expected "25px " but got "calc ( - 5 % + 30px ) "
-FAIL CSS Transitions with transition: all: property <border-image-width> from [10px] to [20%] at (-0.3) is [calc(-6% + 13px)] assert_equals: expected "7px " but got "calc ( - 6 % + 13px ) "
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px] to [20%] at (0) is [10px]
-FAIL CSS Transitions with transition: all: property <border-image-width> from [10px] to [20%] at (0.3) is [calc(6% + 7px)] assert_equals: expected "13px " but got "calc ( 6 % + 7px ) "
-FAIL CSS Transitions with transition: all: property <border-image-width> from [10px] to [20%] at (0.6) is [calc(12% + 4px)] assert_equals: expected "16px " but got "calc ( 12 % + 4px ) "
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px] to [20%] at (1) is [20%]
-FAIL CSS Transitions with transition: all: property <border-image-width> from [10px] to [20%] at (1.5) is [calc(30% + -5px)] assert_equals: expected "25px " but got "calc ( 30 % + - 5px ) "
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (-0.3) is [0px auto auto 0]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0) is [10px auto auto 20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0.3) is [40px auto auto 50]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0.6) is [70px auto auto 80]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (1) is [110px auto auto 120]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (1.5) is [160px auto auto 170]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (-0.3) is [110px auto 120]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0) is [110px auto 120]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.3) is [110px auto 120]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.5) is [110px auto 120]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.6) is [110px auto 120]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (1) is [110px auto 120]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (1.5) is [110px auto 120]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px] to [20] at (-0.3) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px] to [20] at (0) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px] to [20] at (0.3) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px] to [20] at (0.5) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px] to [20] at (0.6) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px] to [20] at (1) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10px] to [20] at (1.5) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20px] at (-0.3) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20px] at (0) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20px] at (0.3) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20px] at (0.5) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20px] at (0.6) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20px] at (1) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20px] at (1.5) is [20px]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10%] to [20] at (-0.3) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10%] to [20] at (0) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10%] to [20] at (0.3) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10%] to [20] at (0.5) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10%] to [20] at (0.6) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10%] to [20] at (1) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10%] to [20] at (1.5) is [20]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20%] at (-0.3) is [20%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20%] at (0) is [20%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20%] at (0.3) is [20%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20%] at (0.5) is [20%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20%] at (0.6) is [20%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20%] at (1) is [20%]
-PASS CSS Transitions with transition: all: property <border-image-width> from [10] to [20%] at (1.5) is [20%]
-PASS CSS Animations: property <border-image-width> from neutral to [20px] at (-0.3) is [7px]
-PASS CSS Animations: property <border-image-width> from neutral to [20px] at (0) is [10px]
-PASS CSS Animations: property <border-image-width> from neutral to [20px] at (0.3) is [13px]
-PASS CSS Animations: property <border-image-width> from neutral to [20px] at (0.6) is [16px]
-PASS CSS Animations: property <border-image-width> from neutral to [20px] at (1) is [20px]
-PASS CSS Animations: property <border-image-width> from neutral to [20px] at (1.5) is [25px]
-PASS CSS Animations: property <border-image-width> from neutral to [20px] at (5) is [60px]
-PASS CSS Animations: property <border-image-width> from neutral to [20px] at (10) is [110px]
-PASS CSS Animations: property <border-image-width> from [initial] to [20px] at (-0.3) is [1]
-PASS CSS Animations: property <border-image-width> from [initial] to [20px] at (0) is [1]
-PASS CSS Animations: property <border-image-width> from [initial] to [20px] at (0.3) is [1]
-PASS CSS Animations: property <border-image-width> from [initial] to [20px] at (0.5) is [20px]
-PASS CSS Animations: property <border-image-width> from [initial] to [20px] at (0.6) is [20px]
-PASS CSS Animations: property <border-image-width> from [initial] to [20px] at (1) is [20px]
-PASS CSS Animations: property <border-image-width> from [initial] to [20px] at (1.5) is [20px]
-PASS CSS Animations: property <border-image-width> from [inherit] to [20px] at (-0.3) is [124px]
-PASS CSS Animations: property <border-image-width> from [inherit] to [20px] at (0) is [100px]
-PASS CSS Animations: property <border-image-width> from [inherit] to [20px] at (0.3) is [76px]
-PASS CSS Animations: property <border-image-width> from [inherit] to [20px] at (0.6) is [52px]
-PASS CSS Animations: property <border-image-width> from [inherit] to [20px] at (1) is [20px]
-PASS CSS Animations: property <border-image-width> from [inherit] to [20px] at (1.5) is [0px]
-PASS CSS Animations: property <border-image-width> from [inherit] to [20px] at (5) is [0px]
-PASS CSS Animations: property <border-image-width> from [inherit] to [20px] at (10) is [0px]
-PASS CSS Animations: property <border-image-width> from [unset] to [20px] at (-0.3) is [1]
-PASS CSS Animations: property <border-image-width> from [unset] to [20px] at (0) is [1]
-PASS CSS Animations: property <border-image-width> from [unset] to [20px] at (0.3) is [1]
-PASS CSS Animations: property <border-image-width> from [unset] to [20px] at (0.5) is [20px]
-PASS CSS Animations: property <border-image-width> from [unset] to [20px] at (0.6) is [20px]
-PASS CSS Animations: property <border-image-width> from [unset] to [20px] at (1) is [20px]
-PASS CSS Animations: property <border-image-width> from [unset] to [20px] at (1.5) is [20px]
-PASS CSS Animations: property <border-image-width> from [0px] to [20px] at (-0.3) is [0px]
-PASS CSS Animations: property <border-image-width> from [0px] to [20px] at (0) is [0px]
-PASS CSS Animations: property <border-image-width> from [0px] to [20px] at (0.3) is [6px]
-PASS CSS Animations: property <border-image-width> from [0px] to [20px] at (0.6) is [12px]
-PASS CSS Animations: property <border-image-width> from [0px] to [20px] at (1) is [20px]
-PASS CSS Animations: property <border-image-width> from [0px] to [20px] at (1.5) is [30px]
-PASS CSS Animations: property <border-image-width> from [0px] to [20px] at (5) is [100px]
-PASS CSS Animations: property <border-image-width> from [0px] to [20px] at (10) is [200px]
-PASS CSS Animations: property <border-image-width> from [0%] to [20%] at (-0.3) is [0%]
-PASS CSS Animations: property <border-image-width> from [0%] to [20%] at (0) is [0%]
-PASS CSS Animations: property <border-image-width> from [0%] to [20%] at (0.3) is [6%]
-PASS CSS Animations: property <border-image-width> from [0%] to [20%] at (0.6) is [12%]
-PASS CSS Animations: property <border-image-width> from [0%] to [20%] at (1) is [20%]
-PASS CSS Animations: property <border-image-width> from [0%] to [20%] at (1.5) is [30%]
-PASS CSS Animations: property <border-image-width> from [0%] to [20%] at (5) is [100%]
-PASS CSS Animations: property <border-image-width> from [0%] to [20%] at (10) is [200%]
-PASS CSS Animations: property <border-image-width> from [0] to [20] at (-0.3) is [0]
-PASS CSS Animations: property <border-image-width> from [0] to [20] at (0) is [0]
-PASS CSS Animations: property <border-image-width> from [0] to [20] at (0.3) is [6]
-PASS CSS Animations: property <border-image-width> from [0] to [20] at (0.6) is [12]
-PASS CSS Animations: property <border-image-width> from [0] to [20] at (1) is [20]
-PASS CSS Animations: property <border-image-width> from [0] to [20] at (1.5) is [30]
-PASS CSS Animations: property <border-image-width> from [0] to [20] at (5) is [100]
-PASS CSS Animations: property <border-image-width> from [0] to [20] at (10) is [200]
-PASS CSS Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (-0.3) is [0px 5% 21 37px]
-PASS CSS Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0) is [10px 20% 30 40px]
-PASS CSS Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0.3) is [31px 35% 39 43px]
-PASS CSS Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0.6) is [52px 50% 48 46px]
-PASS CSS Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (1) is [80px 70% 60 50px]
-PASS CSS Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (1.5) is [115px 95% 75 55px]
-PASS CSS Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (5) is [360px 270% 180 90px]
-PASS CSS Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (10) is [710px 520% 330 140px]
-FAIL CSS Animations: property <border-image-width> from [10%] to [20px] at (-0.3) is [calc(13% + -6px)] assert_equals: expected "7px " but got "calc ( 13 % + - 6px ) "
-PASS CSS Animations: property <border-image-width> from [10%] to [20px] at (0) is [10%]
-FAIL CSS Animations: property <border-image-width> from [10%] to [20px] at (0.3) is [calc(7% + 6px)] assert_equals: expected "13px " but got "calc ( 7 % + 6px ) "
-FAIL CSS Animations: property <border-image-width> from [10%] to [20px] at (0.6) is [calc(4% + 12px)] assert_equals: expected "16px " but got "calc ( 4 % + 12px ) "
-PASS CSS Animations: property <border-image-width> from [10%] to [20px] at (1) is [20px]
-FAIL CSS Animations: property <border-image-width> from [10%] to [20px] at (1.5) is [calc(-5% + 30px)] assert_equals: expected "25px " but got "calc ( - 5 % + 30px ) "
-FAIL CSS Animations: property <border-image-width> from [10px] to [20%] at (-0.3) is [calc(-6% + 13px)] assert_equals: expected "7px " but got "calc ( - 6 % + 13px ) "
-PASS CSS Animations: property <border-image-width> from [10px] to [20%] at (0) is [10px]
-FAIL CSS Animations: property <border-image-width> from [10px] to [20%] at (0.3) is [calc(6% + 7px)] assert_equals: expected "13px " but got "calc ( 6 % + 7px ) "
-FAIL CSS Animations: property <border-image-width> from [10px] to [20%] at (0.6) is [calc(12% + 4px)] assert_equals: expected "16px " but got "calc ( 12 % + 4px ) "
-PASS CSS Animations: property <border-image-width> from [10px] to [20%] at (1) is [20%]
-FAIL CSS Animations: property <border-image-width> from [10px] to [20%] at (1.5) is [calc(30% + -5px)] assert_equals: expected "25px " but got "calc ( 30 % + - 5px ) "
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (-0.3) is [0px auto auto 0]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0) is [10px auto auto 20]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0.3) is [40px auto auto 50]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0.6) is [70px auto auto 80]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (1) is [110px auto auto 120]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (1.5) is [160px auto auto 170]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (-0.3) is [10px auto auto 20]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0) is [10px auto auto 20]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.3) is [10px auto auto 20]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.5) is [110px auto 120]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.6) is [110px auto 120]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (1) is [110px auto 120]
-PASS CSS Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (1.5) is [110px auto 120]
-PASS CSS Animations: property <border-image-width> from [10px] to [20] at (-0.3) is [10px]
-PASS CSS Animations: property <border-image-width> from [10px] to [20] at (0) is [10px]
-PASS CSS Animations: property <border-image-width> from [10px] to [20] at (0.3) is [10px]
-PASS CSS Animations: property <border-image-width> from [10px] to [20] at (0.5) is [20]
-PASS CSS Animations: property <border-image-width> from [10px] to [20] at (0.6) is [20]
-PASS CSS Animations: property <border-image-width> from [10px] to [20] at (1) is [20]
-PASS CSS Animations: property <border-image-width> from [10px] to [20] at (1.5) is [20]
-PASS CSS Animations: property <border-image-width> from [10] to [20px] at (-0.3) is [10]
-PASS CSS Animations: property <border-image-width> from [10] to [20px] at (0) is [10]
-PASS CSS Animations: property <border-image-width> from [10] to [20px] at (0.3) is [10]
-PASS CSS Animations: property <border-image-width> from [10] to [20px] at (0.5) is [20px]
-PASS CSS Animations: property <border-image-width> from [10] to [20px] at (0.6) is [20px]
-PASS CSS Animations: property <border-image-width> from [10] to [20px] at (1) is [20px]
-PASS CSS Animations: property <border-image-width> from [10] to [20px] at (1.5) is [20px]
-PASS CSS Animations: property <border-image-width> from [10%] to [20] at (-0.3) is [10%]
-PASS CSS Animations: property <border-image-width> from [10%] to [20] at (0) is [10%]
-PASS CSS Animations: property <border-image-width> from [10%] to [20] at (0.3) is [10%]
-PASS CSS Animations: property <border-image-width> from [10%] to [20] at (0.5) is [20]
-PASS CSS Animations: property <border-image-width> from [10%] to [20] at (0.6) is [20]
-PASS CSS Animations: property <border-image-width> from [10%] to [20] at (1) is [20]
-PASS CSS Animations: property <border-image-width> from [10%] to [20] at (1.5) is [20]
-PASS CSS Animations: property <border-image-width> from [10] to [20%] at (-0.3) is [10]
-PASS CSS Animations: property <border-image-width> from [10] to [20%] at (0) is [10]
-PASS CSS Animations: property <border-image-width> from [10] to [20%] at (0.3) is [10]
-PASS CSS Animations: property <border-image-width> from [10] to [20%] at (0.5) is [20%]
-PASS CSS Animations: property <border-image-width> from [10] to [20%] at (0.6) is [20%]
-PASS CSS Animations: property <border-image-width> from [10] to [20%] at (1) is [20%]
-PASS CSS Animations: property <border-image-width> from [10] to [20%] at (1.5) is [20%]
-PASS Web Animations: property <border-image-width> from neutral to [20px] at (-0.3) is [7px]
-PASS Web Animations: property <border-image-width> from neutral to [20px] at (0) is [10px]
-PASS Web Animations: property <border-image-width> from neutral to [20px] at (0.3) is [13px]
-PASS Web Animations: property <border-image-width> from neutral to [20px] at (0.6) is [16px]
-PASS Web Animations: property <border-image-width> from neutral to [20px] at (1) is [20px]
-PASS Web Animations: property <border-image-width> from neutral to [20px] at (1.5) is [25px]
-PASS Web Animations: property <border-image-width> from neutral to [20px] at (5) is [60px]
-PASS Web Animations: property <border-image-width> from neutral to [20px] at (10) is [110px]
-PASS Web Animations: property <border-image-width> from [initial] to [20px] at (-0.3) is [1]
-PASS Web Animations: property <border-image-width> from [initial] to [20px] at (0) is [1]
-PASS Web Animations: property <border-image-width> from [initial] to [20px] at (0.3) is [1]
-PASS Web Animations: property <border-image-width> from [initial] to [20px] at (0.5) is [20px]
-PASS Web Animations: property <border-image-width> from [initial] to [20px] at (0.6) is [20px]
-PASS Web Animations: property <border-image-width> from [initial] to [20px] at (1) is [20px]
-PASS Web Animations: property <border-image-width> from [initial] to [20px] at (1.5) is [20px]
-PASS Web Animations: property <border-image-width> from [inherit] to [20px] at (-0.3) is [124px]
-PASS Web Animations: property <border-image-width> from [inherit] to [20px] at (0) is [100px]
-PASS Web Animations: property <border-image-width> from [inherit] to [20px] at (0.3) is [76px]
-PASS Web Animations: property <border-image-width> from [inherit] to [20px] at (0.6) is [52px]
-PASS Web Animations: property <border-image-width> from [inherit] to [20px] at (1) is [20px]
-PASS Web Animations: property <border-image-width> from [inherit] to [20px] at (1.5) is [0px]
-PASS Web Animations: property <border-image-width> from [inherit] to [20px] at (5) is [0px]
-PASS Web Animations: property <border-image-width> from [inherit] to [20px] at (10) is [0px]
-PASS Web Animations: property <border-image-width> from [unset] to [20px] at (-0.3) is [1]
-PASS Web Animations: property <border-image-width> from [unset] to [20px] at (0) is [1]
-PASS Web Animations: property <border-image-width> from [unset] to [20px] at (0.3) is [1]
-PASS Web Animations: property <border-image-width> from [unset] to [20px] at (0.5) is [20px]
-PASS Web Animations: property <border-image-width> from [unset] to [20px] at (0.6) is [20px]
-PASS Web Animations: property <border-image-width> from [unset] to [20px] at (1) is [20px]
-PASS Web Animations: property <border-image-width> from [unset] to [20px] at (1.5) is [20px]
-PASS Web Animations: property <border-image-width> from [0px] to [20px] at (-0.3) is [0px]
-PASS Web Animations: property <border-image-width> from [0px] to [20px] at (0) is [0px]
-PASS Web Animations: property <border-image-width> from [0px] to [20px] at (0.3) is [6px]
-PASS Web Animations: property <border-image-width> from [0px] to [20px] at (0.6) is [12px]
-PASS Web Animations: property <border-image-width> from [0px] to [20px] at (1) is [20px]
-PASS Web Animations: property <border-image-width> from [0px] to [20px] at (1.5) is [30px]
-PASS Web Animations: property <border-image-width> from [0px] to [20px] at (5) is [100px]
-PASS Web Animations: property <border-image-width> from [0px] to [20px] at (10) is [200px]
-PASS Web Animations: property <border-image-width> from [0%] to [20%] at (-0.3) is [0%]
-PASS Web Animations: property <border-image-width> from [0%] to [20%] at (0) is [0%]
-PASS Web Animations: property <border-image-width> from [0%] to [20%] at (0.3) is [6%]
-PASS Web Animations: property <border-image-width> from [0%] to [20%] at (0.6) is [12%]
-PASS Web Animations: property <border-image-width> from [0%] to [20%] at (1) is [20%]
-PASS Web Animations: property <border-image-width> from [0%] to [20%] at (1.5) is [30%]
-PASS Web Animations: property <border-image-width> from [0%] to [20%] at (5) is [100%]
-PASS Web Animations: property <border-image-width> from [0%] to [20%] at (10) is [200%]
-PASS Web Animations: property <border-image-width> from [0] to [20] at (-0.3) is [0]
-PASS Web Animations: property <border-image-width> from [0] to [20] at (0) is [0]
-PASS Web Animations: property <border-image-width> from [0] to [20] at (0.3) is [6]
-PASS Web Animations: property <border-image-width> from [0] to [20] at (0.6) is [12]
-PASS Web Animations: property <border-image-width> from [0] to [20] at (1) is [20]
-PASS Web Animations: property <border-image-width> from [0] to [20] at (1.5) is [30]
-PASS Web Animations: property <border-image-width> from [0] to [20] at (5) is [100]
-PASS Web Animations: property <border-image-width> from [0] to [20] at (10) is [200]
-PASS Web Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (-0.3) is [0px 5% 21 37px]
-PASS Web Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0) is [10px 20% 30 40px]
-PASS Web Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0.3) is [31px 35% 39 43px]
-PASS Web Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (0.6) is [52px 50% 48 46px]
-PASS Web Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (1) is [80px 70% 60 50px]
-PASS Web Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (1.5) is [115px 95% 75 55px]
-PASS Web Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (5) is [360px 270% 180 90px]
-PASS Web Animations: property <border-image-width> from [10px 20% 30 40px] to [80px 70% 60 50px] at (10) is [710px 520% 330 140px]
-FAIL Web Animations: property <border-image-width> from [10%] to [20px] at (-0.3) is [calc(13% + -6px)] assert_equals: expected "7px " but got "calc ( 13 % + - 6px ) "
-PASS Web Animations: property <border-image-width> from [10%] to [20px] at (0) is [10%]
-FAIL Web Animations: property <border-image-width> from [10%] to [20px] at (0.3) is [calc(7% + 6px)] assert_equals: expected "13px " but got "calc ( 7 % + 6px ) "
-FAIL Web Animations: property <border-image-width> from [10%] to [20px] at (0.6) is [calc(4% + 12px)] assert_equals: expected "16px " but got "calc ( 4 % + 12px ) "
-PASS Web Animations: property <border-image-width> from [10%] to [20px] at (1) is [20px]
-FAIL Web Animations: property <border-image-width> from [10%] to [20px] at (1.5) is [calc(-5% + 30px)] assert_equals: expected "25px " but got "calc ( - 5 % + 30px ) "
-FAIL Web Animations: property <border-image-width> from [10px] to [20%] at (-0.3) is [calc(-6% + 13px)] assert_equals: expected "7px " but got "calc ( - 6 % + 13px ) "
-PASS Web Animations: property <border-image-width> from [10px] to [20%] at (0) is [10px]
-FAIL Web Animations: property <border-image-width> from [10px] to [20%] at (0.3) is [calc(6% + 7px)] assert_equals: expected "13px " but got "calc ( 6 % + 7px ) "
-FAIL Web Animations: property <border-image-width> from [10px] to [20%] at (0.6) is [calc(12% + 4px)] assert_equals: expected "16px " but got "calc ( 12 % + 4px ) "
-PASS Web Animations: property <border-image-width> from [10px] to [20%] at (1) is [20%]
-FAIL Web Animations: property <border-image-width> from [10px] to [20%] at (1.5) is [calc(30% + -5px)] assert_equals: expected "25px " but got "calc ( 30 % + - 5px ) "
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (-0.3) is [0px auto auto 0]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0) is [10px auto auto 20]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0.3) is [40px auto auto 50]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (0.6) is [70px auto auto 80]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (1) is [110px auto auto 120]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto auto 120] at (1.5) is [160px auto auto 170]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (-0.3) is [10px auto auto 20]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0) is [10px auto auto 20]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.3) is [10px auto auto 20]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.5) is [110px auto 120]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (0.6) is [110px auto 120]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (1) is [110px auto 120]
-PASS Web Animations: property <border-image-width> from [10px auto auto 20] to [110px auto 120 auto] at (1.5) is [110px auto 120]
-PASS Web Animations: property <border-image-width> from [10px] to [20] at (-0.3) is [10px]
-PASS Web Animations: property <border-image-width> from [10px] to [20] at (0) is [10px]
-PASS Web Animations: property <border-image-width> from [10px] to [20] at (0.3) is [10px]
-PASS Web Animations: property <border-image-width> from [10px] to [20] at (0.5) is [20]
-PASS Web Animations: property <border-image-width> from [10px] to [20] at (0.6) is [20]
-PASS Web Animations: property <border-image-width> from [10px] to [20] at (1) is [20]
-PASS Web Animations: property <border-image-width> from [10px] to [20] at (1.5) is [20]
-PASS Web Animations: property <border-image-width> from [10] to [20px] at (-0.3) is [10]
-PASS Web Animations: property <border-image-width> from [10] to [20px] at (0) is [10]
-PASS Web Animations: property <border-image-width> from [10] to [20px] at (0.3) is [10]
-PASS Web Animations: property <border-image-width> from [10] to [20px] at (0.5) is [20px]
-PASS Web Animations: property <border-image-width> from [10] to [20px] at (0.6) is [20px]
-PASS Web Animations: property <border-image-width> from [10] to [20px] at (1) is [20px]
-PASS Web Animations: property <border-image-width> from [10] to [20px] at (1.5) is [20px]
-PASS Web Animations: property <border-image-width> from [10%] to [20] at (-0.3) is [10%]
-PASS Web Animations: property <border-image-width> from [10%] to [20] at (0) is [10%]
-PASS Web Animations: property <border-image-width> from [10%] to [20] at (0.3) is [10%]
-PASS Web Animations: property <border-image-width> from [10%] to [20] at (0.5) is [20]
-PASS Web Animations: property <border-image-width> from [10%] to [20] at (0.6) is [20]
-PASS Web Animations: property <border-image-width> from [10%] to [20] at (1) is [20]
-PASS Web Animations: property <border-image-width> from [10%] to [20] at (1.5) is [20]
-PASS Web Animations: property <border-image-width> from [10] to [20%] at (-0.3) is [10]
-PASS Web Animations: property <border-image-width> from [10] to [20%] at (0) is [10]
-PASS Web Animations: property <border-image-width> from [10] to [20%] at (0.3) is [10]
-PASS Web Animations: property <border-image-width> from [10] to [20%] at (0.5) is [20%]
-PASS Web Animations: property <border-image-width> from [10] to [20%] at (0.6) is [20%]
-PASS Web Animations: property <border-image-width> from [10] to [20%] at (1) is [20%]
-PASS Web Animations: property <border-image-width> from [10] to [20%] at (1.5) is [20%]
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/display-lock/lock-after-append/acquire-update-disconnect-commit-expected.html b/third_party/blink/web_tests/display-lock/lock-after-append/acquire-update-disconnect-commit-expected.html
new file mode 100644
index 0000000..cbafa7bb
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-after-append/acquire-update-disconnect-commit-expected.html
@@ -0,0 +1,17 @@
+<!doctype HTML>
+
+<style>
+#container {
+  contain: style layout;
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#child {
+  width: 50px;
+  height: 50px;
+  background: lightgreen;
+}
+</style>
+
+<div id="log">PASS</div>
diff --git a/third_party/blink/web_tests/display-lock/lock-after-append/acquire-update-disconnect-commit.html b/third_party/blink/web_tests/display-lock/lock-after-append/acquire-update-disconnect-commit.html
new file mode 100644
index 0000000..65accc7
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-after-append/acquire-update-disconnect-commit.html
@@ -0,0 +1,51 @@
+<!doctype HTML>
+
+<style>
+#container {
+  contain: style layout;
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#child {
+  width: 50px;
+  height: 50px;
+  background: lightgreen;
+}
+</style>
+
+<div id="log"></div>
+<div id="container"></div>
+
+<script>
+// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
+if (window.testRunner)
+  window.testRunner.waitUntilDone();
+
+function finishTest(status_string) {
+  if (document.getElementById("log").innerHTML === "")
+    document.getElementById("log").innerHTML = status_string;
+  if (window.testRunner)
+    window.testRunner.notifyDone();
+}
+
+function runTest() {
+  let container = document.getElementById("container");
+  container.displayLock.acquire({ timeout: Infinity }).then(() => {
+    // Update, then disconnect the element, and commit.
+    let update_promise = container.displayLock.update();
+    container.remove();
+    let commit_promise = container.displayLock.commit();
+
+    // The update promise should reject and commit one should succeed.
+    Promise.all([
+      new Promise((resolve, reject) => update_promise.then(reject, resolve)),
+      commit_promise
+    ]).then(
+      () => finishTest("PASS"),
+      () => finishTest("FAIL"));
+  });
+}
+
+window.onload = runTest;
+</script>
diff --git a/third_party/blink/web_tests/display-lock/lock-after-append/measure-forced-layout-after-commit.html b/third_party/blink/web_tests/display-lock/lock-after-append/measure-forced-layout-after-commit.html
new file mode 100644
index 0000000..0091e45b
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-after-append/measure-forced-layout-after-commit.html
@@ -0,0 +1,91 @@
+<!doctype HTML>
+
+<style>
+.contained {
+  contain: style layout;
+  background: lightgreen;
+}
+#small {
+  width: 100px;
+  height: 100px;
+}
+#large {
+  width: 200px;
+  height: 200px;
+}
+.child {
+  width: 20px;
+  height: 20%;
+  background: cyan;
+}
+#spacer {
+  width: 150px;
+  height: 150px;
+  background: green;
+}
+</style>
+
+<div id="parent"><div class="contained" id="small"></div></div>
+<div id="spacer"></div>
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+
+<script>
+async_test((t) => {
+  function createChild(id) {
+    let child = document.createElement("div");
+    child.classList = "child";
+    child.id = id;
+    return child;
+  }
+
+  function measureForced() {
+    t.step(() => {
+      // Ensure children are laid out; this forces a layout.
+      assert_equals(document.getElementById("0").offsetTop, 0, "0 forced");
+      assert_equals(document.getElementById("1").offsetTop, 40, "1 forced");
+      assert_equals(document.getElementById("2").offsetTop, 80, "2 forced");
+      // Parent should be 100 height, since its child is locked.
+      assert_equals(document.getElementById("parent").offsetTop, 8, "parent forced");
+      assert_equals(document.getElementById("spacer").offsetTop, 108, "spacer forced");
+    });
+  }
+
+  function forcedMeasureAfterCommit() {
+    t.step(() => {
+      // Ensure children are still laid out.
+      assert_equals(document.getElementById("0").offsetTop, 0, "0 in commit");
+      assert_equals(document.getElementById("1").offsetTop, 40, "1 in commit");
+      assert_equals(document.getElementById("2").offsetTop, 80, "2 in commit");
+      // Now the parent should encompass an unlocked container, so spacer is pushed down more.
+      assert_equals(document.getElementById("parent").offsetTop, 8, "parent in commit");
+      assert_equals(document.getElementById("spacer").offsetTop, 208, "spacer in commit");
+    });
+  }
+
+  function construct(container) {
+    container.id = "large";
+    container.appendChild(createChild("0"));
+    container.appendChild(createChild("1"));
+    container.appendChild(createChild("2"));
+  }
+
+  async function runTest() {
+    let container = document.getElementById("small");
+    await container.displayLock.acquire({ timeout: Infinity });
+
+    construct(container);
+    measureForced();
+
+    container.displayLock.commit();
+    forcedMeasureAfterCommit();
+    t.done();
+  }
+
+  window.onload = function() {
+    requestAnimationFrame(() => requestAnimationFrame(runTest));
+  };
+}, "Measure Forced Layout");
+</script>
+
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html
index f0e180ae..d639493 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/commit-while-disconnected.html
@@ -33,11 +33,11 @@
 
   container.displayLock.acquire({ timeout: Infinity }).then(() => {
     container.displayLock.commit().then(
-      () => { finishTest("FAIL"); },
       () => {
         document.body.appendChild(container);
         finishTest("PASS");
-      });
+      },
+      () => { finishTest("FAIL"); });
   });
 }
 
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire-fails-expected.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire-expected.html
similarity index 100%
rename from third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire-fails-expected.html
rename to third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire-expected.html
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire-fails.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire-fails.html
deleted file mode 100644
index 9efbad05..0000000
--- a/third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire-fails.html
+++ /dev/null
@@ -1,45 +0,0 @@
-<!doctype HTML>
-
-<!--
-Calls commit without first acquiring, which fails.
--->
-
-<style>
-#container {
-  contain: style layout;
-  width: 150px;
-  height: 150px;
-  background: lightblue;
-}
-#child {
-  width: 50px;
-  height: 50px;
-  background: lightgreen;
-}
-</style>
-
-<div id="log"></div>
-
-<script>
-// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
-if (window.testRunner)
-  window.testRunner.waitUntilDone();
-
-function finishTest(status_string) {
-  if (document.getElementById("log").innerHTML === "")
-    document.getElementById("log").innerHTML = status_string;
-  if (window.testRunner)
-    window.testRunner.notifyDone();
-}
-
-function runTest() {
-  let container = document.createElement("div");
-  container.id = "container";
-  container.displayLock.commit().then(
-      () => { finishTest("FAIL"); },
-      () => { finishTest("PASS"); });
-  document.body.appendChild(container);
-}
-
-window.onload = runTest;
-</script>
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire.html b/third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire.html
new file mode 100644
index 0000000..569daa65
--- /dev/null
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/commit-without-acquire.html
@@ -0,0 +1,45 @@
+<!doctype HTML>
+
+<!--
+Calls commit without first acquiring, which fails.
+-->
+
+<style>
+#container {
+  contain: style layout;
+  width: 150px;
+  height: 150px;
+  background: lightblue;
+}
+#child {
+  width: 50px;
+  height: 50px;
+  background: lightgreen;
+}
+</style>
+
+<div id="log"></div>
+
+<script>
+// TODO(vmpstr): In WPT this needs to be replaced with reftest-wait.
+if (window.testRunner)
+  window.testRunner.waitUntilDone();
+
+function finishTest(status_string) {
+  if (document.getElementById("log").innerHTML === "")
+    document.getElementById("log").innerHTML = status_string;
+  if (window.testRunner)
+    window.testRunner.notifyDone();
+}
+
+function runTest() {
+  let container = document.createElement("div");
+  container.id = "container";
+  container.displayLock.commit().then(
+      () => { finishTest("PASS"); },
+      () => { finishTest("FAIL"); });
+  document.body.appendChild(container);
+}
+
+window.onload = runTest;
+</script>
diff --git a/third_party/blink/web_tests/display-lock/lock-before-append/update-and-commit.html b/third_party/blink/web_tests/display-lock/lock-before-append/update-and-commit.html
index 0ea2539..4740a11 100644
--- a/third_party/blink/web_tests/display-lock/lock-before-append/update-and-commit.html
+++ b/third_party/blink/web_tests/display-lock/lock-before-append/update-and-commit.html
@@ -31,8 +31,8 @@
   let container = document.createElement("div");
   container.id = "container";
   container.displayLock.updateAndCommit().then(
-    () => { finishTest("FAIL"); },
-    () => { finishTest("PASS"); });
+    () => { finishTest("PASS"); },
+    () => { finishTest("FAIL"); });
 }
 
 window.onload = runTest;
diff --git a/third_party/blink/web_tests/editing/execCommand/6355786.html b/third_party/blink/web_tests/editing/execCommand/6355786.html
deleted file mode 100644
index 3592439..0000000
--- a/third_party/blink/web_tests/editing/execCommand/6355786.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<body>
-<div>This tests find with queries that contain line breaks, for text broken into paragraphs using brs, divs, and preserved newlines.</div>
-<div id="find">test<br>over<br>brs<br><div>test</div><div>over</div><div>divs</div><pre>test
-over
-newlines</pre>
-<script>
-if (window.testRunner)
-    testRunner.dumpAsText();
-if (!document.execCommand("FindString", false, "test\nover\nbrs"))
-    document.body.innerText = "Find with line breaks failed over brs.";
-if (!document.execCommand("FindString", false, "test\nover\ndivs"))
-    document.body.innerText = "Find with line breaks failed over divs.";
-if (!document.execCommand("FindString", false, "test\nover\nnewlines"))
-    document.body.innerText = "Find with line breaks failed over newlines.";
-</script>
-</body>
diff --git a/third_party/blink/web_tests/editing/text-iterator/findString-expected.txt b/third_party/blink/web_tests/editing/text-iterator/findString-expected.txt
index 9e03e1e..39f39be 100644
--- a/third_party/blink/web_tests/editing/text-iterator/findString-expected.txt
+++ b/third_party/blink/web_tests/editing/text-iterator/findString-expected.txt
@@ -98,7 +98,6 @@
 PASS: Got no match as expected.
 
 Searching for ‘lowo’ in ‘hello<img src='../resources/abe.png'>world’ with options []:
-PASS: Got a match at 3,2 as expected.
 PASS: Got no match as expected.
 
 
diff --git a/third_party/blink/web_tests/editing/text-iterator/findString.html b/third_party/blink/web_tests/editing/text-iterator/findString.html
index 77fc6563..2f1aa2d7 100644
--- a/third_party/blink/web_tests/editing/text-iterator/findString.html
+++ b/third_party/blink/web_tests/editing/text-iterator/findString.html
@@ -85,7 +85,7 @@
     testFindString("Use an @import rule", "@", [], [[7, 8], []]);
     testFindString("If ((x + 5) * 2) = 14, then x = 2", "(x", [], [[4, 6], []]);
 
-    testFindString("hello<img src='../resources/abe.png'>world", "lowo", [], [[3, 2], []]);
+    testFindString("hello<img src='../resources/abe.png'>world", "lowo", [], [[]]);
 
     document.getElementById("console").style.removeProperty("visibility");
 </script>
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
index 661eae1e..df99dcd 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_5.json
@@ -5167,6 +5167,12 @@
      {}
     ]
    ],
+   "mathml/relations/html5-tree/href-manual.html": [
+    [
+     "/mathml/relations/html5-tree/href-manual.html",
+     {}
+    ]
+   ],
    "mediacapture-depth/dictionary-manual.https.html": [
     [
      "/mediacapture-depth/dictionary-manual.https.html",
@@ -54283,6 +54289,18 @@
      {}
     ]
    ],
+   "css/css-multicol/multicol-span-all-008.html": [
+    [
+     "/css/css-multicol/multicol-span-all-008.html",
+     [
+      [
+       "/css/css-multicol/multicol-span-all-008-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-multicol/multicol-span-all-009.html": [
     [
      "/css/css-multicol/multicol-span-all-009.html",
@@ -54439,6 +54457,30 @@
      {}
     ]
    ],
+   "css/css-multicol/multicol-span-all-dynamic-add-011.html": [
+    [
+     "/css/css-multicol/multicol-span-all-dynamic-add-011.html",
+     [
+      [
+       "/css/css-multicol/multicol-span-all-dynamic-add-003-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-multicol/multicol-span-all-dynamic-add-012.html": [
+    [
+     "/css/css-multicol/multicol-span-all-dynamic-add-012.html",
+     [
+      [
+       "/css/css-multicol/multicol-span-all-dynamic-add-012-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-multicol/multicol-span-all-dynamic-remove-001.html": [
     [
      "/css/css-multicol/multicol-span-all-dynamic-remove-001.html",
@@ -59671,6 +59713,18 @@
      {}
     ]
    ],
+   "css/css-tables/subpixel-table-cell-height-001.html": [
+    [
+     "/css/css-tables/subpixel-table-cell-height-001.html",
+     [
+      [
+       "/css/css-tables/subpixel-table-cell-height-001-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-tables/subpixel-table-cell-width-001.html": [
     [
      "/css/css-tables/subpixel-table-cell-width-001.html",
@@ -79951,6 +80005,18 @@
      {}
     ]
    ],
+   "css/css-ui/outline-021.html": [
+    [
+     "/css/css-ui/outline-021.html",
+     [
+      [
+       "/css/css-ui/reference/outline-021-notref.html",
+       "!="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-ui/outline-color-001.html": [
     [
      "/css/css-ui/outline-color-001.html",
@@ -80359,6 +80425,66 @@
      {}
     ]
    ],
+   "css/css-values/angle-units-001.html": [
+    [
+     "/css/css-values/angle-units-001.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-values/angle-units-002.html": [
+    [
+     "/css/css-values/angle-units-002.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-values/angle-units-003.html": [
+    [
+     "/css/css-values/angle-units-003.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-values/angle-units-004.html": [
+    [
+     "/css/css-values/angle-units-004.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-values/angle-units-005.html": [
+    [
+     "/css/css-values/angle-units-005.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-values/attr-color-invalid-cast.html": [
     [
      "/css/css-values/attr-color-invalid-cast.html",
@@ -80419,6 +80545,18 @@
      {}
     ]
    ],
+   "css/css-values/attr-invalid-type-008.html": [
+    [
+     "/css/css-values/attr-invalid-type-008.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-values/attr-length-invalid-cast.html": [
     [
      "/css/css-values/attr-length-invalid-cast.html",
@@ -80599,6 +80737,18 @@
      {}
     ]
    ],
+   "css/css-values/calc-positive-fraction-001.html": [
+    [
+     "/css/css-values/calc-positive-fraction-001.html",
+     [
+      [
+       "/css/reference/ref-filled-green-100px-square.xht",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-values/calc-rem-lang.html": [
     [
      "/css/css-values/calc-rem-lang.html",
@@ -80659,6 +80809,66 @@
      {}
     ]
    ],
+   "css/css-values/ch-unit-008.html": [
+    [
+     "/css/css-values/ch-unit-008.html",
+     [
+      [
+       "/css/css-values/reference/ch-unit-008-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-values/ch-unit-009.html": [
+    [
+     "/css/css-values/ch-unit-009.html",
+     [
+      [
+       "/css/css-values/reference/ch-unit-009-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-values/ch-unit-010.html": [
+    [
+     "/css/css-values/ch-unit-010.html",
+     [
+      [
+       "/css/css-values/reference/ch-unit-009-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-values/ch-unit-011.html": [
+    [
+     "/css/css-values/ch-unit-011.html",
+     [
+      [
+       "/css/css-values/reference/ch-unit-011-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-values/ch-unit-012.html": [
+    [
+     "/css/css-values/ch-unit-012.html",
+     [
+      [
+       "/css/css-values/reference/ch-unit-009-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-values/ex-calc-expression-001.html": [
     [
      "/css/css-values/ex-calc-expression-001.html",
@@ -106859,6 +107069,426 @@
      {}
     ]
    ],
+   "mathml/presentation-markup/spaces/space-2.html": [
+    [
+     "/mathml/presentation-markup/spaces/space-2.html",
+     [
+      [
+       "/mathml/presentation-markup/spaces/space-2-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/color-1.html": [
+    [
+     "/mathml/relations/css-styling/color-1.html",
+     [
+      [
+       "/mathml/relations/css-styling/color-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/display-1.html": [
+    [
+     "/mathml/relations/css-styling/display-1.html",
+     [
+      [
+       "/mathml/relations/css-styling/display-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/lengths-1.html": [
+    [
+     "/mathml/relations/css-styling/lengths-1.html",
+     [
+      [
+       "/mathml/relations/css-styling/lengths-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/lengths-2.html": [
+    [
+     "/mathml/relations/css-styling/lengths-2.html",
+     [
+      [
+       "/mathml/relations/css-styling/lengths-2-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold-fraktur.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-bold-fraktur.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-bold-fraktur-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold-italic.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-bold-italic.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-bold-italic-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold-sans-serif.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-bold-sans-serif.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-bold-sans-serif-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold-script.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-bold-script.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-bold-script-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-bold.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-bold-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-double-struck.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-double-struck.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-double-struck-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-fraktur.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-fraktur.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-fraktur-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-initial.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-initial.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-initial-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-italic.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-italic.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-italic-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-looped.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-looped.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-looped-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-monospace.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-monospace.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-monospace-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-sans-serif-italic.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-sans-serif-italic.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-sans-serif-italic-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-sans-serif.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-sans-serif.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-sans-serif-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-script.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-script.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-script-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-stretched.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-stretched.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-stretched-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-tailed.html": [
+    [
+     "/mathml/relations/css-styling/mathvariant-tailed.html",
+     [
+      [
+       "/mathml/relations/css-styling/mathvariant-tailed-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/visibility-1.html": [
+    [
+     "/mathml/relations/css-styling/visibility-1.html",
+     [
+      [
+       "/mathml/relations/css-styling/visibility-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/class-1.html": [
+    [
+     "/mathml/relations/html5-tree/class-1.html",
+     [
+      [
+       "/mathml/relations/html5-tree/class-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/color-attributes-1.html": [
+    [
+     "/mathml/relations/html5-tree/color-attributes-1.html",
+     [
+      [
+       "/mathml/relations/html5-tree/color-attributes-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/dynamic-1.html": [
+    [
+     "/mathml/relations/html5-tree/dynamic-1.html",
+     [
+      [
+       "/mathml/relations/html5-tree/dynamic-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/href-click-1.html": [
+    [
+     "/mathml/relations/html5-tree/href-click-1.html",
+     [
+      [
+       "/mathml/relations/html5-tree/href-click-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/href-click-2.html": [
+    [
+     "/mathml/relations/html5-tree/href-click-2.html",
+     [
+      [
+       "/mathml/relations/html5-tree/href-click-2-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/integration-point-1.html": [
+    [
+     "/mathml/relations/html5-tree/integration-point-1.html",
+     [
+      [
+       "/mathml/relations/html5-tree/integration-point-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/integration-point-2.html": [
+    [
+     "/mathml/relations/html5-tree/integration-point-2.html",
+     [
+      [
+       "/mathml/relations/html5-tree/integration-point-2-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/integration-point-3.html": [
+    [
+     "/mathml/relations/html5-tree/integration-point-3.html",
+     [
+      [
+       "/mathml/relations/html5-tree/integration-point-3-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/required-extensions-2.html": [
+    [
+     "/mathml/relations/html5-tree/required-extensions-2.html",
+     [
+      [
+       "/mathml/relations/html5-tree/required-extensions-2-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/unique-identifier-1.html": [
+    [
+     "/mathml/relations/html5-tree/unique-identifier-1.html",
+     [
+      [
+       "/mathml/relations/html5-tree/unique-identifier-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/unique-identifier-3.html": [
+    [
+     "/mathml/relations/html5-tree/unique-identifier-3.html",
+     [
+      [
+       "/mathml/relations/html5-tree/unique-identifier-3-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "mathml/relations/text-and-math/use-typo-metrics-1.html": [
+    [
+     "/mathml/relations/text-and-math/use-typo-metrics-1.html",
+     [
+      [
+       "/mathml/relations/text-and-math/use-typo-metrics-1-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "portals/portals-rendering.html": [
     [
      "/portals/portals-rendering.html",
@@ -139381,6 +140011,11 @@
      {}
     ]
    ],
+   "css/css-multicol/multicol-span-all-008-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-multicol/multicol-span-all-009-ref.html": [
     [
      {}
@@ -139431,6 +140066,11 @@
      {}
     ]
    ],
+   "css/css-multicol/multicol-span-all-dynamic-add-012-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-multicol/multicol-span-all-dynamic-remove-001-ref.html": [
     [
      {}
@@ -141591,6 +142231,11 @@
      {}
     ]
    ],
+   "css/css-syntax/urange-parsing-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-tables/META.yml": [
     [
      {}
@@ -141711,6 +142356,11 @@
      {}
     ]
    ],
+   "css/css-tables/subpixel-table-cell-height-001-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-tables/subpixel-table-cell-width-001-ref.html": [
     [
      {}
@@ -145236,11 +145886,6 @@
      {}
     ]
    ],
-   "css/css-transforms/transform-2d-getComputedStyle-001-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "css/css-transforms/transform-abspos-ref.html": [
     [
      {}
@@ -146071,6 +146716,11 @@
      {}
     ]
    ],
+   "css/css-transitions/event-dispatch.tentative-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "css/css-transitions/idlharness-expected.txt": [
     [
      {}
@@ -146881,6 +147531,11 @@
      {}
     ]
    ],
+   "css/css-ui/reference/outline-021-notref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-ui/reference/outline-offset.html": [
     [
      {}
@@ -148236,6 +148891,21 @@
      {}
     ]
    ],
+   "css/css-values/reference/ch-unit-008-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "css/css-values/reference/ch-unit-009-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "css/css-values/reference/ch-unit-011-ref.html": [
+    [
+     {}
+    ]
+   ],
    "css/css-values/reference/ic-unit-001-ref.html": [
     [
      {}
@@ -160766,6 +161436,346 @@
      {}
     ]
    ],
+   "fonts/math/axisheight5000-verticalarrow14000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-axisheight7000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-denominatorgapmin4000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-denominatorshiftdown3000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-numeratorgapmin9000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-numeratorshiftup11000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/fraction-rulethickness10000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/largeop-displayoperatorminheight2000-2AFF-italiccorrection3000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/largeop-displayoperatorminheight5000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/limits-lowerlimitbaselinedropmin3000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/limits-lowerlimitgapmin11000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/limits-upperlimitbaselinerisemin5000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/limits-upperlimitgapmin7000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/lineheight5000-typolineheight2300.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-bold-fraktur.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-bold-italic.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-bold-sans-serif.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-bold-script.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-bold.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-double-struck.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-fraktur.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-initial.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-italic.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-looped.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-monospace.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-sans-serif-bold-italic.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-sans-serif-italic.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-sans-serif.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-script.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-stretched.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/mathvariant-tailed.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/radical-degreebottomraisepercent25-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/radical-displaystyleverticalgap7000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/radical-extraascender3000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/radical-kernafterdegreeminus5000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/radical-kernbeforedegree4000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/radical-rulethickness8000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/radical-verticalgap6000-rulethickness1000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-spaceafterscript3000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-subscriptbaselinedropmin9000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-subscriptshiftdown6000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-subscripttopmax4000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-subsuperscriptgapmin11000-superscriptbottommaxwithsubscript3000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-subsuperscriptgapmin11000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-superscriptbaselinedropmax10000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-superscriptbottommin8000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-superscriptshiftup7000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/scripts-superscriptshiftupcramped5000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stack-axisheight7000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stack-bottomdisplaystyleshiftdown5000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stack-bottomshiftdown6000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stack-displaystylegapmin4000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stack-gapmin8000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stack-topdisplaystyleshiftup3000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stack-topshiftup9000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stretchstack-bottomshiftdown3000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stretchstack-gapabovemin7000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stretchstack-gapbelowmin11000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/stretchstack-topshiftup5000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/underover-accentbaseheight4000-overbarextraascender3000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/underover-accentbaseheight4000-overbarverticalgap11000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/underover-accentbaseheight4000-underbarextradescender5000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/underover-accentbaseheight4000-underbarverticalgap7000.woff": [
+    [
+     {}
+    ]
+   ],
+   "fonts/math/xheight500.woff": [
+    [
+     {}
+    ]
+   ],
    "fonts/mplus-1p-regular.woff": [
     [
      {}
@@ -160811,6 +161821,16 @@
      {}
     ]
    ],
+   "fonts/noto/NotoSansMongolian-regular.woff2": [
+    [
+     {}
+    ]
+   ],
+   "fonts/noto/NotoSansNko-regular-webfont.woff2": [
+    [
+     {}
+    ]
+   ],
    "fonts/sileot-webfont.woff": [
     [
      {}
@@ -173001,11 +174021,6 @@
      {}
     ]
    ],
-   "infrastructure/metadata/infrastructure/testdriver/actions/elementTiming.html.ini": [
-    [
-     {}
-    ]
-   ],
    "infrastructure/metadata/infrastructure/testdriver/actions/eventOrder.html.ini": [
     [
      {}
@@ -174081,6 +175096,276 @@
      {}
     ]
    ],
+   "mathml/META.yml": [
+    [
+     {}
+    ]
+   ],
+   "mathml/README.md": [
+    [
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/spaces/space-2-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/color-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/display-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/lengths-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/lengths-2-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold-fraktur-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold-italic-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold-sans-serif-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-bold-script-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-double-struck-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-fraktur-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-initial-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-italic-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-looped-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-monospace-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-sans-serif-bold-italic-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-sans-serif-italic-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-sans-serif-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-script-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-stretched-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/mathvariant-tailed-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/visibility-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/class-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/color-attributes-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/dynamic-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/href-click-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/href-click-2-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/integration-point-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/integration-point-2-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/integration-point-3-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/required-extensions-2-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/unique-identifier-1-iframe-1.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/unique-identifier-1-iframe-2.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/unique-identifier-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/unique-identifier-3-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/relations/text-and-math/use-typo-metrics-1-ref.html": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/axisheight.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/fractions.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/largeop.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/limits.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/mathvariant-transforms.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/radicals.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/scripts.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/stacks.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/stretchstacks.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/underover.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/use-typo-lineheight.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/utils/__init__.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/utils/mathfont.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/utils/misc.py": [
+    [
+     {}
+    ]
+   ],
+   "mathml/tools/xHeight.py": [
+    [
+     {}
+    ]
+   ],
    "media-capabilities/META.yml": [
     [
      {}
@@ -176891,21 +178176,41 @@
      {}
     ]
    ],
-   "portals/resources/portal-activate-event-window.html": [
-    [
-     {}
-    ]
-   ],
    "portals/resources/portal-cross-origin.sub.html": [
     [
      {}
     ]
    ],
+   "portals/resources/portal-embed-and-activate.html": [
+    [
+     {}
+    ]
+   ],
    "portals/resources/portal-forward-with-broadcast.html": [
     [
      {}
     ]
    ],
+   "portals/resources/portal-host-cross-origin-navigate.sub.html": [
+    [
+     {}
+    ]
+   ],
+   "portals/resources/portal-host-cross-origin.sub.html": [
+    [
+     {}
+    ]
+   ],
+   "portals/resources/portal-host-hidden-after-activation-portal.html": [
+    [
+     {}
+    ]
+   ],
+   "portals/resources/portal-host.html": [
+    [
+     {}
+    ]
+   ],
    "portals/resources/portals-rendering-portal.html": [
     [
      {}
@@ -184671,26 +185976,6 @@
      {}
     ]
    ],
-   "streams/writable-streams/aborting.any-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "streams/writable-streams/aborting.any.serviceworker-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "streams/writable-streams/aborting.any.sharedworker-expected.txt": [
-    [
-     {}
-    ]
-   ],
-   "streams/writable-streams/aborting.any.worker-expected.txt": [
-    [
-     {}
-    ]
-   ],
    "streams/writable-streams/properties.any-expected.txt": [
     [
      {}
@@ -187261,21 +188546,6 @@
      {}
     ]
    ],
-   "webaudio/the-audio-api/the-analysernode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-audiobuffer-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-audiobuffersourcenode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
    "webaudio/the-audio-api/the-audiobuffersourcenode-interface/resources/audiobuffersource-multi-channels-expected.wav": [
     [
      {}
@@ -187286,31 +188556,6 @@
      {}
     ]
    ],
-   "webaudio/the-audio-api/the-audiocontext-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-audiodestinationnode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-audiolistener-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-audionode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-audioparam-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
    "webaudio/the-audio-api/the-audioparam-interface/automation-rate-testing.js": [
     [
      {}
@@ -187321,11 +188566,6 @@
      {}
     ]
    ],
-   "webaudio/the-audio-api/the-audioprocessingevent-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
    "webaudio/the-audio-api/the-audioworklet-interface/processors/channel-count-processor.js": [
     [
      {}
@@ -187386,91 +188626,11 @@
      {}
     ]
    ],
-   "webaudio/the-audio-api/the-biquadfilternode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-channelmergernode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-channelsplitternode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-convolvernode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-delaynode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
    "webaudio/the-audio-api/the-delaynode-interface/delaynode-channel-count-1-expected.txt": [
     [
      {}
     ]
    ],
-   "webaudio/the-audio-api/the-dynamicscompressornode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-gainnode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-mediastreamaudiodestinationnode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-offlineaudiocontext-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-oscillatornode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-pannernode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-periodicwave-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-scriptprocessornode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
-   "webaudio/the-audio-api/the-waveshapernode-interface/.gitkeep": [
-    [
-     {}
-    ]
-   ],
    "webauthn/META.yml": [
     [
      {}
@@ -188496,6 +189656,11 @@
      {}
     ]
    ],
+   "webstorage/symbol-props.window-expected.txt": [
+    [
+     {}
+    ]
+   ],
    "webusb/META.yml": [
     [
      {}
@@ -201195,6 +202360,12 @@
      {}
     ]
    ],
+   "animation-worklet/playback-rate.https.html": [
+    [
+     "/animation-worklet/playback-rate.https.html",
+     {}
+    ]
+   ],
    "animation-worklet/scroll-timeline-writing-modes.https.html": [
     [
      "/animation-worklet/scroll-timeline-writing-modes.https.html",
@@ -208055,6 +209226,12 @@
      {}
     ]
    ],
+   "css/css-flexbox/flex-minimum-height-flex-items-012.html": [
+    [
+     "/css/css-flexbox/flex-minimum-height-flex-items-012.html",
+     {}
+    ]
+   ],
    "css/css-flexbox/flexbox_first-letter.html": [
     [
      "/css/css-flexbox/flexbox_first-letter.html",
@@ -210505,6 +211682,12 @@
      {}
     ]
    ],
+   "css/css-grid/grid-model/grid-button-001.html": [
+    [
+     "/css/css-grid/grid-model/grid-button-001.html",
+     {}
+    ]
+   ],
    "css/css-grid/grid-model/grid-computed-value-display-floated-items-001.html": [
     [
      "/css/css-grid/grid-model/grid-computed-value-display-floated-items-001.html",
@@ -210589,6 +211772,12 @@
      {}
     ]
    ],
+   "css/css-grid/layout-algorithm/grid-fit-content-percentage.html": [
+    [
+     "/css/css-grid/layout-algorithm/grid-fit-content-percentage.html",
+     {}
+    ]
+   ],
    "css/css-grid/layout-algorithm/grid-intrinsic-size-with-orthogonal-items.html": [
     [
      "/css/css-grid/layout-algorithm/grid-intrinsic-size-with-orthogonal-items.html",
@@ -211651,6 +212840,12 @@
      {}
     ]
    ],
+   "css/css-position/position-absolute-crash-chrome-001.html": [
+    [
+     "/css/css-position/position-absolute-crash-chrome-001.html",
+     {}
+    ]
+   ],
    "css/css-position/position-absolute-in-inline-crash.html": [
     [
      "/css/css-position/position-absolute-in-inline-crash.html",
@@ -213283,6 +214478,12 @@
      {}
     ]
    ],
+   "css/css-syntax/urange-parsing.html": [
+    [
+     "/css/css-syntax/urange-parsing.html",
+     {}
+    ]
+   ],
    "css/css-syntax/whitespace.html": [
     [
      "/css/css-syntax/whitespace.html",
@@ -241985,6 +243186,12 @@
      {}
     ]
    ],
+   "html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html": [
+    [
+     "/html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html",
+     {}
+    ]
+   ],
    "html/semantics/embedded-content/the-img-element/update-media.html": [
     [
      "/html/semantics/embedded-content/the-img-element/update-media.html",
@@ -249385,6 +250592,156 @@
      {}
     ]
    ],
+   "mathml/presentation-markup/fractions/frac-1.html": [
+    [
+     "/mathml/presentation-markup/fractions/frac-1.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/fractions/frac-parameters-1.html": [
+    [
+     "/mathml/presentation-markup/fractions/frac-parameters-1.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/fractions/frac-parameters-2.html": [
+    [
+     "/mathml/presentation-markup/fractions/frac-parameters-2.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/operators/mo-axis-height-1.html": [
+    [
+     "/mathml/presentation-markup/operators/mo-axis-height-1.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/radicals/root-parameters-1.html": [
+    [
+     "/mathml/presentation-markup/radicals/root-parameters-1.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/subsup-1.html": [
+    [
+     "/mathml/presentation-markup/scripts/subsup-1.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/subsup-2.html": [
+    [
+     "/mathml/presentation-markup/scripts/subsup-2.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/subsup-3.html": [
+    [
+     "/mathml/presentation-markup/scripts/subsup-3.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/subsup-4.html": [
+    [
+     "/mathml/presentation-markup/scripts/subsup-4.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/subsup-5.html": [
+    [
+     "/mathml/presentation-markup/scripts/subsup-5.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/subsup-parameters-1.html": [
+    [
+     "/mathml/presentation-markup/scripts/subsup-parameters-1.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/subsup-parameters-2.html": [
+    [
+     "/mathml/presentation-markup/scripts/subsup-parameters-2.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/underover-1.html": [
+    [
+     "/mathml/presentation-markup/scripts/underover-1.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/underover-parameters-1.html": [
+    [
+     "/mathml/presentation-markup/scripts/underover-parameters-1.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/underover-parameters-2.html": [
+    [
+     "/mathml/presentation-markup/scripts/underover-parameters-2.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/underover-parameters-3.html": [
+    [
+     "/mathml/presentation-markup/scripts/underover-parameters-3.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/scripts/underover-parameters-4.html": [
+    [
+     "/mathml/presentation-markup/scripts/underover-parameters-4.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/spaces/space-1.html": [
+    [
+     "/mathml/presentation-markup/spaces/space-1.html",
+     {}
+    ]
+   ],
+   "mathml/presentation-markup/tables/table-axis-height.html": [
+    [
+     "/mathml/presentation-markup/tables/table-axis-height.html",
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/displaystyle-1.html": [
+    [
+     "/mathml/relations/css-styling/displaystyle-1.html",
+     {}
+    ]
+   ],
+   "mathml/relations/css-styling/lengths-3.html": [
+    [
+     "/mathml/relations/css-styling/lengths-3.html",
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/class-2.html": [
+    [
+     "/mathml/relations/html5-tree/class-2.html",
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/display-1.html": [
+    [
+     "/mathml/relations/html5-tree/display-1.html",
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/required-extensions-1.html": [
+    [
+     "/mathml/relations/html5-tree/required-extensions-1.html",
+     {}
+    ]
+   ],
+   "mathml/relations/html5-tree/unique-identifier-2.html": [
+    [
+     "/mathml/relations/html5-tree/unique-identifier-2.html",
+     {}
+    ]
+   ],
    "media-capabilities/decodingInfo.html": [
     [
      "/media-capabilities/decodingInfo.html",
@@ -263951,6 +265308,18 @@
      {}
     ]
    ],
+   "portals/portals-host-exposure.sub.html": [
+    [
+     "/portals/portals-host-exposure.sub.html",
+     {}
+    ]
+   ],
+   "portals/portals-host-hidden-after-activation.html": [
+    [
+     "/portals/portals-host-hidden-after-activation.html",
+     {}
+    ]
+   ],
    "portals/portals-host-null.html": [
     [
      "/portals/portals-host-null.html",
@@ -284677,6 +286046,12 @@
      {}
     ]
    ],
+   "webrtc/RTCError.html": [
+    [
+     "/webrtc/RTCError.html",
+     {}
+    ]
+   ],
    "webrtc/RTCIceCandidate-constructor.html": [
     [
      "/webrtc/RTCIceCandidate-constructor.html",
@@ -287523,6 +288898,12 @@
      {}
     ]
    ],
+   "webstorage/symbol-props.window.js": [
+    [
+     "/webstorage/symbol-props.window.html",
+     {}
+    ]
+   ],
    "webusb/idlharness.https.any.js": [
     [
      "/webusb/idlharness.https.any.html",
@@ -305852,6 +307233,10 @@
    "f73208c2a3c700d03ea1d39322a3e061b5caa5f7",
    "support"
   ],
+  "animation-worklet/playback-rate.https.html": [
+   "9c975814f1ed09b3e78493df177c3c0eddf74cdd",
+   "testharness"
+  ],
   "animation-worklet/resources/animator-iframe.html": [
    "e30cc281fcdefd8d029e7bf0ea92a1a9cd7af7e7",
    "support"
@@ -305861,7 +307246,7 @@
    "testharness"
   ],
   "animation-worklet/worklet-animation-with-fill-mode.https.html": [
-   "49fead8bd39c56c6335c0748b01be0181767bbec",
+   "b910ab280ec80725eee7894e8331cf72f6c24492",
    "testharness"
   ],
   "animation-worklet/worklet-animation-with-invalid-effect.https.html": [
@@ -307113,19 +308498,19 @@
    "support"
   ],
   "client-hints/echo_client_hints_received.py": [
-   "8f2ccaa2884f0382bdc4ef48b82b9c18b6093171",
+   "f7debdb7b0b7073ec1878ffc17a9816a61f5f4c3",
    "support"
   ],
   "client-hints/http_equiv_accept_ch.tentative.http.html": [
-   "2bdced2ece762d71a13f2322325ef225fe2d489b",
+   "03c5799908b971ec6e6057fb8e5325e1bc2e203d",
    "testharness"
   ],
   "client-hints/http_equiv_accept_ch.tentative.https.html": [
-   "3e4d638ffc320e5e460b3923881c2e002eb26baa",
+   "74eea344e90879e24568a76ba8e1197f6e9cede5",
    "testharness"
   ],
   "client-hints/http_equiv_accept_ch.tentative.sub.https.html": [
-   "459b00e973bc1c69b3010146171683af12e33eb4",
+   "16617dccaf8c9c99011849b48d93e41f35ca374a",
    "testharness"
   ],
   "client-hints/http_equiv_accept_ch_lifetime.tentative.https.html": [
@@ -335172,6 +336557,10 @@
    "19ba83af4049057b468aa1444d59c28d414e2f43",
    "reftest"
   ],
+  "css/css-flexbox/flex-minimum-height-flex-items-012.html": [
+   "a193c29cb6001bfc1bc7dc2c7258a48ae9da17a6",
+   "testharness"
+  ],
   "css/css-flexbox/flex-minimum-width-flex-items-001.xht": [
    "b8e2866edaa46af46900c287238894cd8ddef24c",
    "reftest"
@@ -347664,6 +349053,10 @@
    "b99029293466ddfb514f401931161d75cbd5917c",
    "testharness"
   ],
+  "css/css-grid/grid-model/grid-button-001.html": [
+   "b84c10fcab005145f330c507b9cafcbf288640e2",
+   "testharness"
+  ],
   "css/css-grid/grid-model/grid-computed-value-display-floated-items-001.html": [
    "17c0fbd1b1860e00c8969ce450b487d7dce7c877",
    "testharness"
@@ -347860,6 +349253,10 @@
    "340fe82cfd28dcb80051f432c94e4eb215aff474",
    "testharness"
   ],
+  "css/css-grid/layout-algorithm/grid-fit-content-percentage.html": [
+   "ab55502487328d11ac153440fec4963e6e70709c",
+   "testharness"
+  ],
   "css/css-grid/layout-algorithm/grid-intrinsic-size-with-orthogonal-items.html": [
    "9fb0f4ffa1c0a06d891c61e1fc3f9588db47760e",
    "testharness"
@@ -351292,6 +352689,14 @@
    "2464327ff4238fb167bb8fc76b7f3d01fea1d377",
    "reftest"
   ],
+  "css/css-multicol/multicol-span-all-008-ref.html": [
+   "ddc0b40bdd3b5e51c8808c1f7593ecd51f2d4e2a",
+   "support"
+  ],
+  "css/css-multicol/multicol-span-all-008.html": [
+   "82a24832b7c74d86a13c01e620cc059b98603f8e",
+   "reftest"
+  ],
   "css/css-multicol/multicol-span-all-009-ref.html": [
    "b323e423e0ce6076d166e59349a373715dc91de3",
    "support"
@@ -351384,6 +352789,18 @@
    "e9e96ced2aa540fe716d33cfd33a926be5ce9e91",
    "reftest"
   ],
+  "css/css-multicol/multicol-span-all-dynamic-add-011.html": [
+   "058ca6c3bab02812ede51042898e935a6a948f22",
+   "reftest"
+  ],
+  "css/css-multicol/multicol-span-all-dynamic-add-012-ref.html": [
+   "61b45cc650720435e3c610f33f653b280e86fda3",
+   "support"
+  ],
+  "css/css-multicol/multicol-span-all-dynamic-add-012.html": [
+   "7e152af15eb37b98714138b0ae2003453ca3780b",
+   "reftest"
+  ],
   "css/css-multicol/multicol-span-all-dynamic-remove-001-ref.html": [
    "83c9fd4880f246e77a5f93dcc2755f4f2d812801",
    "support"
@@ -352928,6 +354345,10 @@
    "2b158a86f6599e43f6a2315a2943b4d394405ba5",
    "testharness"
   ],
+  "css/css-position/position-absolute-crash-chrome-001.html": [
+   "592e5d22e70f1b0c5e3e4b9222cbd1ccef99bdef",
+   "testharness"
+  ],
   "css/css-position/position-absolute-in-inline-crash.html": [
    "5d36710b6fe694b256d9841b3e7a0fff4535c85b",
    "testharness"
@@ -356220,6 +357641,14 @@
    "db09540bb08c47f62b4255be6ba72b289987d64c",
    "testharness"
   ],
+  "css/css-syntax/urange-parsing-expected.txt": [
+   "96bfb2fd189de691fdb3ae203e76f2677214447e",
+   "support"
+  ],
+  "css/css-syntax/urange-parsing.html": [
+   "0a69faa39c1d661e17084fe63d86c826cfa58848",
+   "testharness"
+  ],
   "css/css-syntax/whitespace.html": [
    "bc7aa7ebda518a42a32a154c3ed13f00be86429a",
    "testharness"
@@ -356560,6 +357989,14 @@
    "a24556aa0dedfab35ff7792f03b804f44c0f4160",
    "reftest"
   ],
+  "css/css-tables/subpixel-table-cell-height-001-ref.html": [
+   "3b6297fc4ab06643b2910194479b571ca3a462f2",
+   "support"
+  ],
+  "css/css-tables/subpixel-table-cell-height-001.html": [
+   "581efa02b9183d94224bb84b5df4db8ee152e2a4",
+   "reftest"
+  ],
   "css/css-tables/subpixel-table-cell-width-001-ref.html": [
    "ea2f38b6bb216d25a8d5656fdd8d6bcac40982db",
    "support"
@@ -364976,12 +366413,8 @@
    "e98b3e73b36a323a60ad5ef74e090ff193ad41b9",
    "reftest"
   ],
-  "css/css-transforms/transform-2d-getComputedStyle-001-expected.txt": [
-   "ecab08ef439acb7e9e398eae9cccfecbf0eaba39",
-   "support"
-  ],
   "css/css-transforms/transform-2d-getComputedStyle-001.html": [
-   "a085b794fc4dbd01abd8cf779b691b541d0bf8e3",
+   "8fc4f9380fe4739334032975da15f626964277d3",
    "testharness"
   ],
   "css/css-transforms/transform-3d-rotateY-stair-above-001.xht": [
@@ -367964,8 +369397,12 @@
    "545324b0bd74d7ca398581666babf3ec04798b88",
    "testharness"
   ],
+  "css/css-transitions/event-dispatch.tentative-expected.txt": [
+   "ca185ceb39ba12d935805354558dd3e7b55446c8",
+   "support"
+  ],
   "css/css-transitions/event-dispatch.tentative.html": [
-   "81bba60a66e7eb9800113bcd04dcc47a15002fb0",
+   "cd5b6ab30b17f21098a225d61c251e657732bb44",
    "testharness"
   ],
   "css/css-transitions/events-001.html": [
@@ -371340,6 +372777,10 @@
    "457f10ad65772a6c7737f55222e5bc70b34461ae",
    "reftest"
   ],
+  "css/css-ui/outline-021.html": [
+   "02e96ea9a67de331cdaf8962155ec4c0e02217b5",
+   "reftest"
+  ],
   "css/css-ui/outline-color-001.html": [
    "4245b2ceb7393a73850a223e498987e0e1bc4aa3",
    "reftest"
@@ -371548,6 +372989,10 @@
    "194b6336918939bf5978395b73fdab529705435e",
    "support"
   ],
+  "css/css-ui/reference/outline-021-notref.html": [
+   "eb074ed44f8332ae6ef08db47236e1bd14e9c5fb",
+   "support"
+  ],
   "css/css-ui/reference/outline-offset.html": [
    "63f852ca466249417a93341762069c999b4e6905",
    "support"
@@ -372812,6 +374257,26 @@
    "b0e5178bce038cea243089db95f92c1d831c6914",
    "visual"
   ],
+  "css/css-values/angle-units-001.html": [
+   "61ca931905a06f3959d80a274b30a5de0758910d",
+   "reftest"
+  ],
+  "css/css-values/angle-units-002.html": [
+   "ff9cdca17c8914fe0bb4ac8b0f1d52439c475a4b",
+   "reftest"
+  ],
+  "css/css-values/angle-units-003.html": [
+   "786d5faed5370ff8d654317b68f51b91ce0d1783",
+   "reftest"
+  ],
+  "css/css-values/angle-units-004.html": [
+   "34722a3e00018f2ecedc1bc8c8e6340234cfc81e",
+   "reftest"
+  ],
+  "css/css-values/angle-units-005.html": [
+   "625952abb9b3a0abd390fecc850940952eea022e",
+   "reftest"
+  ],
   "css/css-values/attr-color-invalid-cast.html": [
    "d538eebdd9979bfda2398e46de72748dc0099810",
    "reftest"
@@ -372832,6 +374297,10 @@
    "dbdac8a4ba1cf15e583684c9c2b3ac158c0af614",
    "reftest"
   ],
+  "css/css-values/attr-invalid-type-008.html": [
+   "a76be44d76a1c1f03d818b8b11a3ee5830815e38",
+   "reftest"
+  ],
   "css/css-values/attr-length-invalid-cast.html": [
    "930832363605fa85651b075f17d6c109535c0b41",
    "reftest"
@@ -372917,17 +374386,21 @@
    "testharness"
   ],
   "css/css-values/calc-numbers-expected.txt": [
-   "e02b66126c0211872a1f954602d7ac187ab61499",
+   "d0e26353805094c25924c575580b20e1ce89cfa4",
    "support"
   ],
   "css/css-values/calc-numbers.html": [
-   "5c2c91d7a8c97c2dd5dda2d0736660b5134469a5",
+   "995595b8a3e91ab3a72ec465d509030f082ea5bf",
    "testharness"
   ],
   "css/css-values/calc-parenthesis-stack.html": [
    "1d9033d7eecd14066ee9e4f9c52bf1a39e6ddd1b",
    "reftest"
   ],
+  "css/css-values/calc-positive-fraction-001.html": [
+   "383b4af547b9cb50b37636e77b587cbb33d96916",
+   "reftest"
+  ],
   "css/css-values/calc-rem-lang-ref.html": [
    "a0f6add684a57f394f2dd97b163a3ca9fc61a1ef",
    "support"
@@ -372968,6 +374441,26 @@
    "914cec9f64985c930ab96de32693cc819a3c8229",
    "reftest"
   ],
+  "css/css-values/ch-unit-008.html": [
+   "67fe0754a9ed498235418dc5fb463a80e27568ea",
+   "reftest"
+  ],
+  "css/css-values/ch-unit-009.html": [
+   "c6f56718a0d09906c143bb413c88f43882581429",
+   "reftest"
+  ],
+  "css/css-values/ch-unit-010.html": [
+   "bc3fdbcb7cee5bfdd4b59e277d8eaaa622311bb4",
+   "reftest"
+  ],
+  "css/css-values/ch-unit-011.html": [
+   "c486f0c48ab8f43527247888a4e98ef029213965",
+   "reftest"
+  ],
+  "css/css-values/ch-unit-012.html": [
+   "b844d9b3fa45e9a340ab79da7175875c6187d99f",
+   "reftest"
+  ],
   "css/css-values/ex-calc-expression-001-ref.html": [
    "888a51ea9b6ac04fb065ee5d84a18be8fe765aca",
    "support"
@@ -373017,7 +374510,7 @@
    "reftest"
   ],
   "css/css-values/lh-rlh-on-root-001.html": [
-   "228da4e38975a40334b1bf5396489276442baaf1",
+   "123218ddd67fadb0ceab893f16e658cd08c56c9d",
    "testharness"
   ],
   "css/css-values/lh-unit-001.html": [
@@ -373056,6 +374549,18 @@
    "ee6ce1595d765849801743eb2d80c5cee2982fe5",
    "support"
   ],
+  "css/css-values/reference/ch-unit-008-ref.html": [
+   "678a9c1695408ba4c810a43d2578956da485715a",
+   "support"
+  ],
+  "css/css-values/reference/ch-unit-009-ref.html": [
+   "6bd69bed09a99ed449238718bd3e7e6ea80179c6",
+   "support"
+  ],
+  "css/css-values/reference/ch-unit-011-ref.html": [
+   "78b484fe718321d2d0b7c213d72f8bd2dc333acb",
+   "support"
+  ],
   "css/css-values/reference/ic-unit-001-ref.html": [
    "2151f3b0c5dd883ffd2e04157726474eff0a5c2b",
    "support"
@@ -397720,6 +399225,278 @@
    "742c1646d8ee0177843e96b08d17bfb499bcaf1e",
    "support"
   ],
+  "fonts/math/axisheight5000-verticalarrow14000.woff": [
+   "9f5d59ae6a7fbf221fd14126645f1cbc7c25d286",
+   "support"
+  ],
+  "fonts/math/fraction-axisheight7000-rulethickness1000.woff": [
+   "2a9d78cc9d430e9987e977ef83f4e47dfee6a99f",
+   "support"
+  ],
+  "fonts/math/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff": [
+   "e4d381edaf259a15b24c55a0100ce9cd05444967",
+   "support"
+  ],
+  "fonts/math/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff": [
+   "3d90e64302a7d0d9708329ef41ae62d441c53e04",
+   "support"
+  ],
+  "fonts/math/fraction-denominatorgapmin4000-rulethickness1000.woff": [
+   "ab648f5eb66369415831d25d7eedc7305205b351",
+   "support"
+  ],
+  "fonts/math/fraction-denominatorshiftdown3000-rulethickness1000.woff": [
+   "0b7efbaf70647351f336fa5bc416fed4dc856593",
+   "support"
+  ],
+  "fonts/math/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff": [
+   "2e910bbf42147fa041e79f80a12e3bd1b375ecff",
+   "support"
+  ],
+  "fonts/math/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff": [
+   "920c81eb1bf7bf82a613c4aa27972a09765f2ee5",
+   "support"
+  ],
+  "fonts/math/fraction-numeratorgapmin9000-rulethickness1000.woff": [
+   "1b5d60bb14457cf9ee639f7d248ae57268ec0b44",
+   "support"
+  ],
+  "fonts/math/fraction-numeratorshiftup11000-rulethickness1000.woff": [
+   "3e70cd29a344d195edf4ded1cc67ced91a23d2e1",
+   "support"
+  ],
+  "fonts/math/fraction-rulethickness10000.woff": [
+   "ec69f2424f27cc26f713ec46dc5e3229652c6b4e",
+   "support"
+  ],
+  "fonts/math/largeop-displayoperatorminheight2000-2AFF-italiccorrection3000.woff": [
+   "0b4f8bf46a76ab5cc70c2a12cb7db0d49570bd7c",
+   "support"
+  ],
+  "fonts/math/largeop-displayoperatorminheight5000.woff": [
+   "53fcc13a1603621c0985dd8ea8d956fd01c974f4",
+   "support"
+  ],
+  "fonts/math/limits-lowerlimitbaselinedropmin3000.woff": [
+   "76395db141ced4560ed7164c6f09bf50fe771d3d",
+   "support"
+  ],
+  "fonts/math/limits-lowerlimitgapmin11000.woff": [
+   "df67de52bd034d880b87b06a159396239c3e33d9",
+   "support"
+  ],
+  "fonts/math/limits-upperlimitbaselinerisemin5000.woff": [
+   "f5d7b9c84e6226776879c5c6ec9af5f78572e396",
+   "support"
+  ],
+  "fonts/math/limits-upperlimitgapmin7000.woff": [
+   "c88e7a9c709854dde86030331a4a3d47b5c6479d",
+   "support"
+  ],
+  "fonts/math/lineheight5000-typolineheight2300.woff": [
+   "09076604c3a1989956adb083108d2878bfb32c4b",
+   "support"
+  ],
+  "fonts/math/mathvariant-bold-fraktur.woff": [
+   "20cd8e3b49e18b9bc900cf13b2ce9664c5ed4864",
+   "support"
+  ],
+  "fonts/math/mathvariant-bold-italic.woff": [
+   "9651fc8fe05881af509b7ff66e47a1ee76ee3935",
+   "support"
+  ],
+  "fonts/math/mathvariant-bold-sans-serif.woff": [
+   "24f8f2d40b072ac140864ab7f7d9ee96fc9b320e",
+   "support"
+  ],
+  "fonts/math/mathvariant-bold-script.woff": [
+   "3cea72111021dbcd955d29df35b14343d4780bd1",
+   "support"
+  ],
+  "fonts/math/mathvariant-bold.woff": [
+   "f35194ef6286ace97806873ae43b06dcdfc3334b",
+   "support"
+  ],
+  "fonts/math/mathvariant-double-struck.woff": [
+   "ed35ee6d6421c94cc7a69344f4e912626ae6dce5",
+   "support"
+  ],
+  "fonts/math/mathvariant-fraktur.woff": [
+   "40adf0d96558fd271f5bf52b2387400715ef7e67",
+   "support"
+  ],
+  "fonts/math/mathvariant-initial.woff": [
+   "db0ca9b2272784650cbd7c584860d3757fc2b0b6",
+   "support"
+  ],
+  "fonts/math/mathvariant-italic.woff": [
+   "9687f377b32b9f07319bce26d9df5aa92e953383",
+   "support"
+  ],
+  "fonts/math/mathvariant-looped.woff": [
+   "ada1b9e6dec6967236f3489cb37cf118209c13d0",
+   "support"
+  ],
+  "fonts/math/mathvariant-monospace.woff": [
+   "fc3f0a4920390e029c389cb064aee3d9493f38bd",
+   "support"
+  ],
+  "fonts/math/mathvariant-sans-serif-bold-italic.woff": [
+   "05bc859625447b7a794823d6626bc68947f73f48",
+   "support"
+  ],
+  "fonts/math/mathvariant-sans-serif-italic.woff": [
+   "dc139bf32ab0793e46f0d0b24a2091640c6ce853",
+   "support"
+  ],
+  "fonts/math/mathvariant-sans-serif.woff": [
+   "8ef41f51081e74a386e3a17b03fabfdf9a0422b4",
+   "support"
+  ],
+  "fonts/math/mathvariant-script.woff": [
+   "d5c457aae0b2b5d67f6bc2d354e464b67fcee74d",
+   "support"
+  ],
+  "fonts/math/mathvariant-stretched.woff": [
+   "470c879ed974e4173e66f69865923f7de0279a34",
+   "support"
+  ],
+  "fonts/math/mathvariant-tailed.woff": [
+   "d612b1e06dbefef308a30ea8eccf0a3397ee5469",
+   "support"
+  ],
+  "fonts/math/radical-degreebottomraisepercent25-rulethickness1000.woff": [
+   "6401070d44fed8d27c688a931d277bce2ef71517",
+   "support"
+  ],
+  "fonts/math/radical-displaystyleverticalgap7000-rulethickness1000.woff": [
+   "0e8b1e0a434e3da52f54c0f64a273ade855b8f23",
+   "support"
+  ],
+  "fonts/math/radical-extraascender3000-rulethickness1000.woff": [
+   "6c0ca282e9597c2eb5d3093ca05e7a1357784226",
+   "support"
+  ],
+  "fonts/math/radical-kernafterdegreeminus5000-rulethickness1000.woff": [
+   "24aa4c525a1ed14af7fff5134962851e28dfbd62",
+   "support"
+  ],
+  "fonts/math/radical-kernbeforedegree4000-rulethickness1000.woff": [
+   "1fca6f6d85a20c14c7d95ac2563a94a1bfd44e47",
+   "support"
+  ],
+  "fonts/math/radical-rulethickness8000.woff": [
+   "0863dc3230ead9d9deb43dac6185d781f7c098d2",
+   "support"
+  ],
+  "fonts/math/radical-verticalgap6000-rulethickness1000.woff": [
+   "9808112c7aa934f49db078637fb082d1f0f24df5",
+   "support"
+  ],
+  "fonts/math/scripts-spaceafterscript3000.woff": [
+   "44f9ece85703d6a94b51becf5620719618c7e90f",
+   "support"
+  ],
+  "fonts/math/scripts-subscriptbaselinedropmin9000.woff": [
+   "46880c1d0cebf8623e683a52ccbab8f84731a21e",
+   "support"
+  ],
+  "fonts/math/scripts-subscriptshiftdown6000.woff": [
+   "3565f7aaa8d3939933e17f3b8584c7d5421afe54",
+   "support"
+  ],
+  "fonts/math/scripts-subscripttopmax4000.woff": [
+   "69cd23de8b5a0259109c9e40b3b4664a632d9528",
+   "support"
+  ],
+  "fonts/math/scripts-subsuperscriptgapmin11000-superscriptbottommaxwithsubscript3000.woff": [
+   "2f9da79063b371acdec4f7d8ed6a66758920c732",
+   "support"
+  ],
+  "fonts/math/scripts-subsuperscriptgapmin11000.woff": [
+   "d85dfee15e32c02906cd32c892d436ba0f6a875b",
+   "support"
+  ],
+  "fonts/math/scripts-superscriptbaselinedropmax10000.woff": [
+   "3f528edfe040de60067bc58da4128678700946a9",
+   "support"
+  ],
+  "fonts/math/scripts-superscriptbottommin8000.woff": [
+   "f3937e869bcf9e98d8b71cfd78c2111289f8e73c",
+   "support"
+  ],
+  "fonts/math/scripts-superscriptshiftup7000.woff": [
+   "845afdf2ff3f5653a893e1d4c06b3ef800856224",
+   "support"
+  ],
+  "fonts/math/scripts-superscriptshiftupcramped5000.woff": [
+   "c85e7f65135328f6bea5a9e35bf8c16c4f189ee6",
+   "support"
+  ],
+  "fonts/math/stack-axisheight7000.woff": [
+   "7a9dc5d4cb1bbc758d2ecf34641e331e9ac7acb0",
+   "support"
+  ],
+  "fonts/math/stack-bottomdisplaystyleshiftdown5000.woff": [
+   "8761585484b83e3dad6488e99340825bcdb872d1",
+   "support"
+  ],
+  "fonts/math/stack-bottomshiftdown6000.woff": [
+   "96ebce2da516621dae35145f49245bc490deb8e4",
+   "support"
+  ],
+  "fonts/math/stack-displaystylegapmin4000.woff": [
+   "a94d6286eabf8dd95204ccbd72d404c752db6998",
+   "support"
+  ],
+  "fonts/math/stack-gapmin8000.woff": [
+   "a4428d1d49bdfccce2ae95b417ae2c902edb86d8",
+   "support"
+  ],
+  "fonts/math/stack-topdisplaystyleshiftup3000.woff": [
+   "556226f12265581701190efd0bd198586ee55139",
+   "support"
+  ],
+  "fonts/math/stack-topshiftup9000.woff": [
+   "d036e893087cef4dc4ad62681b968f9ef75be9c8",
+   "support"
+  ],
+  "fonts/math/stretchstack-bottomshiftdown3000.woff": [
+   "275a4350c91e105f4254dc4b52b359061d49cad6",
+   "support"
+  ],
+  "fonts/math/stretchstack-gapabovemin7000.woff": [
+   "af61d8ed329316166bb3b56f595f41fa8d3025e3",
+   "support"
+  ],
+  "fonts/math/stretchstack-gapbelowmin11000.woff": [
+   "8900a81419f25da6cea3ed8534dbb2a72ba73b0c",
+   "support"
+  ],
+  "fonts/math/stretchstack-topshiftup5000.woff": [
+   "7a90c74f3e51bf9d61870ec33d16b0857bb07772",
+   "support"
+  ],
+  "fonts/math/underover-accentbaseheight4000-overbarextraascender3000.woff": [
+   "2aa7133879b4edc64e44cee3df826092de8e2111",
+   "support"
+  ],
+  "fonts/math/underover-accentbaseheight4000-overbarverticalgap11000.woff": [
+   "0f975115d9343e11249b9bd1369ef70ae597b947",
+   "support"
+  ],
+  "fonts/math/underover-accentbaseheight4000-underbarextradescender5000.woff": [
+   "bb3e4658d8cd78230ab6b9f8564881ecc6ad02e5",
+   "support"
+  ],
+  "fonts/math/underover-accentbaseheight4000-underbarverticalgap7000.woff": [
+   "36a4e869d118b10a0906ef445c77b078466cd249",
+   "support"
+  ],
+  "fonts/math/xheight500.woff": [
+   "76a37da9e4092a0054320574976216e87c7462a3",
+   "support"
+  ],
   "fonts/mplus-1p-regular.woff": [
    "42cfff628b4680024908635e353bfe9bbf10d952",
    "support"
@@ -397756,6 +399533,14 @@
    "1d17b23076d0c2ac61df2354c6fc1d753f83aa22",
    "support"
   ],
+  "fonts/noto/NotoSansMongolian-regular.woff2": [
+   "e34e5474377e4e29679669d4039efe560ac57fa6",
+   "support"
+  ],
+  "fonts/noto/NotoSansNko-regular-webfont.woff2": [
+   "780661ba1ca55c7ba73a8dabe0716c066b8cc7ca",
+   "support"
+  ],
   "fonts/sileot-webfont.woff": [
    "81547578d36c7989db0a03127074a5f4b33768b4",
    "support"
@@ -411124,6 +412909,10 @@
    "292395d3aef45b01dec8d9db576b42b4cde61749",
    "testharness"
   ],
+  "html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html": [
+   "2cc74e2b8f49ec7f698b325e8570dbdeea7d91fe",
+   "testharness"
+  ],
   "html/semantics/embedded-content/the-img-element/update-media.html": [
    "dd679ef571584a2dea1f050aa22a09b8a99a11a4",
    "testharness"
@@ -417988,20 +419777,16 @@
    "9ae71a6e73e22a855c69d3269936d71c17d6e9e5",
    "support"
   ],
-  "infrastructure/metadata/infrastructure/testdriver/actions/elementTiming.html.ini": [
-   "a06134a6ea427d40aa7b664bde5d4b3d6df24b96",
-   "support"
-  ],
   "infrastructure/metadata/infrastructure/testdriver/actions/eventOrder.html.ini": [
-   "00916c7af3f1ec97692afb727b02dc4f29327a99",
+   "bcd78da24446a9802755a13525be90e3fa0f43fc",
    "support"
   ],
   "infrastructure/metadata/infrastructure/testdriver/actions/multiDevice.html.ini": [
-   "b9fb2596b952ad81b97d7e04b5eb941d37b3b5da",
+   "04b97a7198c75ba941d315716483aad216e1a283",
    "support"
   ],
   "infrastructure/metadata/infrastructure/testdriver/actions/pause.html.ini": [
-   "7ceec9f531bfe3ede763a41726590f3effdbac29",
+   "da47a2a0b6f7b7b019c3ae20e349f37c00b8e0bc",
    "support"
   ],
   "infrastructure/metadata/infrastructure/testdriver/file_upload.sub.html.ini": [
@@ -418589,7 +420374,7 @@
    "support"
   ],
   "interfaces/mediasession.idl": [
-   "d2d16c232b7c72704b2648f844b64b4482ff2072",
+   "0fc9ff05f9475cd7f48fde6c22765d431b51310c",
    "support"
   ],
   "interfaces/mediastream-recording.idl": [
@@ -418837,7 +420622,7 @@
    "support"
   ],
   "interfaces/webxr.idl": [
-   "63643a11b6b0cb7ecf7aabc19b03edf1d47a6642",
+   "b1ed0f9072716efd5ef92ead32d58b396319dbfa",
    "support"
   ],
   "interfaces/worklets.idl": [
@@ -419364,6 +421149,466 @@
    "e6b8f7d23859b457e5b2339d28d4da604668de00",
    "testharness"
   ],
+  "mathml/META.yml": [
+   "a410f582f0a9b006946e8d9c46e1e0a2b5acea43",
+   "support"
+  ],
+  "mathml/README.md": [
+   "bab35474dd5726d8310f22c826da0e6cff86ab7e",
+   "support"
+  ],
+  "mathml/presentation-markup/fractions/frac-1.html": [
+   "fc650eb4c4a65cc49031e870229f463b3422b609",
+   "testharness"
+  ],
+  "mathml/presentation-markup/fractions/frac-parameters-1.html": [
+   "a047a30873c7765dafa5678fc99072bbe2f017e4",
+   "testharness"
+  ],
+  "mathml/presentation-markup/fractions/frac-parameters-2.html": [
+   "544511388685a3ed79eaa2e80875205c19e7a62f",
+   "testharness"
+  ],
+  "mathml/presentation-markup/operators/mo-axis-height-1.html": [
+   "327a72e30b53a6b0a619ef2b33f21bfeb7fd4a63",
+   "testharness"
+  ],
+  "mathml/presentation-markup/radicals/root-parameters-1.html": [
+   "67a4613813421ed55a7fb44622b52f2c1f0f2b0f",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/subsup-1.html": [
+   "2ff14a694c99461f4474af1d9b283ed3082f7b8b",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/subsup-2.html": [
+   "abef28d12df64ffac355fd627fe2493ae7b43ff9",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/subsup-3.html": [
+   "c49718979c30b9fa782fda0dc49686e7f7f362bb",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/subsup-4.html": [
+   "2acc7746c4fb1e754f7c3abbc2d424b244ce9876",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/subsup-5.html": [
+   "19b6eee66747795b00776768aa1738006725e3c0",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/subsup-parameters-1.html": [
+   "9bc6bcbf277c1352d2d23c889048a2afc745ee9f",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/subsup-parameters-2.html": [
+   "eaa4f0ffab154aff2ca1072f306c115a8d0bcf13",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/underover-1.html": [
+   "6e039b9d609d65933ebc736a9a9ff91d2da572b5",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/underover-parameters-1.html": [
+   "1e5a6606b03a19487b2394b95eac26bd4ac0cb4e",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/underover-parameters-2.html": [
+   "c28f29c99e69761ddd2e9205b4f942e65014afc8",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/underover-parameters-3.html": [
+   "0172ff1c700924e3412e84cd2e1445a009e61b5e",
+   "testharness"
+  ],
+  "mathml/presentation-markup/scripts/underover-parameters-4.html": [
+   "061cda79af9f9e7157ac37736f755ac233bf0a6f",
+   "testharness"
+  ],
+  "mathml/presentation-markup/spaces/space-1.html": [
+   "adb36377842295e1bf0ec298d7ec19aa5adaeeb4",
+   "testharness"
+  ],
+  "mathml/presentation-markup/spaces/space-2-ref.html": [
+   "5a8b39e1898c4f8d5555ee11d5757965162780e7",
+   "support"
+  ],
+  "mathml/presentation-markup/spaces/space-2.html": [
+   "544cfb1f6689a51b69c4492af5403a207032800a",
+   "reftest"
+  ],
+  "mathml/presentation-markup/tables/table-axis-height.html": [
+   "50c3491e487bfb3c22d444266e4a1aedb070641a",
+   "testharness"
+  ],
+  "mathml/relations/css-styling/color-1-ref.html": [
+   "0efca480eec5a3da684fe79a429982b139b202e4",
+   "support"
+  ],
+  "mathml/relations/css-styling/color-1.html": [
+   "4ef821340d22109a64809bbb4a1a753279d91420",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/display-1-ref.html": [
+   "ce65aba18c9483274765cde6f62bdf5fdeea001e",
+   "support"
+  ],
+  "mathml/relations/css-styling/display-1.html": [
+   "551f6402d85924005491cf50f3b9d4fcd547f8fe",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/displaystyle-1.html": [
+   "2749e09c48a759c02f1f2148387c8586c207b9e5",
+   "testharness"
+  ],
+  "mathml/relations/css-styling/lengths-1-ref.html": [
+   "9fca6f49632ca080ee1b2a32a9ce2d251241dfcc",
+   "support"
+  ],
+  "mathml/relations/css-styling/lengths-1.html": [
+   "e5864ccc2acaf9fb602274db13b7f6bc846563a3",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/lengths-2-ref.html": [
+   "9fca6f49632ca080ee1b2a32a9ce2d251241dfcc",
+   "support"
+  ],
+  "mathml/relations/css-styling/lengths-2.html": [
+   "e88111aa537c9223e9203f69c8afd6ed5d3d4390",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/lengths-3.html": [
+   "a7133f89ecf4eed567c2d44bbb8d585be474c17d",
+   "testharness"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold-fraktur-ref.html": [
+   "b883b12b57dbc99c0049ba98c8d3574524c42505",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold-fraktur.html": [
+   "487d61d7d852ad850a9d99d56a1f82b92138bf8b",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold-italic-ref.html": [
+   "1e71e89663698ea8e20a4ca20f1ab7879cf3ce60",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold-italic.html": [
+   "640baf34da88b9851f3c06a60c7e81baac21be3a",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold-ref.html": [
+   "11cb2de250d689ca933f82939dad284e680aaf93",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold-sans-serif-ref.html": [
+   "a57fd18bf2229eb538bd5b14ea5d7986c50738a1",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold-sans-serif.html": [
+   "1cab2372ba3405e63f615354b1b61ba98c5d0bed",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold-script-ref.html": [
+   "4203861f707f76a6ed949847451ff09e97bb8ebd",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold-script.html": [
+   "9083afa801d7132b7f6a095d14913a6b3e83f449",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-bold.html": [
+   "b3fe917d23c67476667cc8492b218a5a38cbfc7f",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-double-struck-ref.html": [
+   "56db2591daed0f839645de0bfe0c24e22489ba39",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-double-struck.html": [
+   "e51908e1ea39fccdca8ba481c176bd3436d5bbf3",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-fraktur-ref.html": [
+   "5e0721cfcea5b61c267bb194c6bd1260d1388167",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-fraktur.html": [
+   "124891727cd43dd94cd4408b633169cc57c57e67",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-initial-ref.html": [
+   "722ce5f657da127eb9cbe31241a3728cbc11eaa7",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-initial.html": [
+   "df9e0af7fc53f6486d43e28db588508deaa3aa24",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-italic-ref.html": [
+   "70643b3e61104ee6a6e79920d2c196e06233bab5",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-italic.html": [
+   "e1612a528c8976e76d0f0990621f80880992b7f1",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-looped-ref.html": [
+   "49bafef99b8420cbf01213e520acad24186af2f2",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-looped.html": [
+   "81206e1facff7ead839d4df329c80c6896771b8d",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-monospace-ref.html": [
+   "9b695a1a447b7368eae8d50d0a98b9dcc5502754",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-monospace.html": [
+   "8d4a4ad9234378ab503cfbeca89ec8cebee7b3c3",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-sans-serif-bold-italic-ref.html": [
+   "f8ef62bd6a2f466df8a97cf6760ff9dd903201fe",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html": [
+   "70207367e0588f9460e9fc3e118103768a14b9f6",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-sans-serif-italic-ref.html": [
+   "b82cde87e294cb5fe62d0f7d667489ce9228e782",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-sans-serif-italic.html": [
+   "0576236dbf990669602b031786174c5c12a25537",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-sans-serif-ref.html": [
+   "35e4b6567d2512a3a9e676a0d233c3ead8714ca8",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-sans-serif.html": [
+   "fc880b99aff3cc117af5394b5290412a373a1c96",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-script-ref.html": [
+   "725268a84e5ae12e7d75116ce518bd637de790a7",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-script.html": [
+   "5725ed510331d29cda6f08056b83378b5da88d82",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-stretched-ref.html": [
+   "1450e19edf0ac6b73583d2abb7a3d921cec754f6",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-stretched.html": [
+   "603cfe704fd0605dd15fb71ef1ae0b0e54260190",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/mathvariant-tailed-ref.html": [
+   "b8e6160fea526756851c77aa637e01abbc25aded",
+   "support"
+  ],
+  "mathml/relations/css-styling/mathvariant-tailed.html": [
+   "c00417d8d6ac3a553c930b0f1c753652fa804475",
+   "reftest"
+  ],
+  "mathml/relations/css-styling/visibility-1-ref.html": [
+   "fcaf5fe85b44129db71f82913374859380576369",
+   "support"
+  ],
+  "mathml/relations/css-styling/visibility-1.html": [
+   "be8da3b00a2adafcbd3908733c1edea55f90fddb",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/class-1-ref.html": [
+   "5afa59ecf2bfd2ec071d735464cd3bd20b0b17ce",
+   "support"
+  ],
+  "mathml/relations/html5-tree/class-1.html": [
+   "654852329b4abe04a9c2736586cb7f4dc63a8562",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/class-2.html": [
+   "9dbede840199c32fe89eb7800abd08bf811ecdf1",
+   "testharness"
+  ],
+  "mathml/relations/html5-tree/color-attributes-1-ref.html": [
+   "71ee8cea9db15b3c6359788aa95efd6b825def81",
+   "support"
+  ],
+  "mathml/relations/html5-tree/color-attributes-1.html": [
+   "b7bdf723e38149b0f30522bda8634fa5ac94b286",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/display-1.html": [
+   "4eb302c3ee821a015e76c561b46eb528ce78a93d",
+   "testharness"
+  ],
+  "mathml/relations/html5-tree/dynamic-1-ref.html": [
+   "5fa90e9d2fe8bd44a506a8bffa4f1adec6374dda",
+   "support"
+  ],
+  "mathml/relations/html5-tree/dynamic-1.html": [
+   "ab22ad13cdbd612eabbd160a66867472bed716a8",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/href-click-1-ref.html": [
+   "86952567c76f4b0f1c45ac8b03b34a93d0400163",
+   "support"
+  ],
+  "mathml/relations/html5-tree/href-click-1.html": [
+   "80e4c754d805ce85744f8d6157c1901f471be2a9",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/href-click-2-ref.html": [
+   "86952567c76f4b0f1c45ac8b03b34a93d0400163",
+   "support"
+  ],
+  "mathml/relations/html5-tree/href-click-2.html": [
+   "1e41f77cc4375f7cf8568cecb7b8d169da0be14f",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/href-manual.html": [
+   "7f24f20f4f2b8756409d5cd9daf5ed364ba72431",
+   "manual"
+  ],
+  "mathml/relations/html5-tree/integration-point-1-ref.html": [
+   "49877549670ae9fdd81e24801c74756465c4c595",
+   "support"
+  ],
+  "mathml/relations/html5-tree/integration-point-1.html": [
+   "b3ab19c773875a6778b9588eee0f3c9cada56763",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/integration-point-2-ref.html": [
+   "33c4b7e91041544e62766b71044b4b8798a9b470",
+   "support"
+  ],
+  "mathml/relations/html5-tree/integration-point-2.html": [
+   "e970f9ec8053879844bf5b50e2822fd34b6f93a0",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/integration-point-3-ref.html": [
+   "8362ed28e359dbffb39d2bbdc61c5703961e4e0f",
+   "support"
+  ],
+  "mathml/relations/html5-tree/integration-point-3.html": [
+   "8132acc6d7c2b172fec1509b8af591630362f14f",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/required-extensions-1.html": [
+   "795f516671a616f48038cb16b6cc82329e5826b4",
+   "testharness"
+  ],
+  "mathml/relations/html5-tree/required-extensions-2-ref.html": [
+   "dcc5b2b7d337e769299df17a0eacafddc92665c6",
+   "support"
+  ],
+  "mathml/relations/html5-tree/required-extensions-2.html": [
+   "b49bddaa69d4e88993301d36b0d802a4d0b8ab12",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/unique-identifier-1-iframe-1.html": [
+   "6b3ab07f1aa1dcf7edefd39a3e5f5bc4724ff960",
+   "support"
+  ],
+  "mathml/relations/html5-tree/unique-identifier-1-iframe-2.html": [
+   "ade0110a27e4a97e7e57b7af3bc3382df971057a",
+   "support"
+  ],
+  "mathml/relations/html5-tree/unique-identifier-1-ref.html": [
+   "a219b2c870a2da3184d010963044c05ca2c5bf7d",
+   "support"
+  ],
+  "mathml/relations/html5-tree/unique-identifier-1.html": [
+   "39c49a6b56b4e64a59ae2835081ed18c2e6a4c86",
+   "reftest"
+  ],
+  "mathml/relations/html5-tree/unique-identifier-2.html": [
+   "b3226c293b5f1394f5245644d1630bfef4f86b36",
+   "testharness"
+  ],
+  "mathml/relations/html5-tree/unique-identifier-3-ref.html": [
+   "ef056e00826c845e2ba2ef655e03dae1be51d29f",
+   "support"
+  ],
+  "mathml/relations/html5-tree/unique-identifier-3.html": [
+   "5d66e133ab08b3068306cb2252094262716aaed5",
+   "reftest"
+  ],
+  "mathml/relations/text-and-math/use-typo-metrics-1-ref.html": [
+   "3f7f7640458639ab0b548489e04b63f98cdad40b",
+   "support"
+  ],
+  "mathml/relations/text-and-math/use-typo-metrics-1.html": [
+   "081d66b63212bbba825913b6c1aa2f0a3609a6e1",
+   "reftest"
+  ],
+  "mathml/tools/axisheight.py": [
+   "43827e7031665bdd57ee54e208ea0f875a9a60ec",
+   "support"
+  ],
+  "mathml/tools/fractions.py": [
+   "8652806b2971b90293e5586c3f40b12d79a70fb2",
+   "support"
+  ],
+  "mathml/tools/largeop.py": [
+   "73d967689bb7dc1101632a883aa08059bf5d7979",
+   "support"
+  ],
+  "mathml/tools/limits.py": [
+   "840a76ffb1a6ac199ff9c655e72262f8955012a2",
+   "support"
+  ],
+  "mathml/tools/mathvariant-transforms.py": [
+   "6094a74491d337a2d75bbc7d1ba9e73a455be05d",
+   "support"
+  ],
+  "mathml/tools/radicals.py": [
+   "90fe1d9cc1e56448fc206f8cf5f2c4e2ba9d02ab",
+   "support"
+  ],
+  "mathml/tools/scripts.py": [
+   "add16373845fc09c1ea8431c1e23e8beef03537a",
+   "support"
+  ],
+  "mathml/tools/stacks.py": [
+   "81f79befb69082fd0a4a26ebc9ed9283662c11b9",
+   "support"
+  ],
+  "mathml/tools/stretchstacks.py": [
+   "7b888f49e70c6fdd07caef792ee03d76897695f6",
+   "support"
+  ],
+  "mathml/tools/underover.py": [
+   "df34e7999602be573a1b25bee8debd0663ab3750",
+   "support"
+  ],
+  "mathml/tools/use-typo-lineheight.py": [
+   "9768979f488e3828dffddc2111e5916546860c47",
+   "support"
+  ],
+  "mathml/tools/utils/__init__.py": [
+   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+   "support"
+  ],
+  "mathml/tools/utils/mathfont.py": [
+   "7664c6d2434406090020f20e9ec35e513d0390c6",
+   "support"
+  ],
+  "mathml/tools/utils/misc.py": [
+   "e4d21d60cb09d979404b861a9a6b2787676a9752",
+   "support"
+  ],
+  "mathml/tools/xHeight.py": [
+   "724352bf91679ea9a7e4d862ad43b5707f9cfb7b",
+   "support"
+  ],
   "media-capabilities/META.yml": [
    "fc70ffb18538bfb3c1fd4e637ac6a00df9bab74a",
    "support"
@@ -421077,7 +423322,7 @@
    "support"
   ],
   "mixed-content/imageset.https.sub.html": [
-   "dd371566161dcf4a22fd14bd2cd02ef0b06462a9",
+   "1f3d0471fbd2b38649894619494750ef221d72c9",
    "testharness"
   ],
   "mixed-content/img-tag/http-csp/cross-origin-http/top-level/keep-scheme-redirect/optionally-blockable/opt-in-blocks.https.html": [
@@ -431733,17 +433978,25 @@
    "support"
   ],
   "portals/portal-activate-event.html": [
-   "ac1505d2a5b2fe1df083eae75893483e025a2ad7",
+   "33d91e37d9d9ac77c5243a60b42ce841645d248e",
    "testharness"
   ],
   "portals/portals-activate-no-browsing-context.html": [
-   "9a822e9238a938e48c5c1bc6d76669d48962ee65",
+   "6eebca9f9d982ffd38a96bb72ff0173bcfb07903",
    "testharness"
   ],
   "portals/portals-cross-origin-load.sub.html": [
    "f860ac54dc9dc6578fa1a66c25da70bc3262d995",
    "testharness"
   ],
+  "portals/portals-host-exposure.sub.html": [
+   "83e31bd4735131d35b2a03ae82d07be364497689",
+   "testharness"
+  ],
+  "portals/portals-host-hidden-after-activation.html": [
+   "f51e54bd49c5c1a422dab6f249fc3ee1da87eb0d",
+   "testharness"
+  ],
   "portals/portals-host-null.html": [
    "e0f1d63743c54c687d62f86abe278873fa823430",
    "testharness"
@@ -431764,18 +434017,34 @@
    "b2759c3701aaba4f5887a8b90bf4ee30e8153661",
    "support"
   ],
-  "portals/resources/portal-activate-event-window.html": [
-   "cf09caebc0ff9ac38facde84075a7af5be19fd48",
-   "support"
-  ],
   "portals/resources/portal-cross-origin.sub.html": [
    "145ab5a2d21295f615d3ecd5d36f9e3034a4202a",
    "support"
   ],
+  "portals/resources/portal-embed-and-activate.html": [
+   "6b77a2bfcc37e5269340a0c3969e65ac8e6c8cc0",
+   "support"
+  ],
   "portals/resources/portal-forward-with-broadcast.html": [
    "39bda69b0eef9b0062809507bfb91d9fc3401d95",
    "support"
   ],
+  "portals/resources/portal-host-cross-origin-navigate.sub.html": [
+   "44c6c16c5771f1027c3cc82e966342bbaa80ad8d",
+   "support"
+  ],
+  "portals/resources/portal-host-cross-origin.sub.html": [
+   "dc4e9e7b5de4d17d2110f943fc04b99ed076102c",
+   "support"
+  ],
+  "portals/resources/portal-host-hidden-after-activation-portal.html": [
+   "586929e31d0efde5865b7416009ebcc1882d071c",
+   "support"
+  ],
+  "portals/resources/portal-host.html": [
+   "5043a158ea74ef173f166c0580f9c1a27242bd14",
+   "support"
+  ],
   "portals/resources/portals-rendering-portal.html": [
    "1b6f23f512da5bb7d1c7b5b85e48277470d2e146",
    "support"
@@ -431793,11 +434062,11 @@
    "support"
   ],
   "preload/avoid-delaying-onload-link-preload.html": [
-   "77838c37741990153df68bdf746bd93b63d3d7a1",
+   "a1b19c81c6783477f8914bf94d8e58d8644e06f3",
    "testharness"
   ],
   "preload/delaying-onload-link-preload-after-discovery.html": [
-   "095d89ad90ca15eac57a142d051f60207cf94f92",
+   "1c856d16d409479746f4c18c65028c38a026fbba",
    "testharness"
   ],
   "preload/download-resources-expected.txt": [
@@ -431805,15 +434074,15 @@
    "support"
   ],
   "preload/download-resources.html": [
-   "dc2b4693cf11fe224e599b97ba8556894a9e0240",
+   "510ebb480457e9e1b0d6ea788a8bd36c825bc634",
    "testharness"
   ],
   "preload/dynamic-adding-preload-imagesrcset.tentative.html": [
-   "be8f0afcd5bdcc7daa61fa2666220780acf37ebf",
+   "e1b8431d7bcaca618014496342055d533ba7399c",
    "testharness"
   ],
   "preload/dynamic-adding-preload-nonce.html": [
-   "10dae6b99586450367852cf5bb006d8511cf2865",
+   "19e09472eef3813c9fb25ff7b6549477615c760e",
    "testharness"
   ],
   "preload/dynamic-adding-preload-nonce.html.headers": [
@@ -431821,15 +434090,15 @@
    "support"
   ],
   "preload/dynamic-adding-preload.html": [
-   "2a299bd8446bf049d7e29b69c7fa6e0249f9ae6d",
+   "0cecc1983eaa50fbaa00b7d70031c8b11b84c4e7",
    "testharness"
   ],
   "preload/link-header-on-subresource.html": [
-   "a02bc7c819eb7c5049788b493556f8e34cc15091",
+   "087a3429e649348d2b35fd92bf03ebc6307c72dc",
    "testharness"
   ],
   "preload/link-header-preload-delay-onload.html": [
-   "7f38f8c9ee53d27614b20df9a76a493f3b4aabcf",
+   "a445d800a586357a6eb5d0ef11778b55fbc301d4",
    "testharness"
   ],
   "preload/link-header-preload-delay-onload.html.headers": [
@@ -431837,7 +434106,7 @@
    "support"
   ],
   "preload/link-header-preload-nonce.html": [
-   "240d6f11dd5979457ed8a4d6ab3c97e9d1ce9f9c",
+   "dc1ec100776916319a637daa0397589a05a23804",
    "testharness"
   ],
   "preload/link-header-preload-nonce.html.headers": [
@@ -431845,7 +434114,7 @@
    "support"
   ],
   "preload/link-header-preload-srcset.tentative.html": [
-   "024da965796fb5960bc43cdf4de1806fbef74ffe",
+   "8d057549a1930dea4502ef7400f44c9113ec3e62",
    "testharness"
   ],
   "preload/link-header-preload-srcset.tentative.html.headers": [
@@ -431853,7 +434122,7 @@
    "support"
   ],
   "preload/link-header-preload.html": [
-   "94a731bdcebb0eea04e5fe09152dfc013bb37afa",
+   "0ca364bdef71ad98fcf12db36e8e71c414745b57",
    "testharness"
   ],
   "preload/link-header-preload.html.headers": [
@@ -431869,7 +434138,7 @@
    "support"
   ],
   "preload/onerror-event.html": [
-   "5fae70d3bcab22bf50448c817f856c4b90f110a0",
+   "8190be87a4b7caf69ba07d07d239ff8143be3b05",
    "testharness"
   ],
   "preload/onload-event-expected.txt": [
@@ -431877,15 +434146,15 @@
    "support"
   ],
   "preload/onload-event.html": [
-   "6af2d64a1c1d73adb8a6504a300cb35de9807038",
+   "f9348b8ceb392117a2da6560e64118df2482dfdf",
    "testharness"
   ],
   "preload/preload-csp.sub.html": [
-   "8e5e45b9a1cdac572650bfa270a57930154e7bbc",
+   "7fe06eb079133a245472c085068bb311dabebc68",
    "testharness"
   ],
   "preload/preload-default-csp.sub.html": [
-   "cb080e62ba3efecbd86139c3952f3b4461735a51",
+   "7813e36d4d8ada4bd4e5f79a583f2ff89519c5eb",
    "testharness"
   ],
   "preload/preload-strict-dynamic.html": [
@@ -431897,7 +434166,7 @@
    "support"
   ],
   "preload/preload-with-type.html": [
-   "8578143a23495e1828d313ddc6a9310df75fcdb0",
+   "83eafc5848b9c514e9136cbedfc64df6665d5aa9",
    "testharness"
   ],
   "preload/reflected-as-value-expected.txt": [
@@ -431977,7 +434246,7 @@
    "support"
   ],
   "preload/resources/preload_helper.js": [
-   "b2cf8323db0145fa4876fb40d0d1ed3ff1c6413b",
+   "f464908fa513353917901d49af150d53cfd99945",
    "support"
   ],
   "preload/resources/sound_5.oga": [
@@ -432009,11 +434278,11 @@
    "support"
   ],
   "preload/single-download-late-used-preload.html": [
-   "5549cb84fdb9ff03e348c6d005b5850b7294536b",
+   "51533ba71445cc5b9edb235aaf28215671a1ca62",
    "testharness"
   ],
   "preload/single-download-preload.html": [
-   "e8f261787107ff976b9df36b408c9dc5ce2a50ac",
+   "16d893ca7e54adde5fec3744b95a14f7e2cf3f34",
    "testharness"
   ],
   "presentation-api/META.yml": [
@@ -442105,7 +444374,7 @@
    "support"
   ],
   "resources/testharness.js": [
-   "fb86c580d22ca14159b19b21a55b2cabfc76a742",
+   "d40817c7d4197ccc2469f646207b82d8a0af4c84",
    "support"
   ],
   "resources/testharness.js.headers": [
@@ -442181,11 +444450,11 @@
    "support"
   ],
   "screen-capture/getdisplaymedia.https-expected.txt": [
-   "b8e2309976dad9543bb99af4eb4e99bd7a678261",
+   "2209b8770be42f3bf4da8b471ec2bfc6f9e4314c",
    "support"
   ],
   "screen-capture/getdisplaymedia.https.html": [
-   "e52e596d6fb9285b74e45646a24817082b6454ce",
+   "7427e3433d01682570126b4bfe967c240fd12d3e",
    "testharness"
   ],
   "screen-capture/historical.https.html": [
@@ -447420,26 +449689,10 @@
    "2bf9eabed8410c9352a70163c8f40e25811dfd0f",
    "testharness"
   ],
-  "streams/writable-streams/aborting.any-expected.txt": [
-   "f2ded616cec362b3d8e95eea45ad9e15a7fdd01f",
-   "support"
-  ],
   "streams/writable-streams/aborting.any.js": [
    "ea47a55fa9ff61cdc2f0ac3caca1e98c7b2c719d",
    "testharness"
   ],
-  "streams/writable-streams/aborting.any.serviceworker-expected.txt": [
-   "f2ded616cec362b3d8e95eea45ad9e15a7fdd01f",
-   "support"
-  ],
-  "streams/writable-streams/aborting.any.sharedworker-expected.txt": [
-   "f2ded616cec362b3d8e95eea45ad9e15a7fdd01f",
-   "support"
-  ],
-  "streams/writable-streams/aborting.any.worker-expected.txt": [
-   "f2ded616cec362b3d8e95eea45ad9e15a7fdd01f",
-   "support"
-  ],
   "streams/writable-streams/bad-strategies.any.js": [
    "d67ee6b5039dc98e5093aef0c3f2820462112a4c",
    "testharness"
@@ -447509,7 +449762,7 @@
    "testharness"
   ],
   "streams/writable-streams/write.any.js": [
-   "85c7f8ceb969b02f1928ad9992c31b94527e369e",
+   "28fcf650e9f5660279ca7485c7f6478bbd1c97ce",
    "testharness"
   ],
   "subresource-integrity/META.yml": [
@@ -451673,7 +453926,7 @@
    "support"
   ],
   "web-animations/testcommon.js": [
-   "5dbf6fd7f29f4985d3f2fbce4407f6594d80fac7",
+   "e6dad7cbf8d42b67660753fc5932e44ac531893c",
    "support"
   ],
   "web-animations/timing-model/animation-effects/active-time.html": [
@@ -452284,10 +454537,6 @@
    "2778493e3b6c12d4c00c77bd975e845063621522",
    "support"
   ],
-  "webaudio/the-audio-api/the-analysernode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-analysernode-interface/ctor-analyser.html": [
    "a9aa4831516c6a5cefa7c8b4f67f3ef246d24777",
    "testharness"
@@ -452324,10 +454573,6 @@
    "a8b5a7154e94479460c1085c6b5cb584e9b6976c",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-audiobuffer-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-audiobuffer-interface/audiobuffer-copy-channel.html": [
    "e0359953d2e909f69066885515f4a3f3cc00ff02",
    "testharness"
@@ -452348,10 +454593,6 @@
    "9845d5eaba384cced3c63ddbf4df1400b31f4994",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-audiobuffersourcenode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-audiobuffersourcenode-interface/audiobuffersource-basic.html": [
    "6ce7eb0c103f1d087d99ff6a995976e6ff8d594b",
    "testharness"
@@ -452420,10 +454661,6 @@
    "5fafd024eef9b476f4d97b2ebaa2190a4ca520d5",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-audiocontext-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-audiocontext-interface/audiocontext-getoutputtimestamp.html": [
    "952f38b1edceb62ab3f99c25777ebdb2c59e691a",
    "testharness"
@@ -452436,18 +454673,6 @@
    "3a11074a41c0918343291505526eaf08a01117a3",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-audiodestinationnode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
-  "webaudio/the-audio-api/the-audiolistener-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
-  "webaudio/the-audio-api/the-audionode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-audionode-interface/audionode-channel-rules.html": [
    "9067e6869bcf682e4f3f945d567a2d8a300d9f4b",
    "testharness"
@@ -452480,10 +454705,6 @@
    "35cfca8e4eec6809832845aa48388d0a30c602fb",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-audioparam-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-audioparam-interface/audioparam-close.html": [
    "b5555b0137af4c1c8f6c5578de4bc9c5eedfe405",
    "testharness"
@@ -452608,10 +454829,6 @@
    "36fde2b9964953f196a811add078c63610373334",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-audioprocessingevent-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-audioworklet-interface/audioworklet-addmodule-resolution.https.html": [
    "e94621296a3114ddbc53f26d74c330d3496dac63",
    "testharness"
@@ -452724,10 +454941,6 @@
    "b97ed6e115132e7fe9679812788d252b01090c4c",
    "support"
   ],
-  "webaudio/the-audio-api/the-biquadfilternode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-biquadfilternode-interface/biquad-allpass.html": [
    "86618f9e46dbc1b5462f949cadf703ac59993577",
    "testharness"
@@ -452788,10 +455001,6 @@
    "d54bc0bd8abe5f65bbfa618c76c286be78b9a6ac",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-channelmergernode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-channelmergernode-interface/audiochannelmerger-basic.html": [
    "71a62f176f8eefd9b9168b2c222cc6d1c752d3a5",
    "testharness"
@@ -452812,10 +455021,6 @@
    "0d6b45c56df0f43a0eac3f364cadca2e4669c6ed",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-channelsplitternode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-channelsplitternode-interface/audiochannelsplitter.html": [
    "954c71a96b288530e6c22878fed7a91f42091dc2",
    "testharness"
@@ -452844,10 +455049,6 @@
    "1e0d7255c556bec234ec57dd229f544e00c93749",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-convolvernode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-convolvernode-interface/convolution-mono-mono.html": [
    "570efebe220e94e68ffe3f067595e4cfe766a1b4",
    "testharness"
@@ -452888,10 +455089,6 @@
    "935ceeb715edd2ffdeb7979d6824736fa82b6d2f",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-delaynode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-delaynode-interface/ctor-delay.html": [
    "e7ccefc655364d20bb240beacc81a4f7a10806dd",
    "testharness"
@@ -452932,10 +455129,6 @@
    "7857cf16aa0574bf1974186d327fd7dd9fb99403",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-dynamicscompressornode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-dynamicscompressornode-interface/ctor-dynamicscompressor.html": [
    "c2460dfa1ddd26a5c2e199873c0b28189275ff83",
    "testharness"
@@ -452944,10 +455137,6 @@
    "6c602010d0e3a9a7c4199b8923600676a5f5d3a1",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-gainnode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-gainnode-interface/ctor-gain.html": [
    "dec273e9698702c5b1ee476a5c2e343bbc6e5bb6",
    "testharness"
@@ -452984,10 +455173,6 @@
    "61c11ffc5083a90ba7a29183a521a7aacac38f72",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/cors-check.https.html": [
    "a2fa8040b2c6100f7ec22f0f133525808403ce01",
    "testharness"
@@ -453000,18 +455185,6 @@
    "38324a9f67a67f50f134fb78af43117e2ec9b8c8",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-mediastreamaudiodestinationnode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
-  "webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
-  "webaudio/the-audio-api/the-offlineaudiocontext-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-offlineaudiocontext-interface/ctor-offlineaudiocontext.html": [
    "4b6863103622c5fb248dee3e3eb20d955275d037",
    "testharness"
@@ -453020,10 +455193,6 @@
    "ee976f7f72aa2530754c115fe7039f17c058f744",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-oscillatornode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-oscillatornode-interface/ctor-oscillator.html": [
    "36bf604b296c63b213d99408ab38937c62a755dc",
    "testharness"
@@ -453032,10 +455201,6 @@
    "81a1293d0355ed448e60c0e31ad4435ea708e224",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-pannernode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-pannernode-interface/automation-changes.html": [
    "8aa73552aab5e1639ac95e4ae7e06c725c9cad0c",
    "testharness"
@@ -453096,14 +455261,6 @@
    "ce474b10b5122eaf40b8b6d1af874ad7ec9bff70",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-periodicwave-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
-  "webaudio/the-audio-api/the-scriptprocessornode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-stereopanner-interface/ctor-stereopanner.html": [
    "9409f1ffce2110c177332388988d7ddb559d3ae2",
    "testharness"
@@ -453120,10 +455277,6 @@
    "f683fd78bf981698d77c58e83dc7b933f852266e",
    "testharness"
   ],
-  "webaudio/the-audio-api/the-waveshapernode-interface/.gitkeep": [
-   "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
-   "support"
-  ],
   "webaudio/the-audio-api/the-waveshapernode-interface/ctor-waveshaper.html": [
    "7aa33ca5aa3323754755ebfda200976b0b292730",
    "testharness"
@@ -453996,12 +456149,16 @@
    "fd7215fa4e162acaed2af563c83b5b0846af3dd5",
    "testharness"
   ],
+  "webrtc/RTCError.html": [
+   "e83dba27453cc4677479d7d601e169997ec63610",
+   "testharness"
+  ],
   "webrtc/RTCIceCandidate-constructor-expected.txt": [
-   "bb163f48a594fec740fa1fbd93a101f8c271bdc1",
+   "e5457312c5a221ec18a8bbf372af533cff270080",
    "support"
   ],
   "webrtc/RTCIceCandidate-constructor.html": [
-   "cad0464a3c045a000270127ce50f89798f9eca0b",
+   "344007ded2b4d4496171402896d738817cdde12e",
    "testharness"
   ],
   "webrtc/RTCIceConnectionState-candidate-pair.https.html": [
@@ -454273,7 +456430,7 @@
    "testharness"
   ],
   "webrtc/RTCPeerConnectionIceEvent-constructor-expected.txt": [
-   "9a48c640c5b08e9e39c1ce3b800d74c417f42b96",
+   "fe7ba8dc82c09cd95febf85cd002fa3dc001a40b",
    "support"
   ],
   "webrtc/RTCPeerConnectionIceEvent-constructor.html": [
@@ -454477,7 +456634,7 @@
    "testharness"
   ],
   "webrtc/idlharness.https.window-expected.txt": [
-   "a55770611b4412d120939d29495bf5fabe01b68d",
+   "e8123d1f8d18fab140b1b1fe50614cbed7035274",
    "support"
   ],
   "webrtc/idlharness.https.window.js": [
@@ -455912,6 +458069,14 @@
    "d2911d867fef7752b1be963ce12d3602cd946cf3",
    "testharness"
   ],
+  "webstorage/symbol-props.window-expected.txt": [
+   "df6dc939b54b2382dab1781528067751c399ff00",
+   "support"
+  ],
+  "webstorage/symbol-props.window.js": [
+   "61dd8f83dc4f5ba36aebe9f61253b2763346d36e",
+   "testharness"
+  ],
   "webusb/META.yml": [
    "546094855e50f17ef6d92f4bec412af644f155ad",
    "support"
diff --git a/third_party/blink/web_tests/external/wpt/animation-worklet/playback-rate.https.html b/third_party/blink/web_tests/external/wpt/animation-worklet/playback-rate.https.html
new file mode 100644
index 0000000..9c97581
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/animation-worklet/playback-rate.https.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>The playback rate of a worklet animation</title>
+<link rel="help" href="https://drafts.css-houdini.org/css-animationworklet/">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+'use strict';
+// Presence of playback rate adds FP operations to calculating start_time
+// and current_time of animations. That's why it's needed to increase FP error
+// for comparing times in these tests.
+window.assert_times_equal = (actual, expected, description) => {
+  assert_approx_equals(actual, expected, 0.002, description);
+};
+</script>
+<script src="/web-animations/testcommon.js"></script>
+<script src="common.js"></script>
+<body>
+<div id="log"></div>
+<script>
+'use strict';
+
+function InstantiateWorkletAnimation(test) {
+  const DURATION = 10000; // ms
+  const KEYFRAMES = { height : ['100px', '50px'] };
+  return new WorkletAnimation('passthrough', new KeyframeEffect(createDiv(test),
+        KEYFRAMES, DURATION), document.timeline);
+}
+
+promise_test(async t => {
+  await registerPassthroughAnimator();
+  const animation = InstantiateWorkletAnimation(t);
+
+  animation.playbackRate = 0.5;
+  animation.play();
+  assert_equals(animation.currentTime, 0,
+    'Zero current time is not affected by playbackRate.');
+}, 'Zero current time is not affected by playbackRate set while the animation is in idle state.');
+
+promise_test(async t => {
+  await registerPassthroughAnimator();
+  const animation = InstantiateWorkletAnimation(t);
+
+  animation.play();
+  animation.playbackRate = 0.5;
+  assert_equals(animation.currentTime, 0,
+    'Zero current time is not affected by playbackRate.');
+}, 'Zero current time is not affected by playbackRate set while the animation is in play-pending state.');
+
+promise_test(async t => {
+  await registerPassthroughAnimator();
+  const animation = InstantiateWorkletAnimation(t);
+  const playbackRate = 2;
+
+  animation.play();
+
+  await waitForNextFrame();
+
+  // Set playback rate while the animation is playing.
+  const prevCurrentTime = animation.currentTime;
+  animation.playbackRate = playbackRate;
+
+  assert_times_equal(animation.currentTime, prevCurrentTime,
+    'The current time should stay unaffected by setting playback rate.');
+}, 'Non zero current time is not affected by playbackRate set while the animation is in play state.');
+
+promise_test(async t => {
+  await registerPassthroughAnimator();
+  const animation = InstantiateWorkletAnimation(t);
+  const playbackRate = 2;
+
+  animation.play();
+
+  await waitForNextFrame();
+
+  // Set playback rate while the animation is playing
+  const prevCurrentTime = animation.currentTime;
+  const prevTimelineTime = document.timeline.currentTime;
+  animation.playbackRate = playbackRate;
+
+  // Play the animation some more.
+  await waitForNextFrame();
+
+  const currentTime = animation.currentTime;
+  const currentTimelineTime = document.timeline.currentTime;
+
+  assert_times_equal(currentTime - prevCurrentTime, (currentTimelineTime - prevTimelineTime) * playbackRate,
+    'The current time should increase two times faster than timeline.');
+
+}, 'The playback rate affects the rate of progress of the current time.');
+
+promise_test(async t => {
+  await registerPassthroughAnimator();
+  const animation = InstantiateWorkletAnimation(t);;
+  const playbackRate = 2;
+
+  // Set playback rate while the animation is in 'idle' state.
+  animation.playbackRate = playbackRate;
+  animation.play();
+  const prevTimelineTime = document.timeline.currentTime;
+
+  await waitForNextFrame();
+
+  const currentTime = animation.currentTime;
+  const timelineTime = document.timeline.currentTime;
+  assert_times_equal(currentTime, (timelineTime - prevTimelineTime) * playbackRate,
+    'The current time should increase two times faster than timeline.');
+}, 'The playback rate set before the animation started playing affects the ' +
+   'rate of progress of the current time');
+
+promise_test(async t => {
+  await registerPassthroughAnimator();
+  const timing = { duration: 100,
+                   easing: 'linear',
+                   fill: 'none',
+                   iterations: 1
+                 };
+  const target = createDiv(t);
+  const keyframeEffect = new KeyframeEffect(target, { opacity: [0, 1] }, timing);
+  const animation = new WorkletAnimation('passthrough', keyframeEffect, document.timeline);
+  const playbackRate = 2;
+
+  animation.play();
+  animation.playbackRate = playbackRate;
+
+  await waitForNextFrame();
+
+  assert_times_equal(keyframeEffect.getComputedTiming().localTime, animation.currentTime,
+    'When playback rate is set on WorkletAnimation, the underlying effect\'s timing should be properly updated.');
+
+  assert_approx_equals(Number(getComputedStyle(target).opacity),
+    animation.currentTime / 100, 0.001,
+    'When playback rate is set on WorkletAnimation, the underlying effect should produce correct visual result.');
+
+}, 'When playback rate is updated, the underlying effect is properly updated ' +
+   'with the current time of its WorkletAnimation and produces correct ' +
+   'visual result.');
+
+</script>
+</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/echo_client_hints_received.py b/third_party/blink/web_tests/external/wpt/client-hints/echo_client_hints_received.py
index 8f2ccaa..f7debdb 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/echo_client_hints_received.py
+++ b/third_party/blink/web_tests/external/wpt/client-hints/echo_client_hints_received.py
@@ -18,3 +18,5 @@
             response.headers.set("downlink-received", request.headers.get("downlink"))
     if "ect" in request.headers:
             response.headers.set("ect-received", request.headers.get("ect"))
+    if "Sec-CH-Lang" in request.headers:
+            response.headers.set("lang-received", request.headers.get("Sec-CH-Lang"))
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.http.html b/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.http.html
index 2bdced2..03c5799 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.http.html
+++ b/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.http.html
@@ -1,5 +1,5 @@
 <html>
-<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width, Device-Memory, rtt, downlink, ect">
+<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width, Device-Memory, rtt, downlink, ect, lang">
 <title>Accept-CH http-equiv insecure transport test</title>
 <body>
 <script src="/resources/testharness.js"></script>
@@ -27,6 +27,7 @@
     assert_false(r.headers.has("rtt-received"), "rtt-received");
     assert_false(r.headers.has("downlink-received"), "downlink-received");
     assert_false(r.headers.has("ect-received"), "ect-received");
+    assert_false(r.headers.has("lang-received"), "lang-received");
   });
 }, "Accept-CH http-equiv test over insecure transport");
 
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.https.html b/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.https.html
index 3e4d638..74eea34 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.https.html
+++ b/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.https.html
@@ -1,5 +1,5 @@
 <html>
-<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width, Device-Memory, rtt, downlink, ect">
+<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width, Device-Memory, rtt, downlink, ect, lang">
 <title>Accept-CH http-equiv cross-navigation test</title>
 <body>
 <script src="/resources/testharness.js"></script>
@@ -39,9 +39,9 @@
 // not persisted for the origin.
 window.open("resources/do_not_expect_client_hints_headers.html");
 async_test(t => {
-window.addEventListener('message', function(event) {
-  t.done();
-})
+  window.addEventListener('message', t.step_func_done(e => {
+    assert_equals(e.data, 'PASS');
+  }));
 }, "Loading of resources/do_not_expect_client_hints_headers.html did not finish.");
 
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.sub.https.html b/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.sub.https.html
index 459b00e..16617dc 100644
--- a/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.sub.https.html
+++ b/third_party/blink/web_tests/external/wpt/client-hints/http_equiv_accept_ch.tentative.sub.https.html
@@ -1,5 +1,5 @@
 <html>
-<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width, Device-Memory, rtt, downlink, ect">
+<meta http-equiv="Accept-CH" content="DPR, Width, Viewport-Width, Device-Memory, rtt, downlink, ect, lang">
 <title>Accept-CH http-equiv same-origin and cross-origin test</title>
 <body>
 <script src="/resources/testharness.js"></script>
@@ -38,6 +38,8 @@
 
     assert_in_array(r.headers.get("ect-received"), ["slow-2g", "2g",
           "3g", "4g"], 'ect-received is unexpected');
+
+    assert_true(r.headers.has("lang-received"), "lang-received");
   });
 }, "Same origin Accept-CH http-equiv test");
 
@@ -52,6 +54,7 @@
     assert_false(r.headers.has("rtt-received"), "rtt-received");
     assert_false(r.headers.has("downlink-received"), "downlink-received");
     assert_false(r.headers.has("ect-received"), "ect-received");
+    assert_false(r.headers.has("lang-received"), "lang-received");
   });
 }, "Cross-Origin Accept-CH http-equiv test");
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-012.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-012.html
new file mode 100644
index 0000000..a193c29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-height-flex-items-012.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<title>CSS Flexbox: Combining 100% heights with min-height: auto should not lead to overflow</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=927066" />
+
+<style>
+.flexbox {
+    display: flex;
+}
+
+.column {
+    flex-flow: column;
+}
+
+.flexbox span {
+    height: 100%;
+    background: orange;
+    display: block;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+<body onload="checkLayout('.flexbox')">
+<div id=log></div>
+
+<div class="flexbox column" style="height: 100px; width: 100px; background: green">
+    <div style="height: 10px; flex: 0.1;" data-expected-height="10"></div>
+    <div style="height: 100%; display: flex; background: teal; flex: 0.9;" data-expected-height="90">
+        <div style="height: 100%"></div>
+    </div>
+</div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/grid-model/grid-button-001.html b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-model/grid-button-001.html
new file mode 100644
index 0000000..b84c10fc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/grid-model/grid-button-001.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<title>Grid items work inside a button</title>
+<meta name="assert" content="When a button is set to display: grid, its children should flow into its grid cells">
+<link rel="author" title="Bryan Robinson" href="bryanlrobinson@gmail.com">
+<link rel="help" href="https://drafts.csswg.org/css-grid-1/#grid-containers">
+<style>
+  .grid { display: grid; grid-template-columns: 100px 200px; border: 2px solid purple; box-sizing: border-box; }
+  span { border: 1px dashed green; box-sizing: border-box; }
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/check-layout-th.js"></script>
+
+<button class="grid" ><span class="item1" data-expected-width="100">item 1</span> <span class="item2" data-expected-width="200">item 2</span></button>
+
+<script>
+  checkLayout("[data-expected-width]")
+</script>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/grid-fit-content-percentage.html b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/grid-fit-content-percentage.html
new file mode 100644
index 0000000..ab555024
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-grid/layout-algorithm/grid-fit-content-percentage.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS Grid Layout Test: indefinite percentage in fit-content()</title>
+<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/css-grid/#track-sizes" title="7.2.1. Track Sizes">
+<meta name="assert" content="Checks that an indefinite percentage in fit-content lets the grid container grow enough to contain the max-content contribution of its grid items.">
+<style>
+.container {
+  width: 200px;
+  margin-top: 10px;
+}
+.grid {
+  display: inline-grid;
+  background: blue;
+}
+.item {
+  background: orange;
+}
+.item::before, .item::after {
+  content: '';
+  float: left;
+  width: 50px;
+  height: 50px;
+}
+</style>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id="log"></div>
+
+<script>
+"use strict";
+function clamp(value, min, max) {
+  return Math.max(min, Math.min(max, value));
+}
+const minContent = 50;
+const maxContent = 100;
+for (const percentage of [0, 50, 75, 100, 150]) {
+  const container = document.createElement("div");
+  container.className = "container";
+  document.body.appendChild(container);
+  const grid = document.createElement("div");
+  grid.className = "grid";
+  grid.style.gridTemplateColumns = `fit-content(${percentage}%)`;
+  container.appendChild(grid);
+  const item = document.createElement("div");
+  item.className = "item";
+  grid.appendChild(item);
+  test(function() {
+    const colSize = clamp(percentage * maxContent / 100, minContent, maxContent);
+    const height = colSize < maxContent ? maxContent : minContent;
+    assert_equals(item.offsetWidth, colSize, "Grid item width");
+    assert_equals(item.offsetHeight, height, "Grid item height");
+    assert_equals(grid.offsetWidth, maxContent, "Grid container width");
+    assert_equals(grid.offsetHeight, height, "Grid container height");
+    assert_equals(getComputedStyle(grid).gridTemplateColumns, colSize + "px",
+                  "Grid column size");
+  }, `fit-content(${percentage}%)`);
+}
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-008-ref.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-008-ref.html
new file mode 100644
index 0000000..ddc0b40
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-008-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test Reference: Test a bidi-override multi-column container with a dir=rtl column-span:all child</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+
+  <style>
+  body {
+    column-count: 1;
+    column-rule: 6px solid;
+    width: 400px;
+    outline: 1px solid black;
+  }
+  h3 {
+    /* "column-count: 1" makes this behave like a real spanner. */
+    outline: 1px solid blue;
+  }
+  </style>
+
+  <body>
+    <div>block1</div>
+    <h3 dir="rtl">spanner</h3>
+    <div>block2</div>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-008.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-008.html
new file mode 100644
index 0000000..82a2483
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-008.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test: Test a bidi-override multi-column container with a dir=rtl column-span:all child</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-span">
+  <link rel="match" href="multicol-span-all-008-ref.html">
+  <meta name="assert" content="This test checks the page is rendered correctly for a bidi-override multi-column container with a dir=rtl column-span:all child.">
+
+  <style>
+  article {
+    column-count: 3;
+    column-rule: 6px solid;
+    width: 400px;
+    outline: 1px solid black;
+    unicode-bidi: bidi-override; /* Needed to trigger bidi resolution. */
+  }
+  h3 {
+    column-span: all;
+    outline: 1px solid blue;
+  }
+  </style>
+
+  <article>
+    <div>block1</div>
+    <h3 dir="rtl">spanner</h3>
+    <div>block2</div>
+  </article>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-011.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-011.html
new file mode 100644
index 0000000..058ca6c3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-011.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test: Insert a block containing a spanner kid. The spanner kid should correctly span across all columns</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-span">
+  <link rel="match" href="multicol-span-all-dynamic-add-003-ref.html">
+  <meta name="assert" content="This test checks that an inserted block containing 'column-span' element should be rendered correctly.">
+
+  <script>
+  function runTest() {
+    document.body.offsetHeight;
+
+    // Create a subtree like the following, and insert it into column as the
+    // first child.
+    // <div>
+    //   block1
+    //   <h3>spanner</h3>
+    // </div>
+    var spanner = document.createElement("h3");
+    var spannerText = document.createTextNode("spanner");
+    spanner.appendChild(spannerText);
+
+    var block1 = document.createElement("div");
+    var block1Text = document.createTextNode("block1");
+    block1.appendChild(block1Text)
+    block1.appendChild(spanner);
+
+    var column = document.getElementById("column");
+    column.insertBefore(block1, column.children[0]);
+
+    document.documentElement.removeAttribute("class");
+  }
+  </script>
+
+  <style>
+  #column {
+    column-count: 3;
+    column-rule: 6px solid;
+    width: 400px;
+    outline: 1px solid black;
+  }
+  h3 {
+    column-span: all;
+    outline: 1px solid blue;
+  }
+  </style>
+
+  <body onload="runTest();">
+    <article id="column">
+      <div>block2</div>
+    </article>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-012-ref.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-012-ref.html
new file mode 100644
index 0000000..61b45cc6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-012-ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test Reference: Append a block containing a spanner kid. The spanner kid should correctly span across all columns</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+
+  <style>
+  #column {
+    column-count: 3;
+    column-rule: 6px solid;
+    width: 400px;
+    outline: 1px solid black;
+  }
+  h3 {
+    column-span: all;
+    outline: 1px solid blue;
+  }
+  </style>
+
+  <body>
+    <article id="column">
+      <div>block1
+        <div>
+          <h3>spanner</h3>
+          block2
+        </div>
+      </div>
+    </article>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-012.html b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-012.html
new file mode 100644
index 0000000..7e152af1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-multicol/multicol-span-all-dynamic-add-012.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test: Append a block containing a spanner kid. The spanner kid should correctly span across all columns</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-span">
+  <link rel="match" href="multicol-span-all-dynamic-add-012-ref.html">
+  <meta name="assert" content="This test checks that an appended block containing 'column-span' element should be rendered correctly.">
+
+  <script>
+  function runTest() {
+    document.body.offsetHeight;
+
+    // Create a subtree like the following, and append it to block1.
+    // <div>
+    //   <h3>spanner</h3>
+    //   block2
+    // </div>
+    var spanner = document.createElement("h3");
+    var spannerText = document.createTextNode("spanner");
+    spanner.appendChild(spannerText);
+
+    var block2 = document.createElement("div");
+    var block2Text = document.createTextNode("block2");
+    block2.appendChild(spanner);
+    block2.appendChild(block2Text)
+
+    var block1 = document.getElementById("block1");
+    block1.appendChild(block2);
+
+    document.documentElement.removeAttribute("class");
+  }
+  </script>
+
+  <style>
+  #column {
+    column-count: 3;
+    column-rule: 6px solid;
+    width: 400px;
+    outline: 1px solid black;
+  }
+  h3 {
+    column-span: all;
+    outline: 1px solid blue;
+  }
+  </style>
+
+  <body onload="runTest();">
+    <article id="column">
+      <div id="block1">block1</div>
+    </article>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-001.html b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-001.html
new file mode 100644
index 0000000..592e5d2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-position/position-absolute-crash-chrome-001.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html>
+<head>
+  <title>Chrome crash with inline </title>
+  <link rel="help" href="https://www.w3.org/TR/css-position-3/#def-cb">
+  <link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=928224">
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script>test(()=>{})</script>
+<style>
+  a {
+    position: relative;
+  }
+  a:before {
+    content: "foo";
+    position: absolute;
+    background: green;
+  }
+</style>
+</head>
+<body>
+  <div>
+    <li>
+      <a href="dummy">success if does not crash</a>
+    </li>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-syntax/urange-parsing-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-syntax/urange-parsing-expected.txt
new file mode 100644
index 0000000..96bfb2fd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-syntax/urange-parsing-expected.txt
@@ -0,0 +1,96 @@
+This is a testharness.js-based test.
+Found 92 tests; 77 PASS, 15 FAIL, 0 TIMEOUT, 0 NOTRUN.
+FAIL "u/**/+/**/a/**/?" => "U+A0-AF" assert_equals: expected "U+A0-AF" but got "U+1357"
+PASS "u+abc" => "U+ABC"
+PASS "U+abc" => "U+ABC"
+PASS "u+ABC" => "U+ABC"
+PASS "U+ABC" => "U+ABC"
+PASS "u+AbC" => "U+ABC"
+PASS "u+efg" is invalid
+PASS "u+ abc" is invalid
+PASS "u +abc" is invalid
+PASS "u + abc" is invalid
+PASS "U + a b c" is invalid
+PASS "u+a" => "U+A"
+PASS "u+aa" => "U+AA"
+PASS "u+aaa" => "U+AAA"
+PASS "u+aaaa" => "U+AAAA"
+PASS "u+aaaaa" => "U+AAAAA"
+PASS "u+aaaaaaa" is invalid
+PASS "u+a?" => "U+A0-AF"
+PASS "u+a??" => "U+A00-AFF"
+PASS "u+a???" => "U+A000-AFFF"
+PASS "u+a????" => "U+A0000-AFFFF"
+PASS "u+aaaaaa?" is invalid
+PASS "u+aaaaa??" is invalid
+PASS "u+aaaa???" is invalid
+PASS "u+aaa????" is invalid
+PASS "u+aa?????" is invalid
+PASS "u+a??????" is invalid
+PASS "u+a?a" is invalid
+FAIL "u+aaaaaa" is invalid assert_equals: expected "U+1357" but got "U+AAAAAA"
+FAIL "u+a?????" is invalid assert_equals: expected "U+1357" but got "U+A00000-AFFFFF"
+FAIL "u/**/+0a/**/?" => "U+A0-AF" assert_equals: expected "U+A0-AF" but got "U+1357"
+PASS "u+0a" => "U+A"
+PASS "U+0a0" => "U+A0"
+PASS "u+0aaaaa" => "U+AAAAA"
+PASS "u+0aaaaaa" is invalid
+PASS "u+0a0000" => "U+A0000"
+PASS "u+0a00000" is invalid
+PASS "u+0aaaaa0" is invalid
+PASS "u+00000a" => "U+A"
+PASS "u+00000aa" is invalid
+PASS "u+00000a0" is invalid
+PASS "u+000000a" is invalid
+PASS "u+0a????" => "U+A0000-AFFFF"
+PASS "u+0a?????" is invalid
+PASS "u+00a????" is invalid
+FAIL "u+22222a" is invalid assert_equals: expected "U+1357" but got "U+22222A"
+PASS "u+1e9a" => "U+1E9A"
+FAIL "u/**/+0/**/?" => "U+0-F" assert_equals: expected "U+0-F" but got "U+1357"
+PASS "u/**/0" is invalid
+PASS "u+0" => "U+0"
+PASS "u+00" => "U+0"
+PASS "u+000" => "U+0"
+PASS "u+0000" => "U+0"
+PASS "u+00000" => "U+0"
+PASS "u+000000" => "U+0"
+PASS "u+0000000" is invalid
+PASS "u+00000?" => "U+0-F"
+PASS "u+0?????" => "U+0-FFFFF"
+PASS "u+0?a" is invalid
+PASS "u+000000?" is invalid
+PASS "u+00000??" is invalid
+PASS "u+0??????" is invalid
+PASS "u+1e3" => "U+1E3"
+PASS "u+1e-20" => "U+1E-20"
+FAIL "u+222222" is invalid assert_equals: expected "U+1357" but got "U+222222"
+FAIL "u+2?????" is invalid assert_equals: expected "U+1357" but got "U+200000-2FFFFF"
+FAIL "u/**/+0/**/-0a" => "U+0-A" assert_equals: expected "U+0-A" but got "U+1357"
+PASS "u+0-0a" => "U+0-A"
+PASS "u+000000-0aaaaa" => "U+0-AAAAA"
+PASS "u+0000000-0a" is invalid
+PASS "u+0-0aaaaaa" is invalid
+PASS "u+0-000000a" is invalid
+PASS "u+0+0a" is invalid
+PASS "u+0?-0a" is invalid
+PASS "u+0-0a?" is invalid
+FAIL "u+222222-22222a" is invalid assert_equals: expected "U+1357" but got "U+222222-22222A"
+FAIL "u/**/+0/**/-1" => "U+0-1" assert_equals: expected "U+0-1" but got "U+1357"
+PASS "u+0-1" => "U+0-1"
+PASS "u-0-1" is invalid
+PASS "u-0+1" is invalid
+PASS "u+0+1" is invalid
+PASS "u+000000-000001" => "U+0-1"
+PASS "u+0000000-1" is invalid
+PASS "u+0-0000001" is invalid
+FAIL "u+0-222222" is invalid assert_equals: expected "U+1357" but got "U+0-222222"
+FAIL "u/**/+/**/?" => "U+0-F" assert_equals: expected "U+0-F" but got "U+1357"
+PASS "u+?" => "U+0-F"
+PASS "u+?????" => "u+0-FFFFF"
+PASS "u+???????" is invalid
+PASS "u+?a" is invalid
+FAIL "u+??????" is invalid assert_equals: expected "U+1357" but got "U+0-FFFFFF"
+FAIL u+a is a valid selector assert_equals: expected "u + a" but got ".error"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-syntax/urange-parsing.html b/third_party/blink/web_tests/external/wpt/css/css-syntax/urange-parsing.html
new file mode 100644
index 0000000..0a69faa3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-syntax/urange-parsing.html
@@ -0,0 +1,173 @@
+<!doctype html>
+<title>Urange Parsing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+
+@font-face {
+    font-family: foo;
+    src: url(http://example.com);
+}
+
+.error {}
+
+</style>
+
+<meta name=author title="Tab Atkins-Bittner">
+<link rel=help href="https://drafts.csswg.org/css-syntax/#urange-syntax">
+
+<script>
+
+function testUrange(input, expected) {
+    test(()=>{
+        const rule = document.styleSheets[0].cssRules[0];
+        rule.style.unicodeRange = "U+1357";
+        rule.style.unicodeRange = input;
+        assert_equals(rule.style.unicodeRange.toUpperCase(), expected.toUpperCase());
+    }, `"${input}" => "${expected}"`)
+}
+function testInvalidUrange(input) {
+    test(()=>{
+        const rule = document.styleSheets[0].cssRules[0];
+        rule.style.unicodeRange = "U+1357";
+        rule.style.unicodeRange = input;
+        assert_equals(rule.style.unicodeRange.toUpperCase(), "U+1357");
+    }, `"${input}" is invalid`);
+}
+
+/* First exercise all the clauses individually */
+//<urange> =
+//  u '+' <ident-token> '?'* |
+/* comments can go between tokens */
+testUrange("u/**/+/**/a/**/?", "U+A0-AF");
+/* capitalization doesn't matter */
+testUrange("u+abc", "U+ABC");
+testUrange("U+abc", "U+ABC");
+testUrange("u+ABC", "U+ABC");
+testUrange("U+ABC", "U+ABC");
+testUrange("u+AbC", "U+ABC");
+/* only hex */
+testInvalidUrange("u+efg");
+/* no spacing */
+testInvalidUrange("u+ abc");
+testInvalidUrange("u +abc");
+testInvalidUrange("u + abc");
+testInvalidUrange("U + a b c");
+/* 1-6 characters */
+testUrange("u+a", "U+A");
+testUrange("u+aa", "U+AA");
+testUrange("u+aaa", "U+AAA");
+testUrange("u+aaaa", "U+AAAA");
+testUrange("u+aaaaa", "U+AAAAA");
+testInvalidUrange("u+aaaaaaa");
+/* Or ? at the end, still up to 6 */
+testUrange("u+a?", "U+A0-AF");
+testUrange("u+a??", "U+A00-AFF");
+testUrange("u+a???", "U+A000-AFFF");
+testUrange("u+a????", "U+A0000-AFFFF");
+testInvalidUrange("u+aaaaaa?");
+testInvalidUrange("u+aaaaa??");
+testInvalidUrange("u+aaaa???");
+testInvalidUrange("u+aaa????");
+testInvalidUrange("u+aa?????");
+testInvalidUrange("u+a??????");
+/* no characters after ? */
+testInvalidUrange("u+a?a");
+// Too large!
+testInvalidUrange("u+aaaaaa");
+testInvalidUrange("u+a?????");
+
+//  u <dimension-token> '?'* |
+testUrange("u/**/+0a/**/?", "U+A0-AF");
+testUrange("u+0a", "U+A");
+testUrange("U+0a0", "U+A0");
+testUrange("u+0aaaaa", "U+AAAAA");
+testInvalidUrange("u+0aaaaaa");
+testUrange("u+0a0000", "U+A0000");
+testInvalidUrange("u+0a00000");
+testInvalidUrange("u+0aaaaa0");
+testUrange("u+00000a", "U+A");
+testInvalidUrange("u+00000aa");
+testInvalidUrange("u+00000a0")
+testInvalidUrange("u+000000a");
+testUrange("u+0a????", "U+A0000-AFFFF");
+testInvalidUrange("u+0a?????");
+testInvalidUrange("u+00a????");
+// Too large!
+testInvalidUrange("u+22222a");
+// Scinot!
+testUrange("u+1e9a", "U+1E9A");
+
+//  u <number-token> '?'* |
+testUrange("u/**/+0/**/?", "U+0-F");
+testInvalidUrange("u/**/0");
+testUrange("u+0", "U+0");
+testUrange("u+00", "U+0");
+testUrange("u+000", "U+0");
+testUrange("u+0000", "U+0");
+testUrange("u+00000", "U+0");
+testUrange("u+000000", "U+0");
+testInvalidUrange("u+0000000");
+testUrange("u+00000?", "U+0-F");
+testUrange("u+0?????", "U+0-FFFFF");
+testInvalidUrange("u+0?a");
+testInvalidUrange("u+000000?");
+testInvalidUrange("u+00000??");
+testInvalidUrange("u+0??????");
+// Scinot!
+testUrange("u+1e3", "U+1E3");
+testUrange("u+1e-20", "U+1E-20");
+// Too large!
+testInvalidUrange("u+222222");
+testInvalidUrange("u+2?????");
+
+//  u <number-token> <dimension-token> |
+testUrange("u/**/+0/**/-0a", "U+0-A");
+testUrange("u+0-0a", "U+0-A");
+testUrange("u+000000-0aaaaa", "U+0-AAAAA");
+testInvalidUrange("u+0000000-0a");
+testInvalidUrange("u+0-0aaaaaa");
+testInvalidUrange("u+0-000000a");
+testInvalidUrange("u+0+0a");
+testInvalidUrange("u+0?-0a");
+testInvalidUrange("u+0-0a?");
+// Too large!
+testInvalidUrange("u+222222-22222a");
+
+//  u <number-token> <number-token> |
+testUrange("u/**/+0/**/-1", "U+0-1");
+testUrange("u+0-1", "U+0-1");
+testInvalidUrange("u-0-1");
+testInvalidUrange("u-0+1");
+testInvalidUrange("u+0+1");
+testUrange("u+000000-000001", "U+0-1");
+testInvalidUrange("u+0000000-1");
+testInvalidUrange("u+0-0000001");
+// Too large!
+testInvalidUrange("u+0-222222");
+
+//  u '+' '?'+
+testUrange("u/**/+/**/?", "U+0-F");
+testUrange("u+?", "U+0-F");
+testUrange("u+?????", "u+0-FFFFF");
+testInvalidUrange("u+???????");
+testInvalidUrange("u+?a");
+// U+FFFFFF is too large!
+testInvalidUrange("u+??????");
+
+
+/* Finally, verify that u+a is properly parsed
+   as IDENT(u) DELIM(+) IDENT(a) in other contexts */
+
+test(()=>{
+    const rule = document.styleSheets[0].cssRules[1];
+    // Establish that it works with whitespace...
+    rule.selectorText = "u + a";
+    assert_equals(rule.selectorText, "u + a");
+    // And then again without...
+    rule.selectorText = ".error";
+    rule.selectorText = "u+a";
+    assert_equals(rule.selectorText, "u + a");
+}, "u+a is a valid selector");
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/subpixel-table-cell-height-001-ref.html b/third_party/blink/web_tests/external/wpt/css/css-tables/subpixel-table-cell-height-001-ref.html
new file mode 100644
index 0000000..3b6297f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/subpixel-table-cell-height-001-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<style>
+div {
+  border: 1px solid blue;
+  width: 120px;
+  height: 37.4px;
+}
+</style>
+
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-tables/subpixel-table-cell-height-001.html b/third_party/blink/web_tests/external/wpt/css/css-tables/subpixel-table-cell-height-001.html
new file mode 100644
index 0000000..581efa0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-tables/subpixel-table-cell-height-001.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/css-values-3/#calc-notation">
+<link rel="match" href="subpixel-table-cell-height-001-ref.html">
+<meta name="flags" content="" />
+<meta name="assert" content="When a cell has a specified calc height and a block box has the same specified calc height, their actual heights should match." />
+
+<style>
+td {
+  border: 1px solid blue;
+  width: 120px;
+  height: 37.4px;
+  padding: 0px;
+}
+</style>
+
+<table style="border-spacing:0;">
+  <tr>
+    <td></td>
+  </tr>
+  <tr>
+    <td></td>
+  </tr>
+  <tr>
+    <td></td>
+  </tr>
+  <tr>
+    <td></td>
+  </tr>
+  <tr>
+    <td></td>
+  </tr>
+  <tr>
+    <td></td>
+  </tr>
+  <tr>
+    <td></td>
+  </tr>
+  <tr>
+    <td></td>
+  </tr>
+  <tr>
+    <td></td>
+  </tr>
+</table>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/event-dispatch.tentative-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-transitions/event-dispatch.tentative-expected.txt
new file mode 100644
index 0000000..ca185ceb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/event-dispatch.tentative-expected.txt
@@ -0,0 +1,29 @@
+This is a testharness.js-based test.
+FAIL Idle -> Pending or Before assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Idle -> Before assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Idle or Pending -> Active assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Idle or Pending -> After assert_equals: Expected transitionrun event, but got transitionend event instead expected "transitionrun" but got "transitionend"
+FAIL Before -> Idle (display: none) assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Before -> Idle (Animation.timeline = null) assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Before -> Active assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Before -> After assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Active -> Idle, no delay (display: none) assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Active -> Idle, no delay (Animation.timeline = null) assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Active -> Idle, with positive delay (display: none) assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Active -> Idle, with positive delay (Animation.timeline = null) assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Active -> Idle, with negative delay (display: none) assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Active -> Idle, with negative delay (Animation.timeline = null) assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Active -> Before assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Active -> After assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL After -> Before assert_equals: Expected transitionrun event, but got transitionend event instead expected "transitionrun" but got "transitionend"
+FAIL After -> Active assert_equals: Expected transitionrun event, but got transitionend event instead expected "transitionrun" but got "transitionend"
+FAIL Calculating the interval start and end time with negative start delay. assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Calculating the interval start and end time with negative end delay. assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Call Animation.cancel after canceling transition. assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Restart transition after canceling transition immediately assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Call Animation.cancel after restarting transition immediately assert_true: Timed out waiting for transitionrun expected true got false
+FAIL Set timeline and play transition after clear the timeline assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Set null target effect after canceling the transition assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+FAIL Cancel the transition after clearing the target effect assert_true: Timed out waiting for transitionrun, transitionstart expected true got false
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/css/css-transitions/event-dispatch.tentative.html b/third_party/blink/web_tests/external/wpt/css/css-transitions/event-dispatch.tentative.html
index 81bba60..cd5b6ab 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-transitions/event-dispatch.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-transitions/event-dispatch.tentative.html
@@ -9,12 +9,20 @@
 <script>
 'use strict';
 
+// All transition events should be received on the next animation frame. If
+// two animation frames pass before receiving the expected events then we
+// can immediately fail the current test.
+const transitionEventsTimeout = () => {
+  return waitForAnimationFrames(2);
+};
+
 const setupTransition = (t, transitionStyle) => {
   const div = addDiv(t, { style: 'transition: ' + transitionStyle });
   const watcher = new EventWatcher(t, div, [ 'transitionrun',
                                              'transitionstart',
                                              'transitionend',
-                                             'transitioncancel' ]);
+                                             'transitioncancel' ],
+                                   transitionEventsTimeout);
   getComputedStyle(div).marginLeft;
 
   div.style.marginLeft = '100px';
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/outline-021.html b/third_party/blink/web_tests/external/wpt/css/css-ui/outline-021.html
new file mode 100644
index 0000000..02e96ea
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/outline-021.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>CSS-UI test: outline works on button inputs</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel=help href="https://drafts.csswg.org/css-ui/#outline-props">
+<link rel="mismatch" href="./reference/outline-021-notref.html">
+
+<input type="submit" style="outline: 10px solid blue">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-ui/reference/outline-021-notref.html b/third_party/blink/web_tests/external/wpt/css/css-ui/reference/outline-021-notref.html
new file mode 100644
index 0000000..eb074ed
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-ui/reference/outline-021-notref.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>CSS test reference</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+
+<input type="submit" style="outline: none">
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-001.html b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-001.html
new file mode 100644
index 0000000..61ca931
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-001.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: invalid angle units</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#angles">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+
+  <meta name="flags" content="invalid">
+
+  <style>
+  div
+    {
+      height: 100px;
+      width: 100px;
+    }
+
+  div#test-overlapping-green
+    {
+      background-image: linear-gradient(green, green);
+      background-image: linear-gradient(90degree, red, red);   /* invalid; 90deg is valid */
+      background-image: linear-gradient(100gradian, red, red); /* invalid; 100grad is valid */
+      background-image: linear-gradient(1.57radian, red, red); /* invalid; 1.57rad is valid */
+      background-image: linear-gradient(0.25turns, red, red);  /* invalid; 0.25turn is valid */
+    }
+
+  div#reference-overlapped-red
+    {
+      background-color: red;
+      bottom: 100px;
+      position: relative;
+      z-index: -1;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div id="test-overlapping-green"></div>
+
+  <div id="reference-overlapped-red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-002.html b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-002.html
new file mode 100644
index 0000000..ff9cdca
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-002.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: degree angle unit with mixed case</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#angles">
+  <link rel="help" href="https://www.w3.org/TR/CSS22/syndata.html#characters">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+
+  <meta content="This test checks that 'deg' angle unit is case-insensitive." name="assert">
+  <meta name="flags" content="">
+
+  <style>
+  div
+    {
+      height: 100px;
+      width: 100px;
+    }
+
+  div#test-overlapping-green
+    {
+      background-color: red;
+      background-image: linear-gradient(90DeG, green, green);
+    }
+
+  div#reference-overlapped-red
+    {
+      background-color: red;
+      bottom: 100px;
+      position: relative;
+      z-index: -1;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div id="test-overlapping-green"></div>
+
+  <div id="reference-overlapped-red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-003.html b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-003.html
new file mode 100644
index 0000000..786d5fa
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-003.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: grad angle unit with mixed case</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#angles">
+  <link rel="help" href="https://www.w3.org/TR/CSS22/syndata.html#characters">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+
+  <meta content="This test checks that 'grad' angle unit is case-insensitive." name="assert">
+  <meta name="flags" content="">
+
+  <style>
+  div
+    {
+      height: 100px;
+      width: 100px;
+    }
+
+  div#test-overlapping-green
+    {
+      background-color: red;
+      background-image: linear-gradient(100gRaD, green, green);
+    }
+
+  div#reference-overlapped-red
+    {
+      background-color: red;
+      bottom: 100px;
+      position: relative;
+      z-index: -1;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div id="test-overlapping-green"></div>
+
+  <div id="reference-overlapped-red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-004.html b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-004.html
new file mode 100644
index 0000000..34722a3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-004.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: rad angle unit with mixed case</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#angles">
+  <link rel="help" href="https://www.w3.org/TR/CSS22/syndata.html#characters">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+
+  <meta content="This test checks that 'rad' angle unit is case-insensitive." name="assert">
+  <meta name="flags" content="">
+
+  <style>
+  div
+    {
+      height: 100px;
+      width: 100px;
+    }
+
+  div#test-overlapping-green
+    {
+      background-color: red;
+      background-image: linear-gradient(1.57rAd, green, green);
+    }
+
+  div#reference-overlapped-red
+    {
+      background-color: red;
+      bottom: 100px;
+      position: relative;
+      z-index: -1;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div id="test-overlapping-green"></div>
+
+  <div id="reference-overlapped-red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-005.html b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-005.html
new file mode 100644
index 0000000..625952a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/angle-units-005.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: turn angle unit with mixed case</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#angles">
+  <link rel="help" href="https://www.w3.org/TR/CSS22/syndata.html#characters">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+
+  <meta content="This test checks that 'turn' angle unit is case-insensitive." name="assert">
+  <meta name="flags" content="">
+
+  <style>
+  div
+    {
+      height: 100px;
+      width: 100px;
+    }
+
+  div#test-overlapping-green
+    {
+      background-color: red;
+      background-image: linear-gradient(0.25tUrN, green, green);
+    }
+
+  div#reference-overlapped-red
+    {
+      background-color: red;
+      bottom: 100px;
+      position: relative;
+      z-index: -1;
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div id="test-overlapping-green"></div>
+
+  <div id="reference-overlapped-red"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/attr-invalid-type-008.html b/third_party/blink/web_tests/external/wpt/css/css-values/attr-invalid-type-008.html
new file mode 100644
index 0000000..a76be44
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/attr-invalid-type-008.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: attr() function with valid and invalid data types</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link href="http://www.w3.org/TR/css3-values/#attr-notation" rel="help">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+
+  <meta name="flags" content="invalid">
+  <meta content="When the data type of an attr() function is invalid or when the fallback value does not match the data type, then the attr() function generates an invalid declaration." name="assert">
+
+  <style>
+  div
+    {
+      height: 50px;
+      position: relative;
+      width: 50px;
+    }
+
+  div#valid1
+    {
+      background-color: green;
+      width: attr(attr-test-valid1 length);
+    }
+
+  div#invalid1
+    {
+      background-color: green;
+      width: 100px;
+      width: attr(attr-test-invalid1 number, 0);
+    }
+
+    /*
+    '0' can be both a number and a length. But here,
+    in this sub-test, the width CSS property requires
+    a length type and not a number type. So, the
+    attr() function generates an invalid declaration.
+    */
+
+  div#reference-overlapped-red
+    {
+      background-color: red;
+      bottom: 100px;
+      height: 100px;
+      width: 100px;
+      z-index: -1;
+    }
+
+  div#invalid2
+    {
+      background-color: red;
+      bottom: 100px;
+      width: 0px;
+      width: attr(attr-test-invalid2 length, 100);
+    }
+
+    /*
+    '100' is not a valid length value.
+    So, the attr() function generates an invalid
+    declaration.
+    */
+
+  div#invalid3
+    {
+      background-color: red;
+      bottom: 100px;
+      width: 0px;
+      width: attr(attr-test-invalid3 number, 100px);
+    }
+
+    /*
+    number type is not a valid length value.
+    So, the attr() function generates an invalid
+    declaration.
+    */
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div id="valid1" attr-test-valid1="100px"></div>
+
+  <div id="invalid1" attr-test-invalid1="foo"></div>
+
+  <div id="reference-overlapped-red"></div>
+
+  <div id="invalid2" attr-test-invalid2="bar"></div>
+
+  <div id="invalid3" attr-test-invalid3="baz"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-numbers-expected.txt b/third_party/blink/web_tests/external/wpt/css/css-values/calc-numbers-expected.txt
index e02b6612..d0e2635 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-values/calc-numbers-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-numbers-expected.txt
@@ -1,6 +1,6 @@
 This is a testharness.js-based test.
 PASS testing tab-size: calc(2 * 3)
-PASS testing tab-size: calc(2 * -4)
+FAIL testing tab-size: calc(2 * -4) assert_equals: calc(2 * -4) should compute to 0 expected "0" but got "12345"
 PASS testing opacity: calc(2 / 4)
 FAIL testing tab-size: calc(2 / 4) assert_equals: calc(2 / 4) should compute to 0.5 expected "0.5" but got "12345"
 PASS testing opacity: calc(2 / 4) * 1px
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-numbers.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-numbers.html
index 5c2c91d..995595b 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-values/calc-numbers.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-numbers.html
@@ -16,9 +16,10 @@
   <link rel="help" href="https://www.w3.org/TR/css-color-3/#transparency">
   <link rel="help" href="https://www.w3.org/TR/css-text-3/#tab-size-property">
   <link rel="help" href="https://www.w3.org/TR/css3-values/#calc-computed-value">
+  <link rel="help" href="https://www.w3.org/TR/css3-values/#calc-range">
 
   <meta name="flags" content="invalid">
-  <meta content="This test verifies how 12 calc() functions are computed for 'opacity' and 'tab-size'." name="assert">
+  <meta content="This test verifies how 11 calc() functions are computed for 'opacity' and 'tab-size'." name="assert">
 
   <script src="/resources/testharness.js"></script>
 
@@ -41,12 +42,11 @@
       elemTarget.style.setProperty(property_name, initial_value);
 
       /*
-      In exactly 9 out of the 12 sub-tests, the initial_value will
-      act as a fallback value because the calc() function in the
-      specified value generates an invalid value. Since we are
-      running 12 consecutive tests on the same element, then
-      it is necessary to 'reset' its property to an initial
-      value.
+      In exactly 6 out of the 11 sub-tests, the initial_value will
+      act as a fallback value because the specified value generates
+      an invalid value. Since we are running 11 consecutive tests
+      on the same element, then it is necessary to 'reset' its
+      property to an initial value.
       */
 
       elemTarget.style.setProperty(property_name, specified_value);
@@ -60,11 +60,21 @@
 
     verifyComputedStyle("tab-size", "initial", "calc(2 * 3)", "6", "testing tab-size: calc(2 * 3)");
 
-    verifyComputedStyle("tab-size", "12345", "calc(2 * -4)", "12345", "testing tab-size: calc(2 * -4)");
+    verifyComputedStyle("tab-size", "12345", "calc(2 * -4)", "0", "testing tab-size: calc(2 * -4)");
+    /*
+    an out-of-range value inside a calc() does not cause
+    the declaration to become invalid. The value resulting
+    from an expression must be clamped to the range
+    allowed in the target context.
+    https://www.w3.org/TR/css-values-3/#calc-range
+    */
 
     verifyComputedStyle("opacity", "initial", "calc(2 / 4)", "0.5", "testing opacity: calc(2 / 4)");
 
     verifyComputedStyle("tab-size", "12345", "calc(2 / 4)", "0.5", "testing tab-size: calc(2 / 4)");
+    /*
+    'tab-size' accepts <number> values.
+    */
 
     verifyComputedStyle("opacity", "0.9", "calc(2 / 4) * 1px", "0.9", "testing opacity: calc(2 / 4) * 1px");
 
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/calc-positive-fraction-001.html b/third_party/blink/web_tests/external/wpt/css/css-values/calc-positive-fraction-001.html
new file mode 100644
index 0000000..383b4af5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/calc-positive-fraction-001.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Test: calc() with positive fraction halfway between adjacent integers</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-4/#combine-integers">
+  <link rel="match" href="../reference/ref-filled-green-100px-square.xht">
+  <meta name="flags" content="">
+
+  <style>
+  div
+    {
+      height: 100px;
+      position: absolute;
+      width: 100px;
+    }
+
+  div#red-overlapped
+    {
+      background-color: red;
+      z-index: 2;
+    }
+
+  div#green-overlapping
+    {
+      background-color: green;
+      z-index: calc(3 / 2);
+      /*
+      should resolve to 'z-index: 2' since "values
+      halfway between adjacent integers rounded
+      towards positive infinity" and since
+      div#green-overlapping is last in document
+      tree order, then it should overlap
+      div#red-overlapped
+      */
+    }
+  </style>
+
+  <p>Test passes if there is a filled green square and <strong>no red</strong>.
+
+  <div id="red-overlapped"></div>
+
+  <div id="green-overlapping"></div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-008.html b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-008.html
new file mode 100644
index 0000000..67fe0754
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-008.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: ch unit in width (basic)</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#font-relative-lengths">
+  <link rel="match" href="reference/ch-unit-008-ref.html">
+
+  <meta name="flags" content="">
+
+  <style>
+  div
+    {
+      font-size: 80px; /* arbitrary font size */
+    }
+
+  div#test-blue
+    {
+      background-color: blue;
+      height: 1.8em;
+      width: 5ch;
+    }
+
+  div#reference-orange
+    {
+      background-color: orange;
+      color: orange;
+      float: left;
+      line-height: 1.8; /* arbitrary line-height */
+    }
+  </style>
+
+  <p>Test passes if there is a blue rectangle with the <strong>same width</strong> as an orange rectangle.
+
+  <div id="test-blue"></div>
+
+  <div id="reference-orange">00000</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-009.html b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-009.html
new file mode 100644
index 0000000..c6f5671
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-009.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: ch unit in height (basic)</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#font-relative-lengths">
+  <link rel="match" href="reference/ch-unit-009-ref.html">
+
+  <meta name="flags" content="">
+
+  <style>
+  div
+    {
+      float: left;
+      font-size: 80px; /* arbitrary font size */
+    }
+
+  div#test-blue
+    {
+      background-color: blue;
+      height: 5ch;
+      width: 1.8em;
+    }
+
+  div#reference-orange
+    {
+      background-color: orange;
+      color: orange;
+      line-height: 1.8; /* arbitrary line-height */
+      writing-mode: vertical-rl;
+    }
+  </style>
+
+  <p>Test passes if there is a blue rectangle with the <strong>same height</strong> as an orange rectangle.
+
+  <div id="test-blue"></div>
+
+  <div id="reference-orange">00000</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-010.html b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-010.html
new file mode 100644
index 0000000..bc3fdbc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-010.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: ch unit in height with 'text-orientation: mixed'</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#font-relative-lengths">
+  <link rel="match" href="reference/ch-unit-009-ref.html">
+
+  <meta name="flags" content="">
+  <meta name="assert" content="In this test, the ch unit is the advance width measure of the 0 (ZERO, U+0030) glyph.">
+
+  <style>
+  div
+    {
+      float: left;
+      font-size: 80px; /* arbitrary font size */
+      text-orientation: mixed;
+      writing-mode: vertical-rl;
+    }
+
+  div#test-blue
+    {
+      background-color: blue;
+      height: 5ch;
+      width: 1.8em;
+    }
+
+  div#reference-orange
+    {
+      background-color: orange;
+      color: orange;
+      line-height: 1.8; /* arbitrary line-height */
+    }
+  </style>
+
+  <p>Test passes if there is a blue rectangle with the <strong>same height</strong> as an orange rectangle.
+
+  <div id="test-blue"></div>
+
+  <div id="reference-orange">00000</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-011.html b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-011.html
new file mode 100644
index 0000000..c486f0c4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-011.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: ch unit in height with 'text-orientation: upright'</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#font-relative-lengths">
+  <link rel="match" href="reference/ch-unit-011-ref.html">
+
+  <meta name="flags" content="">
+  <meta name="assert" content="In this test, the ch unit is the advance height measure of the 0 (ZERO, U+0030) glyph.">
+
+  <style>
+  div
+    {
+      float: left;
+      font-size: 80px; /* arbitrary font size */
+      text-orientation: upright;
+      writing-mode: vertical-rl;
+    }
+
+  div#test-blue
+    {
+      background-color: blue;
+      height: 5ch;
+      width: 1.8em;
+    }
+
+  div#reference-orange
+    {
+      background-color: orange;
+      color: orange;
+      line-height: 1.8; /* arbitrary line-height */
+    }
+  </style>
+
+  <p>Test passes if there is a blue rectangle with the <strong>same height</strong> as an orange rectangle.
+
+  <div id="test-blue"></div>
+
+  <div id="reference-orange">00000</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-012.html b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-012.html
new file mode 100644
index 0000000..b844d9b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/ch-unit-012.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test: ch unit in height with 'text-orientation: sideways'</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+  <link rel="help" href="https://www.w3.org/TR/css-values-3/#font-relative-lengths">
+  <link rel="match" href="reference/ch-unit-009-ref.html">
+
+  <meta name="flags" content="">
+  <meta name="assert" content="In this test, the ch unit is the advance width measure of the 0 (ZERO, U+0030) glyph.">
+
+  <style>
+  div
+    {
+      float: left;
+      font-size: 80px; /* arbitrary font size */
+      text-orientation: sideways;
+      writing-mode: vertical-rl;
+    }
+
+  div#test-blue
+    {
+      background-color: blue;
+      height: 5ch;
+      width: 1.8em;
+    }
+
+  div#reference-orange
+    {
+      background-color: orange;
+      color: orange;
+      line-height: 1.8; /* arbitrary line-height */
+    }
+  </style>
+
+  <p>Test passes if there is a blue rectangle with the <strong>same height</strong> as an orange rectangle.
+
+  <div id="test-blue"></div>
+
+  <div id="reference-orange">00000</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/lh-rlh-on-root-001.html b/third_party/blink/web_tests/external/wpt/css/css-values/lh-rlh-on-root-001.html
index 228da4e..123218dd 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-values/lh-rlh-on-root-001.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/lh-rlh-on-root-001.html
@@ -45,13 +45,13 @@
   test(function() {
     window.document.documentElement.style="font-size: 1lh; line-height: 142px;";
     f_s = get_root_font_size();
-    assert_approx_equals( f_s, initial_f_s, 1, "the lh unit on the root element's font-size property uses font metrics corresponding to the initial values of the font or line-height properties");
+    assert_approx_equals( f_s, initial_l_h, 1, "the lh unit on the root element's font-size property uses font metrics corresponding to the initial values of the font or line-height properties");
   }, "lh in font-size on root");
 
   test(function() {
     window.document.documentElement.style="font-size: 1rlh; line-height: 142px;";
     f_s = get_root_font_size();
-    assert_approx_equals( f_s, initial_f_s, 1, "the rlh unit on the root element's font-size property uses font metrics corresponding to the initial values of the font or line-height properties");
+    assert_approx_equals( f_s, initial_l_h, 1, "the rlh unit on the root element's font-size property uses font metrics corresponding to the initial values of the font or line-height properties");
 
   }, "rlh in font-size on root");
 
@@ -71,7 +71,7 @@
   test(function() {
     window.document.documentElement.style="font-size: 2lh; line-height: 142px;";
     f_s = get_root_font_size();
-    assert_approx_equals( f_s, initial_f_s * 2, 1, "the lh unit on the root element's font-size property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account");
+    assert_approx_equals( f_s, initial_l_h * 2, 1, "the lh unit on the root element's font-size property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account");
   }, "2lh in font-size on root");
 
   test(function() {
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-008-ref.html b/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-008-ref.html
new file mode 100644
index 0000000..678a9c1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-008-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test Reference File</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+  <style>
+  div
+    {
+      float: left;
+      font-size: 80px; /* arbitrary font size */
+      line-height: 1.8; /* arbitrary line-height */
+    }
+
+  div#blue
+    {
+      background-color: blue;
+      color: blue;
+    }
+
+  div#orange
+    {
+      background-color: orange;
+      color: orange;
+      clear: left;
+    }
+  </style>
+
+  <p>Test passes if there is a blue rectangle with the <strong>same width</strong> as an orange rectangle.
+
+  <div id="blue">00000</div>
+
+  <div id="orange">00000</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-009-ref.html b/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-009-ref.html
new file mode 100644
index 0000000..6bd69bed
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-009-ref.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test Reference File</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+  <style>
+  div
+    {
+      float: left;
+      font-size: 80px; /* arbitrary font size */
+      line-height: 1.8; /* arbitrary line-height */
+      writing-mode: vertical-rl;
+    }
+
+  div#blue
+    {
+      background-color: blue;
+      color: blue;
+    }
+
+  div#orange
+    {
+      background-color: orange;
+      color: orange;
+    }
+  </style>
+
+  <p>Test passes if there is a blue rectangle with the <strong>same height</strong> as an orange rectangle.
+
+  <div id="blue">00000</div>
+
+  <div id="orange">00000</div>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-011-ref.html b/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-011-ref.html
new file mode 100644
index 0000000..78b484fe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/css-values/reference/ch-unit-011-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+
+  <meta charset="UTF-8">
+
+  <title>CSS Values and Units Test Reference File</title>
+
+  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
+
+  <style>
+  div
+    {
+      float: left;
+      font-size: 80px; /* arbitrary font size */
+      line-height: 1.8; /* arbitrary line-height */
+      text-orientation: upright;
+      writing-mode: vertical-rl;
+    }
+
+  div#blue
+    {
+      background-color: blue;
+      color: blue;
+    }
+
+  div#orange
+    {
+      background-color: orange;
+      color: orange;
+    }
+  </style>
+
+  <p>Test passes if there is a blue rectangle with the <strong>same height</strong> as an orange rectangle.
+
+  <div id="blue">00000</div>
+
+  <div id="orange">00000</div>
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/axisheight5000-verticalarrow14000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/axisheight5000-verticalarrow14000.woff
new file mode 100644
index 0000000..9f5d59a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/axisheight5000-verticalarrow14000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-axisheight7000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-axisheight7000-rulethickness1000.woff
new file mode 100644
index 0000000..2a9d78c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-axisheight7000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff
new file mode 100644
index 0000000..e4d381e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff
new file mode 100644
index 0000000..3d90e64
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatorgapmin4000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatorgapmin4000-rulethickness1000.woff
new file mode 100644
index 0000000..ab648f5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatorgapmin4000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatorshiftdown3000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatorshiftdown3000-rulethickness1000.woff
new file mode 100644
index 0000000..0b7efba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-denominatorshiftdown3000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff
new file mode 100644
index 0000000..2e910bb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff
new file mode 100644
index 0000000..920c81eb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratorgapmin9000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratorgapmin9000-rulethickness1000.woff
new file mode 100644
index 0000000..1b5d60b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratorgapmin9000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratorshiftup11000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratorshiftup11000-rulethickness1000.woff
new file mode 100644
index 0000000..3e70cd2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-numeratorshiftup11000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/fraction-rulethickness10000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-rulethickness10000.woff
new file mode 100644
index 0000000..ec69f24
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/fraction-rulethickness10000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/largeop-displayoperatorminheight2000-2AFF-italiccorrection3000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/largeop-displayoperatorminheight2000-2AFF-italiccorrection3000.woff
new file mode 100644
index 0000000..0b4f8bf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/largeop-displayoperatorminheight2000-2AFF-italiccorrection3000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/largeop-displayoperatorminheight5000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/largeop-displayoperatorminheight5000.woff
new file mode 100644
index 0000000..53fcc13a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/largeop-displayoperatorminheight5000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/limits-lowerlimitbaselinedropmin3000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/limits-lowerlimitbaselinedropmin3000.woff
new file mode 100644
index 0000000..76395db
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/limits-lowerlimitbaselinedropmin3000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/limits-lowerlimitgapmin11000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/limits-lowerlimitgapmin11000.woff
new file mode 100644
index 0000000..df67de5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/limits-lowerlimitgapmin11000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/limits-upperlimitbaselinerisemin5000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/limits-upperlimitbaselinerisemin5000.woff
new file mode 100644
index 0000000..f5d7b9c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/limits-upperlimitbaselinerisemin5000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/limits-upperlimitgapmin7000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/limits-upperlimitgapmin7000.woff
new file mode 100644
index 0000000..c88e7a9c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/limits-upperlimitgapmin7000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/lineheight5000-typolineheight2300.woff b/third_party/blink/web_tests/external/wpt/fonts/math/lineheight5000-typolineheight2300.woff
new file mode 100644
index 0000000..09076604
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/lineheight5000-typolineheight2300.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-fraktur.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-fraktur.woff
new file mode 100644
index 0000000..20cd8e3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-fraktur.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-italic.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-italic.woff
new file mode 100644
index 0000000..9651fc8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-italic.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-sans-serif.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-sans-serif.woff
new file mode 100644
index 0000000..24f8f2d40
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-sans-serif.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-script.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-script.woff
new file mode 100644
index 0000000..3cea721
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold-script.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold.woff
new file mode 100644
index 0000000..f35194e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-bold.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-double-struck.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-double-struck.woff
new file mode 100644
index 0000000..ed35ee6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-double-struck.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-fraktur.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-fraktur.woff
new file mode 100644
index 0000000..40adf0d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-fraktur.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-initial.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-initial.woff
new file mode 100644
index 0000000..db0ca9b2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-initial.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-italic.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-italic.woff
new file mode 100644
index 0000000..9687f377
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-italic.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-looped.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-looped.woff
new file mode 100644
index 0000000..ada1b9e6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-looped.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-monospace.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-monospace.woff
new file mode 100644
index 0000000..fc3f0a4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-monospace.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-sans-serif-bold-italic.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-sans-serif-bold-italic.woff
new file mode 100644
index 0000000..05bc859
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-sans-serif-bold-italic.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-sans-serif-italic.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-sans-serif-italic.woff
new file mode 100644
index 0000000..dc139bf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-sans-serif-italic.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-sans-serif.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-sans-serif.woff
new file mode 100644
index 0000000..8ef41f5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-sans-serif.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-script.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-script.woff
new file mode 100644
index 0000000..d5c457a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-script.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-stretched.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-stretched.woff
new file mode 100644
index 0000000..470c879e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-stretched.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-tailed.woff b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-tailed.woff
new file mode 100644
index 0000000..d612b1e0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/mathvariant-tailed.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/radical-degreebottomraisepercent25-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/radical-degreebottomraisepercent25-rulethickness1000.woff
new file mode 100644
index 0000000..6401070
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/radical-degreebottomraisepercent25-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/radical-displaystyleverticalgap7000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/radical-displaystyleverticalgap7000-rulethickness1000.woff
new file mode 100644
index 0000000..0e8b1e0a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/radical-displaystyleverticalgap7000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/radical-extraascender3000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/radical-extraascender3000-rulethickness1000.woff
new file mode 100644
index 0000000..6c0ca28
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/radical-extraascender3000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/radical-kernafterdegreeminus5000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/radical-kernafterdegreeminus5000-rulethickness1000.woff
new file mode 100644
index 0000000..24aa4c5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/radical-kernafterdegreeminus5000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/radical-kernbeforedegree4000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/radical-kernbeforedegree4000-rulethickness1000.woff
new file mode 100644
index 0000000..1fca6f6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/radical-kernbeforedegree4000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/radical-rulethickness8000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/radical-rulethickness8000.woff
new file mode 100644
index 0000000..0863dc3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/radical-rulethickness8000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/radical-verticalgap6000-rulethickness1000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/radical-verticalgap6000-rulethickness1000.woff
new file mode 100644
index 0000000..9808112c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/radical-verticalgap6000-rulethickness1000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-spaceafterscript3000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-spaceafterscript3000.woff
new file mode 100644
index 0000000..44f9ece
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-spaceafterscript3000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subscriptbaselinedropmin9000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subscriptbaselinedropmin9000.woff
new file mode 100644
index 0000000..46880c1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subscriptbaselinedropmin9000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subscriptshiftdown6000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subscriptshiftdown6000.woff
new file mode 100644
index 0000000..3565f7a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subscriptshiftdown6000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subscripttopmax4000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subscripttopmax4000.woff
new file mode 100644
index 0000000..69cd23de
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subscripttopmax4000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subsuperscriptgapmin11000-superscriptbottommaxwithsubscript3000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subsuperscriptgapmin11000-superscriptbottommaxwithsubscript3000.woff
new file mode 100644
index 0000000..2f9da790
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subsuperscriptgapmin11000-superscriptbottommaxwithsubscript3000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subsuperscriptgapmin11000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subsuperscriptgapmin11000.woff
new file mode 100644
index 0000000..d85dfee1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-subsuperscriptgapmin11000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptbaselinedropmax10000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptbaselinedropmax10000.woff
new file mode 100644
index 0000000..3f528edf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptbaselinedropmax10000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptbottommin8000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptbottommin8000.woff
new file mode 100644
index 0000000..f3937e8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptbottommin8000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptshiftup7000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptshiftup7000.woff
new file mode 100644
index 0000000..845afdf
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptshiftup7000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptshiftupcramped5000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptshiftupcramped5000.woff
new file mode 100644
index 0000000..c85e7f6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/scripts-superscriptshiftupcramped5000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stack-axisheight7000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stack-axisheight7000.woff
new file mode 100644
index 0000000..7a9dc5d4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stack-axisheight7000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stack-bottomdisplaystyleshiftdown5000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stack-bottomdisplaystyleshiftdown5000.woff
new file mode 100644
index 0000000..8761585
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stack-bottomdisplaystyleshiftdown5000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stack-bottomshiftdown6000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stack-bottomshiftdown6000.woff
new file mode 100644
index 0000000..96ebce2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stack-bottomshiftdown6000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stack-displaystylegapmin4000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stack-displaystylegapmin4000.woff
new file mode 100644
index 0000000..a94d6286
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stack-displaystylegapmin4000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stack-gapmin8000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stack-gapmin8000.woff
new file mode 100644
index 0000000..a4428d1d4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stack-gapmin8000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stack-topdisplaystyleshiftup3000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stack-topdisplaystyleshiftup3000.woff
new file mode 100644
index 0000000..556226f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stack-topdisplaystyleshiftup3000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stack-topshiftup9000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stack-topshiftup9000.woff
new file mode 100644
index 0000000..d036e89
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stack-topshiftup9000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-bottomshiftdown3000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-bottomshiftdown3000.woff
new file mode 100644
index 0000000..275a435
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-bottomshiftdown3000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-gapabovemin7000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-gapabovemin7000.woff
new file mode 100644
index 0000000..af61d8e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-gapabovemin7000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-gapbelowmin11000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-gapbelowmin11000.woff
new file mode 100644
index 0000000..8900a814
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-gapbelowmin11000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-topshiftup5000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-topshiftup5000.woff
new file mode 100644
index 0000000..7a90c74f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/stretchstack-topshiftup5000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-overbarextraascender3000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-overbarextraascender3000.woff
new file mode 100644
index 0000000..2aa7133
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-overbarextraascender3000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-overbarverticalgap11000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-overbarverticalgap11000.woff
new file mode 100644
index 0000000..0f97511
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-overbarverticalgap11000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-underbarextradescender5000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-underbarextradescender5000.woff
new file mode 100644
index 0000000..bb3e465
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-underbarextradescender5000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-underbarverticalgap7000.woff b/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-underbarverticalgap7000.woff
new file mode 100644
index 0000000..36a4e86
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/underover-accentbaseheight4000-underbarverticalgap7000.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/math/xheight500.woff b/third_party/blink/web_tests/external/wpt/fonts/math/xheight500.woff
new file mode 100644
index 0000000..76a37da9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/math/xheight500.woff
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/noto/NotoSansMongolian-regular.woff2 b/third_party/blink/web_tests/external/wpt/fonts/noto/NotoSansMongolian-regular.woff2
new file mode 100644
index 0000000..e34e547
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/noto/NotoSansMongolian-regular.woff2
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/fonts/noto/NotoSansNko-regular-webfont.woff2 b/third_party/blink/web_tests/external/wpt/fonts/noto/NotoSansNko-regular-webfont.woff2
new file mode 100644
index 0000000..780661b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fonts/noto/NotoSansNko-regular-webfont.woff2
Binary files differ
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html
new file mode 100644
index 0000000..2cc74e2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/srcset/srcset-media-dynamic.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<title>source element in picture handles dynamic media change correctly.</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1523627">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<picture id="pic">
+  <source srcset="data:,a">
+</picture>
+<script>
+let t = async_test("Dynamic media change is handled correctly");
+
+let pic = document.getElementById("pic");
+// Something that will never match.
+pic.querySelector("source").setAttribute("media", "not all");
+
+let img = document.createElement("img");
+img.src = "data:,b";
+pic.appendChild(img);
+
+onload = t.step_func_done(function() {
+  assert_equals(img.currentSrc, "data:,b");
+});
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/elementTiming.html.ini b/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/elementTiming.html.ini
deleted file mode 100644
index a06134a6..0000000
--- a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/elementTiming.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[elementTiming.html]
-  [TestDriver actions: element timing]
-    expected:
-      if product == "chrome": FAIL
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/eventOrder.html.ini b/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/eventOrder.html.ini
index 00916c7a..bcd78da2 100644
--- a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/eventOrder.html.ini
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/eventOrder.html.ini
@@ -1,7 +1,3 @@
 [eventOrder.html]
   expected:
     if product == "safari": ERROR
-
-  [TestDriver actions: event order]
-    expected:
-      if product == "chrome": FAIL
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/multiDevice.html.ini b/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/multiDevice.html.ini
index b9fb259..04b97a7 100644
--- a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/multiDevice.html.ini
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/multiDevice.html.ini
@@ -1,3 +1,3 @@
 [multiDevice.html]
   expected:
-    if product == "chrome" or product == "safari": ERROR
+    if product == "safari": ERROR
diff --git a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/pause.html.ini b/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/pause.html.ini
index 7ceec9f..da47a2a 100644
--- a/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/pause.html.ini
+++ b/third_party/blink/web_tests/external/wpt/infrastructure/metadata/infrastructure/testdriver/actions/pause.html.ini
@@ -1,7 +1,3 @@
 [pause.html]
   expected:
     if product == "safari": ERROR
-
-  [TestDriver actions: pause]
-    expected:
-      if product == "chrome": FAIL
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/mediasession.idl b/third_party/blink/web_tests/external/wpt/interfaces/mediasession.idl
index d2d16c2..0fc9ff05 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/mediasession.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/mediasession.idl
@@ -21,7 +21,7 @@
   "seekforward",
   "previoustrack",
   "nexttrack",
-  "skip-ad",
+  "skipad",
 };
 
 callback MediaSessionActionHandler = void();
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl b/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl
index dc51e2f..d60139a 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webrtc.idl
@@ -628,7 +628,7 @@
           };
 
 [Exposed=Window,
- Constructor(DOMString type, RTCErrorEventInit eventInitDict)]
+ Constructor(DOMString type, optional RTCErrorEventInit eventInitDict)]
 interface RTCErrorEvent : Event {
     readonly        attribute RTCError? error;
 };
diff --git a/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl b/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
index 63643a1..b1ed0f9 100644
--- a/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
+++ b/third_party/blink/web_tests/external/wpt/interfaces/webxr.idl
@@ -93,23 +93,20 @@
   "unbounded"
 };
 
-dictionary XRReferenceSpaceOptions {
-  required XRReferenceSpaceType type;
-};
-
-[SecureContext, Exposed=Window] interface XRReferenceSpace : XRSpace {
-  attribute XRRigidTransform originOffset;
-  attribute EventHandler onreset;
-};
-
 enum XRStationaryReferenceSpaceSubtype {
   "eye-level",
   "floor-level",
   "position-disabled"
 };
 
-dictionary XRStationaryReferenceSpaceOptions : XRReferenceSpaceOptions {
-  required XRStationaryReferenceSpaceSubtype subtype;
+dictionary XRReferenceSpaceOptions {
+  required XRReferenceSpaceType type;
+  XRStationaryReferenceSpaceSubtype subtype;
+};
+
+[SecureContext, Exposed=Window] interface XRReferenceSpace : XRSpace {
+  attribute XRRigidTransform originOffset;
+  attribute EventHandler onreset;
 };
 
 [SecureContext, Exposed=Window]
diff --git a/third_party/blink/web_tests/external/wpt/mathml/META.yml b/third_party/blink/web_tests/external/wpt/mathml/META.yml
new file mode 100644
index 0000000..a410f58
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/META.yml
@@ -0,0 +1,3 @@
+spec: http://www.mathml-association.org/MathMLinHTML5/
+suggested_reviewers:
+  - fred-wang
diff --git a/third_party/blink/web_tests/external/wpt/mathml/README.md b/third_party/blink/web_tests/external/wpt/mathml/README.md
new file mode 100644
index 0000000..bab3547
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/README.md
@@ -0,0 +1,18 @@
+# MathML: Tests for the MathML in HTML5 implementation note
+
+This directory contains tests for the
+[MathML in HTML5 implementation note](http://www.mathml-association.org/MathMLinHTML5/)
+which is itself based on the
+[HTML5 W3C recommendation](https://www.w3.org/TR/html5/),
+on the [MathML3 W3C recommendation](https://www.w3.org/TR/MathML3/)
+and on the
+[Open Font Format 3](http://www.iso.org/iso/home/store/catalogue_ics/catalogue_detail_ics.htm?csnumber=66391) standard.
+
+Many of the tests verify OpenType features and require specific Web fonts for
+that purpose. WOFF fonts are generated by scripts in the `tools/` folder using
+the Python API of
+[fontforge](https://github.com/fontforge/fontforge/). A recent enough version
+of FontForge is necessary so that it includes fixes for
+[WOFF checkSumAdjustment](https://github.com/fontforge/fontforge/issues/926),
+[USE_TYPO_METRICS flag](https://github.com/fontforge/fontforge/pull/2274) and
+for other various bugs.
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html
new file mode 100644
index 0000000..fc650eb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-1.html
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Fraction</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2">
+<meta name="assert" content="Verify fraction metrics for different sizes of numerator and denominator.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+</style>
+<script>
+  /* This test does not use any specific fonts and so the exact rules are not
+  specified precisely. We assume reasonable values for numerator/denominator
+  shifts and spacing. */
+
+  function getBox(aId) {
+    var box = document.getElementById(aId).getBoundingClientRect();
+    box.middle = (box.bottom + box.top) / 2;
+    box.center = (box.left + box.right) / 2;
+    return box;
+  }
+
+  function getFractionAxis(aId) {
+    return (getBox(aId).top * den.height + getBox(aId).bottom * num.height) / (num.height + den.height);
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+    test(function() {
+      var e = 3;
+      var mathAxis = getBox("axis").middle;
+      // For stacks, nothing in the OpenType MATH specification seems to ensure
+      // that the gap is split symmetrically around the math axis so we only
+      // do the following verification for standard fractions.
+      for (var i = 0; i <= 4; i++) {
+        var frac = getBox("frac" + i);
+        var num = getBox("frac" + i + "num");
+        var den = getBox("frac" + i + "den");
+        // To estimate the fraction axis, we calculate barycenter between the
+        // top and bottom of the fraction, using the heights of numerator and
+        // denominator as weights.
+        var fracAxis = (frac.top * den.height + frac.bottom * num.height) / (num.height + den.height);
+        assert_approx_equals(fracAxis, mathAxis, e, "frac" + i + " fraction bar");
+      }
+    }, "Fraction axis is aligned on the math axis");
+
+    test(function() {
+      for (var i = 0; i < 10; i++) {
+        assert_less_than(getBox("frac" + i + "num").bottom, getBox("frac" + i + "den").top, "numerator is above denominator");
+        assert_less_than(getBox("frac" + i + "den").top - getBox("frac" + i + "num").bottom, 5, "The gap between numerator and denominator is not too large");
+      }
+    }, "Vertical positions of numerator and denominator");
+
+    test(function() {
+      var e = 3;
+      for (var i = 0; i < 10; i++)
+        assert_approx_equals(getBox("frac" + i + "num").center, getBox("frac" + i + "den").center, e, "numerator and denominator are horizontally centered");
+    }, "Horizontal alignments of numerator and denominator");
+
+    test(function() {
+      var e = 5;
+      for (var i = 0; i < 10; i++) {
+        var frac = getBox("frac" + i);
+        var num = getBox("frac" + i + "num");
+        var den = getBox("frac" + i + "den");
+        assert_approx_equals(frac.height, den.bottom - num.top, e, "height of frac " + i + " is determined by the bottom/top sides of the denominator/numerator");
+        assert_approx_equals(frac.width, Math.max(num.right, den.right) - Math.min(num.left, den.left), e, "width of frac " + i + " is determined by the left/right sides of the denominator/numerator (plus some spacing)");
+      }
+    }, "Dimension of mfrac elements");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math>
+      <mo id="axis">−</mo>
+      <mfrac id="frac0">
+        <mspace id="frac0num" width="15px" height="15px" mathbackground="blue"/>
+        <mspace id="frac0den" width="15px" height="15px" mathbackground="green"/>
+      </mfrac>
+      <mfrac id="frac1">
+        <mspace id="frac1num" width="30px" height="15px" mathbackground="blue"/>
+        <mspace id="frac1den" width="15px" height="15px" mathbackground="green"/>
+      </mfrac>
+      <mfrac id="frac2">
+        <mspace id="frac2num" width="15px" height="15px" mathbackground="blue"/>
+        <mspace id="frac2den" width="30px" height="15px" mathbackground="green"/>
+      </mfrac>
+      <mfrac id="frac3">
+        <mspace id="frac3num" width="15px" height="30px" mathbackground="blue"/>
+        <mspace id="frac3den" width="15px" height="15px" mathbackground="green"/>
+      </mfrac>
+      <mfrac id="frac4">
+        <mspace id="frac4num" width="15px" height="15px" mathbackground="blue"/>
+        <mspace id="frac4den" width="15px" height="30px" mathbackground="green"/>
+      </mfrac>
+      <mfrac id="frac5" linethickness="0px">
+        <mspace id="frac5num" width="15px" height="15px" mathbackground="blue"/>
+        <mspace id="frac5den" width="15px" height="15px" mathbackground="green"/>
+      </mfrac>
+      <mfrac id="frac6" linethickness="0px">
+        <mspace id="frac6num" width="30px" height="15px" mathbackground="blue"/>
+        <mspace id="frac6den" width="15px" height="15px" mathbackground="green"/>
+      </mfrac>
+      <mfrac id="frac7" linethickness="0px">
+        <mspace id="frac7num" width="15px" height="15px" mathbackground="blue"/>
+        <mspace id="frac7den" width="30px" height="15px" mathbackground="green"/>
+      </mfrac>
+      <mfrac id="frac8" linethickness="0px">
+        <mspace id="frac8num" width="15px" height="30px" mathbackground="blue"/>
+        <mspace id="frac8den" width="15px" height="15px" mathbackground="green"/>
+      </mfrac>
+      <mfrac id="frac9" linethickness="0px">
+        <mspace id="frac9num" width="15px" height="15px" mathbackground="blue"/>
+        <mspace id="frac9den" width="15px" height="30px" mathbackground="green"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html
new file mode 100644
index 0000000..a047a30
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-1.html
@@ -0,0 +1,242 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Fraction parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2">
+<meta name="assert" content="Element mfrac correctly uses the fraction parameters from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: axisheight7000-rulethickness1000;
+    src: url("/fonts/math/fraction-axisheight7000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: denominatordisplaystylegapmin5000-rulethickness1000;
+    src: url("/fonts/math/fraction-denominatordisplaystylegapmin5000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: denominatordisplaystyleshiftdown6000-rulethickness1000;
+    src: url("/fonts/math/fraction-denominatordisplaystyleshiftdown6000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: denominatorgapmin4000-rulethickness1000;
+    src: url("/fonts/math/fraction-denominatorgapmin4000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: denominatorshiftdown3000-rulethickness1000;
+    src: url("/fonts/math/fraction-denominatorshiftdown3000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: numeratordisplaystylegapmin8000-rulethickness1000;
+    src: url("/fonts/math/fraction-numeratordisplaystylegapmin8000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: numeratordisplaystyleshiftup2000-rulethickness1000;
+    src: url("/fonts/math/fraction-numeratordisplaystyleshiftup2000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: numeratorgapmin9000-rulethickness1000;
+    src: url("/fonts/math/fraction-numeratorgapmin9000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: numeratorshiftup11000-rulethickness1000;
+    src: url("/fonts/math/fraction-numeratorshiftup11000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: rulethickness10000;
+    src: url("/fonts/math/fraction-rulethickness10000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 1;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      var v1 = 7000 * emToPx;
+      var v2 = 1000 * emToPx;
+      assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
+                           v1 + v2 / 2, epsilon, "mfrac: axis height");
+    }, "AxisHeight");
+
+    test(function() {
+      var v1 = 5000 * emToPx;
+      assert_approx_equals(getBox("den0002").top - getBox("ref0002").bottom,
+                           v1, epsilon, "mfrac: denominator gap");
+    }, "DenominatorDisplayStyleGapMin");
+
+    test(function() {
+      var v1 = 6000 * emToPx;
+      assert_approx_equals(getBox("den0003").top - getBox("ref0003").bottom,
+                           v1, epsilon, "mfrac: denominator shift");
+    }, "DenominatorDisplayStyleShiftDown");
+
+    test(function() {
+      var v1 = 4000 * emToPx;
+      assert_approx_equals(getBox("den0004").top - getBox("ref0004").bottom,
+                           v1, epsilon, "mfrac: denominator gap");
+    }, "DenominatorGapMin");
+
+    test(function() {
+      var v1 = 3000 * emToPx;
+      assert_approx_equals(getBox("den0005").top - getBox("ref0005").bottom,
+                           v1, epsilon, "mfrac: denominator shift");
+    }, "DenominatorShiftDown");
+
+    test(function() {
+      var v1 = 8000 * emToPx;
+      assert_approx_equals(getBox("ref0006").top - getBox("num0006").bottom,
+                           v1, epsilon, "mfrac: numerator gap");
+    }, "NumeratorDisplayStyleGapMin");
+
+    test(function() {
+      var v1 = 2000 * emToPx;
+      assert_approx_equals(getBox("ref0007").top - getBox("num0007").bottom,
+                           v1, epsilon, "mfrac: numerator shift");
+    }, "NumeratorDisplayStyleShiftDown");
+
+    test(function() {
+      var v1 = 9000 * emToPx;
+      assert_approx_equals(getBox("ref0008").top - getBox("num0008").bottom,
+                           v1, epsilon, "mfrac: numerator gap");
+    }, "NumeratorGapMin");
+
+    test(function() {
+      var v1 = 11000 * emToPx;
+      assert_approx_equals(getBox("ref0009").top - getBox("num0009").bottom,
+                           v1, epsilon, "mfrac: numerator shift");
+    }, "NumeratorShiftDown");
+
+    test(function() {
+      var v1 = 10000 * emToPx;
+      assert_approx_equals(getBox("den0010").top - getBox("num0010").bottom,
+                           v1, epsilon, "mfrac: rule thickness");
+    }, "FractionRuleThickness");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math style="font-family: axisheight7000-rulethickness1000;">
+      <mspace id="ref0001" depth="1em" width="3em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" height="1em" id="num0001" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: denominatordisplaystylegapmin5000-rulethickness1000;">
+      <mspace id="ref0002" width="3em"
+              height=".5em" depth=".5em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em"/>
+        <mspace width="3em" height="1em" id="den0002" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: denominatordisplaystyleshiftdown6000-rulethickness1000;">
+      <mspace id="ref0003" width="3em" height="1em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em"/>
+        <mspace width="3em" depth="1em" id="den0003" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: denominatorgapmin4000-rulethickness1000;">
+      <mspace id="ref0004" width="3em"
+              height=".5em" depth=".5em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em"/>
+        <mspace width="3em" height="1em" id="den0004" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: denominatorshiftdown3000-rulethickness1000;">
+      <mspace id="ref0005" width="3em" height="1em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em"/>
+        <mspace width="3em" depth="1em" id="den0005" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: numeratordisplaystylegapmin8000-rulethickness1000;">
+      <mspace id="ref0006" width="3em"
+              height=".5em" depth=".5em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" depth="1em" id="num0006" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: numeratordisplaystyleshiftup2000-rulethickness1000;">
+      <mspace id="ref0007" width="3em"
+              depth="1em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" height="1em" id="num0007" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: numeratorgapmin9000-rulethickness1000;">
+      <mspace id="ref0008" width="3em"
+              height=".5em" depth=".5em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" depth="1em" id="num0008" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: numeratorshiftup11000-rulethickness1000;">
+      <mspace id="ref0009" width="3em"
+              depth="1em" mathbackground="green"/>
+      <mfrac>
+        <mspace width="3em" height="1em" id="num0009" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: rulethickness10000">
+      <mfrac>
+        <mspace width="3em" height="1em" id="num0010" mathbackground="blue"/>
+        <mspace width="3em" depth="1em" id="den0010" mathbackground="green"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
new file mode 100644
index 0000000..5445113
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-parameters-2.html
@@ -0,0 +1,174 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Stack parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS2">
+<meta name="assert" content="Element mfrac correctly uses the stack parameters from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: axisheight7000;
+    src: url("/fonts/math/stack-axisheight7000.woff");
+  }
+  @font-face {
+    font-family: bottomdisplaystyleshiftdown5000;
+    src: url("/fonts/math/stack-bottomdisplaystyleshiftdown5000.woff");
+  }
+  @font-face {
+    font-family: bottomshiftdown6000;
+    src: url("/fonts/math/stack-bottomshiftdown6000.woff");
+  }
+  @font-face {
+    font-family: displaystylegapmin4000;
+    src: url("/fonts/math/stack-displaystylegapmin4000.woff");
+  }
+  @font-face {
+    font-family: gapmin8000;
+    src: url("/fonts/math/stack-gapmin8000.woff");
+  }
+  @font-face {
+    font-family: topdisplaystyleshiftup3000;
+    src: url("/fonts/math/stack-topdisplaystyleshiftup3000.woff");
+  }
+  @font-face {
+    font-family: topshiftup9000;
+    src: url("/fonts/math/stack-topshiftup9000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 1;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      var v = 7000 * emToPx;
+      assert_approx_equals(getBox("ref0001").top - getBox("num0001").bottom,
+                           v, epsilon, "mfrac: axis height");
+    }, "AxisHeight");
+
+    test(function() {
+      var v = 5000 * emToPx;
+      assert_approx_equals(getBox("den0002").top - getBox("ref0002").bottom,
+                           v, epsilon, "mfrac: denominator shift");
+    }, "BottomDisplayStyleShiftDown");
+
+    test(function() {
+      var v = 6000 * emToPx;
+      assert_approx_equals(getBox("den0003").top - getBox("ref0003").bottom,
+                           v, epsilon, "mfrac: denominator shift");
+    }, "BottomShiftDown");
+
+    test(function() {
+      var v = 4000 * emToPx;
+      assert_approx_equals(getBox("den0004").top - getBox("num0004").bottom,
+                           v, epsilon, "mfrac: gap");
+    }, "DisplayStyleGapMin");
+
+    test(function() {
+      var v = 8000 * emToPx;
+      assert_approx_equals(getBox("den0005").top - getBox("num0005").bottom,
+                           v, epsilon, "mfrac: gap");
+    }, "GapMin");
+
+    test(function() {
+      var v = 3000 * emToPx;
+      assert_approx_equals(getBox("ref0006").top - getBox("num0006").bottom,
+                           v, epsilon, "mfrac: numerator shift");
+    }, "TopDisplayStyleShiftUp");
+
+    test(function() {
+      var v = 9000 * emToPx;
+      assert_approx_equals(getBox("ref0007").top - getBox("num0007").bottom,
+                           v, epsilon, "mfrac: numerator shift");
+    }, "ToShiftUp");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math style="font-family: axisheight7000;">
+      <mspace id="ref0001" depth="1em" width="3em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0001" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: bottomdisplaystyleshiftdown5000;">
+      <mspace id="ref0002" width="3em" height="1em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em"/>
+        <mspace width="3em" depth="1em" id="den0002" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: bottomshiftdown6000;">
+      <mspace id="ref0003" width="3em" height="1em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em"/>
+        <mspace width="3em" depth="1em" id="den0003" mathbackground="blue"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: displaystylegapmin4000;">
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0004" mathbackground="blue"/>
+        <mspace width="3em" depth="1em" id="den0004" mathbackground="green"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: gapmin8000;">
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0005" mathbackground="blue"/>
+        <mspace width="3em" depth="1em" id="den0005" mathbackground="green"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block" style="font-family: topdisplaystyleshiftup3000;">
+      <mspace id="ref0006" width="3em" depth="1em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0006" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: topshiftup9000;">
+      <mspace id="ref0007" width="3em" depth="1em" mathbackground="green"/>
+      <mfrac linethickness="0px">
+        <mspace width="3em" height="1em" id="num0007" mathbackground="blue"/>
+        <mspace width="3em"/>
+      </mfrac>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-axis-height-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-axis-height-1.html
new file mode 100644
index 0000000..327a72e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/operators/mo-axis-height-1.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>mo axis height</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS2.SSS4">
+<meta name="assert" content="Element mo correctly uses the axis height parameter from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: axisheight5000-verticalarrow14000;
+    src: url("/fonts/math/axisheight5000-verticalarrow14000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 5;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      var v1 = 5000 * emToPx;
+      var moMiddle = (getBox("mo1").bottom + getBox("mo1").top) / 2;
+      assert_approx_equals(getBox("mo1").height,
+                           14000 * emToPx, epsilon, "mo: size");
+      assert_approx_equals(getBox("baseline1").bottom - moMiddle,
+                           v1, epsilon, "mo: axis height");
+    }, "AxisHeight (size variant)");
+
+    test(function() {
+      var v1 = 5000 * emToPx;
+      var moMiddle = (getBox("mo2").bottom + getBox("mo2").top) / 2;
+      assert_approx_equals(getBox("mo2").height,
+                           2 * (getBox("target2").height - v1),
+                           epsilon, "mo: size");
+      assert_approx_equals(getBox("baseline2").bottom - moMiddle,
+                           v1, epsilon, "mo: axis height");
+    }, "AxisHeight (glyph assembly)");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math style="font-family: axisheight5000-verticalarrow14000;">
+      <mrow>
+        <mspace id="baseline1" mathbackground="blue" width="50px" height="1px"/>
+        <mpadded voffset="50px"><mspace mathbackground="cyan" width="50px" height="1px"/></mpadded>
+        <mo id="mo1" symmetric="true" mathcolor="green">&#x21A8;</mo>
+        <mspace mathbackground="gray" width="10px" height="50px"/>
+      </mrow>
+    </math>
+    <math style="font-family: axisheight5000-verticalarrow14000;">
+      <mrow>
+        <mspace id="baseline2" mathbackground="blue" width="50px" height="1px"/>
+        <mpadded voffset="50px"><mspace mathbackground="cyan" width="50px" height="1px"/></mpadded>
+        <mo id="mo2" symmetric="true" mathcolor="green">&#x21A8;</mo>
+        <mspace id="target2" mathbackground="gray" width="10px" height="200px"/>
+      </mrow>
+    </math>
+  </p>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html
new file mode 100644
index 0000000..67a4613
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/radicals/root-parameters-1.html
@@ -0,0 +1,209 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Radical parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS3.SSS3">
+<meta name="assert" content="Elements msqrt and mroot correctly use the radical parameters from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: degreebottomraisepercent25-rulethickness1000;
+    src: url("/fonts/math/radical-degreebottomraisepercent25-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: displaystyleverticalgap7000-rulethickness1000;
+    src: url("/fonts/math/radical-displaystyleverticalgap7000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: extraascender3000-rulethickness1000;
+    src: url("/fonts/math/radical-extraascender3000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: kernafterdegreeminus5000-rulethickness1000;
+    src: url("/fonts/math/radical-kernafterdegreeminus5000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: kernbeforedegree4000-rulethickness1000;
+    src: url("/fonts/math/radical-kernbeforedegree4000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: verticalgap6000-rulethickness1000;
+    src: url("/fonts/math/radical-verticalgap6000-rulethickness1000.woff");
+  }
+  @font-face {
+    font-family: rulethickness8000;
+    src: url("/fonts/math/radical-rulethickness8000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 1;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      var v1 = 25;
+      var v2 = 1000 * emToPx;
+      var radicalHeight = getBox("base001").height + v2;
+      assert_approx_equals(getBox("ref001").top - getBox("index001").bottom,
+                           v1 * radicalHeight / 100, epsilon,
+                           "mroot: vertical position of index");
+    }, "RadicalDegreeBottomRaisePercent");
+
+    test(function() {
+      var v1 = 7000 * emToPx;
+      var v2 = 1000 * emToPx;
+      assert_approx_equals(getBox("base0021").top - getBox("radical0021").top,
+                           v1 + v2, epsilon,
+                           "msqrt: vertical gap");
+      assert_approx_equals(getBox("base0022").top - getBox("radical0022").top,
+                           v1 + v2, epsilon,
+                           "mroot: vertical gap");
+    }, "RadicalDisplayStyleVerticalGap");
+
+    test(function() {
+      var v1 = 3000 * emToPx;
+      var v2 = 1000 * emToPx;
+      assert_approx_equals(getBox("base0031").top - getBox("radical0031").top,
+                           v1 + v2, epsilon,
+                           "msqrt: vertical gap");
+      assert_approx_equals(getBox("base0032").top - getBox("radical0032").top,
+                           v1 + v2, epsilon,
+                           "mroot: vertical gap");
+    }, "RadicalExtraAscender");
+
+  test(function() {
+      // Note: the size variants of U+221A in this font have width 1000.
+      var v1 = 5000 * emToPx;
+      var radicalSymbolWidth = 1000 * emToPx;
+      var radicalLeft = getBox("base004").left - radicalSymbolWidth;
+      assert_approx_equals(getBox("index004").right - radicalLeft,
+                           v1, epsilon,
+                           "mroot: kern after degree");
+    }, "RadicalKernAfterDegree");
+
+    test(function() {
+      var v1 = 4000 * emToPx;
+      assert_approx_equals(getBox("index005").left - getBox("radical005").left,
+                           v1, epsilon,
+                           "mroot: kern before degree");
+    }, "RadicalKernBeforeDegree");
+
+    test(function() {
+      var v = 8000 * emToPx;
+      assert_approx_equals(getBox("base0061").top - getBox("radical0061").top,
+                           v, epsilon,
+                           "msqrt: vertical gap");
+      assert_approx_equals(getBox("base0062").top - getBox("radical0062").top,
+                           v, epsilon,
+                           "msqrt: vertical gap");
+    }, "RadicalRuleThickness");
+
+    test(function() {
+      var v1 = 6000 * emToPx;
+      var v2 = 1000 * emToPx;
+      assert_approx_equals(getBox("base0071").top - getBox("radical0071").top,
+                           v1 + v2, epsilon,
+                           "msqrt: vertical gap");
+      assert_approx_equals(getBox("base0072").top - getBox("radical0072").top,
+                           v1 + v2, epsilon,
+                           "msqrt: vertical gap");
+    }, "RadicalVerticalGap");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math style="font-family: degreebottomraisepercent25-rulethickness1000;">
+      <mspace id="ref001" width="3em" depth="1em" mathbackground="green"/>
+      <mroot>
+        <mspace id="base001" width="3em" height="10em" mathbackground="green"/>
+        <mspace id="index001" width="3em" height="1em" mathbackground="blue"/>
+      </mroot>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math display="block"
+          style="font-family: displaystyleverticalgap7000-rulethickness1000;">
+      <msqrt mathbackground="green" id="radical0021">
+        <mspace id="base0021" width="3em" height="1em" mathbackground="blue"/>
+      </msqrt>
+      <mroot mathbackground="green" id="radical0022">
+        <mspace id="base0022" width="3em" height="1em" mathbackground="blue"/>
+        <mspace width="3em" height="1em" mathbackground="black"/>
+      </mroot>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: extraascender3000-rulethickness1000;">
+      <msqrt mathbackground="green" id="radical0031">
+        <mspace id="base0031" width="3em" height="1em" mathbackground="blue"/>
+      </msqrt>
+      <mroot mathbackground="green" id="radical0032">
+        <mspace id="base0032" width="3em" height="1em" mathbackground="blue"/>
+        <mspace width="3em" height="1em" mathbackground="black"/>
+      </mroot>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: kernafterdegreeminus5000-rulethickness1000;">
+      <mroot>
+        <mspace id="base004" width="3em" height="2em"  mathbackground="blue"/>
+        <mspace id="index004" width="7em" height="1em" mathbackground="green"/>
+      </mroot>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: kernbeforedegree4000-rulethickness1000;">
+      <mroot id="radical005" mathbackground="blue">
+        <mspace width="3em" height="1em"/>
+        <mspace id="index005" width="3em" height="1em" mathbackground="green"/>
+      </mroot>
+    </math>
+  </p>
+  <hr/>
+  <p>
+    <math style="font-family: rulethickness8000;">
+      <msqrt mathbackground="green" id="radical0061">
+        <mspace id="base0061" width="3em" height="1em" mathbackground="blue"/>
+      </msqrt>
+      <mroot mathbackground="green" id="radical0062">
+        <mspace id="base0062" width="3em" height="1em" mathbackground="blue"/>
+        <mspace width="3em" height="1em" mathbackground="black"/>
+      </mroot>
+    </math>
+  </p>
+  <p>
+    <math style="font-family: verticalgap6000-rulethickness1000;">
+      <msqrt mathbackground="green" id="radical0071">
+        <mspace id="base0071" width="3em" height="1em" mathbackground="blue"/>
+      </msqrt>
+      <mroot mathbackground="green" id="radical0072">
+        <mspace id="base0072" width="3em" height="1em" mathbackground="blue"/>
+        <mspace width="3em" height="1em" mathbackground="black"/>
+      </mroot>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html
new file mode 100644
index 0000000..2ff14a6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-1.html
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Subscripts and Superscripts metrics</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Basic metrics for elements msub, msup and msubsup.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+</style>
+<script>
+  /* This test does not use any specific fonts and so the exact rules are not
+  specified precisely. We assume reasonable values for script shifts and
+  spacing. */
+
+  function getBox(aId) {
+    var box = document.getElementById(aId).getBoundingClientRect();
+    box.middle = (box.bottom + box.top) / 2;
+    return box;
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+    test(function() {
+      var e = 1;
+      assert_less_than_equal(getBox("msubBase").right, getBox("msubSub").left, e, "msub: subscript is after base");
+      assert_less_than_equal(getBox("msupBase").right, getBox("msupSup").left, e, "msup: superscript is after base");
+      assert_less_than_equal(getBox("msubsupBase").right, getBox("msubsupSub").left, e, "msubsup: subscript is after base");
+      assert_less_than_equal(getBox("msubsupBase").right, getBox("msubsupSup").left, e, "msubsup: superscript is after base");
+
+      e = 3;
+      assert_approx_equals(getBox("msubBase").right, getBox("msubSub").left, e, "msub: space between base and subscript is small");
+      assert_approx_equals(getBox("msubBase").right, getBox("msubSub").left, e, "msub: subscript is after base");
+      assert_approx_equals(getBox("msupBase").right, getBox("msupSup").left, e, "msup: superscript is after base");
+      assert_approx_equals(getBox("msubsupBase").right, getBox("msubsupSub").left, e, "msubsup: subscript is after base");
+      assert_approx_equals(getBox("msubsupBase").right, getBox("msubsupSup").left, e, "msubsup: superscript is after base");
+    }, "Respective horizontal positions");
+
+    test(function() {
+      var e = 1;
+      assert_approx_equals(getBox("msubBase").middle, getBox("baseline").bottom, e, "msub: base is placed on the baseline");
+      assert_approx_equals(getBox("msupBase").middle, getBox("baseline").bottom, e, "msup: base is placed on the baseline");
+      assert_approx_equals(getBox("msubsupBase").middle, getBox("baseline").bottom, e, "msubsup: base is placed on the baseline");
+    }, "Alignment of the base on the baseline");
+
+    test(function() {
+      var e = 3;
+      assert_approx_equals(getBox("msubSub").middle, getBox("msubBase").bottom, e, "msub: script is placed at the bottom of the base");
+      assert_approx_equals(getBox("msupSup").middle, getBox("msupBase").top, e, "msup: script is placed at the top of the base");
+      assert_approx_equals(getBox("msubsupSub").middle, getBox("msubsupBase").bottom, e, "msubsup: script is placed at the bottom of the base");
+      assert_approx_equals(getBox("msubsupSup").middle, getBox("msubsupBase").top, e, "msubsup: script is placed at the top of the base");
+    }, "Vertical position of scripts");
+
+    test(function() {
+      var e = 3;
+      assert_approx_equals(getBox("msub").width, getBox("msubSub").right - getBox("msubBase").left, e, "msub: width is determined by the left/right sides of base/script (+ some space after script)");
+      assert_approx_equals(getBox("msup").width, getBox("msupSup").right - getBox("msupBase").left, e, "msup: width is determined by the left/right sides of base/script (+ some space after script)");
+      assert_approx_equals(getBox("msubsup").width, Math.max(getBox("msubsupSub").right, getBox("msubsupSup").right) - getBox("msubsupBase").left, e, "msubsup: width is determined by the left/right sides of base/scripts (+ some space after script)");
+    }, "Width of scripted elements");
+
+    test(function() {
+      var e = 1;
+      assert_greater_than_equal(getBox("msub").height, getBox("msubBase").height, e, "msub: height is at least the one of the base");
+      assert_greater_than_equal(getBox("msup").height, getBox("msupBase").height, e, "msup: height is at least the one of the base");
+      assert_greater_than_equal(getBox("msubsup").height, getBox("msubsupBase").height, e, "msubsup: height is at least the one of the base");
+
+      e = 3;
+      assert_approx_equals(getBox("msub").height, Math.max(getBox("msubSub").bottom, getBox("msubBase").bottom) - getBox("msubBase").top, e, "msub: height is determined by the top/bottom sides of base/scripts");
+      assert_approx_equals(getBox("msup").height, getBox("msupBase").bottom - Math.min(getBox("msupSup").top, getBox("msupBase").top), e, "msup: height is determined by the top/bottom sides of base/scripts");
+      assert_approx_equals(getBox("msubsup").height, Math.max(getBox("msubSub").bottom, getBox("msubBase").bottom) - Math.min(getBox("msupSup").top, getBox("msupBase").top), e, "msubsup: height is determined by the top/bottom sides of base/scripts");
+    }, "Height of scripted elements");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math>
+      <mspace id="baseline" width="30px" height="2px" depth="0px" mathbackground="blue"/>
+      <msub id="msub" mathbackground="green">
+        <mspace id="msubBase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="msubSub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msub>
+      <msup id="msup" mathbackground="blue">
+        <mspace id="msupBase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="msupSup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msup>
+      <msubsup id="msubsup" mathbackground="green">
+        <mspace id="msubsupBase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="msubsupSub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="msubsupSup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msubsup>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html
new file mode 100644
index 0000000..abef28d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-2.html
@@ -0,0 +1,163 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Subscripts and Superscripts metrics</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Basic metrics for the mmultiscript element.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+</style>
+<script>
+  /* This test does not use any specific fonts and so the exact rules are not
+  specified precisely. We assume reasonable values for script shifts and
+  spacing. */
+
+  function getBox(aId) {
+    var box = document.getElementById(aId).getBoundingClientRect();
+    box.middle = (box.bottom + box.top) / 2;
+    return box;
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+    test(function() {
+      var e = 1;
+      assert_less_than_equal(getBox("msubBase").right, getBox("msubSub").left, e, "subscript is after base");
+      assert_less_than_equal(getBox("msupBase").right, getBox("msupSup").left, e, "superscript is after base");
+      assert_less_than_equal(getBox("msubsupBase").right, getBox("msubsupSub").left, e, "subscript is after base");
+      assert_less_than_equal(getBox("msubsupBase").right, getBox("msubsupSup").left, e, "superscript is after base");
+
+     assert_greater_than_equal(getBox("premsubBase").right, getBox("premsubSub").left, e, "subscript is before base");
+      assert_greater_than_equal(getBox("premsupBase").right, getBox("premsupSup").left, e, "superscript is before base");
+      assert_greater_than_equal(getBox("premsubsupBase").right, getBox("premsubsupSub").left, e, "subscript is before base");
+      assert_greater_than_equal(getBox("premsubsupBase").right, getBox("premsubsupSup").left, e, "superscript is before base");
+
+      e = 3;
+      assert_approx_equals(getBox("msubBase").right, getBox("msubSub").left, e, "msub: space between base and subscript is small");
+      assert_approx_equals(getBox("msubBase").right, getBox("msubSub").left, e, "msub: subscript is after base");
+      assert_approx_equals(getBox("msupBase").right, getBox("msupSup").left, e, "msup: superscript is after base");
+      assert_approx_equals(getBox("msubsupBase").right, getBox("msubsupSub").left, e, "msubsup: subscript is after base");
+      assert_approx_equals(getBox("msubsupBase").right, getBox("msubsupSup").left, e, "msubsup: superscript is after base");
+
+      assert_approx_equals(getBox("premsubBase").left, getBox("premsubSub").right, e, "msub: space between base and subscript is small");
+      assert_approx_equals(getBox("premsubBase").left, getBox("premsubSub").right, e, "msub: subscript is after base");
+      assert_approx_equals(getBox("premsupBase").left, getBox("premsupSup").right, e, "msup: superscript is after base");
+      assert_approx_equals(getBox("premsubsupBase").left, getBox("premsubsupSub").right, e, "msubsup: subscript is after base");
+      assert_approx_equals(getBox("premsubsupBase").left, getBox("premsubsupSup").right, e, "msubsup: superscript is after base");
+    }, "Respective horizontal positions");
+
+    test(function() {
+      var e = 1;
+      assert_approx_equals(getBox("msubBase").middle, getBox("baseline").bottom, e, "base is placed on the baseline");
+      assert_approx_equals(getBox("msupBase").middle, getBox("baseline").bottom, e, "base is placed on the baseline");
+      assert_approx_equals(getBox("msubsupBase").middle, getBox("baseline").bottom, e, "base is placed on the baseline");
+      assert_approx_equals(getBox("premsubBase").middle, getBox("baseline").bottom, e, "base is placed on the baseline");
+      assert_approx_equals(getBox("premsupBase").middle, getBox("baseline").bottom, e, "base is placed on the baseline");
+      assert_approx_equals(getBox("premsubsupBase").middle, getBox("baseline").bottom, e, "base is placed on the baseline");
+    }, "Alignment of the base on the baseline");
+
+    test(function() {
+      var e = 3;
+      assert_approx_equals(getBox("msubSub").middle, getBox("msubBase").bottom, e, "script is placed at the bottom of the base");
+      assert_approx_equals(getBox("msupSup").middle, getBox("msupBase").top, e, "script is placed at the top of the base");
+      assert_approx_equals(getBox("msubsupSub").middle, getBox("msubsupBase").bottom, e, "script is placed at the bottom of the base");
+      assert_approx_equals(getBox("msubsupSup").middle, getBox("msubsupBase").top, e, "script is placed at the top of the base");
+
+      assert_approx_equals(getBox("premsubSub").middle, getBox("premsubBase").bottom, e, "script is placed at the bottom of the base");
+      assert_approx_equals(getBox("premsupSup").middle, getBox("premsupBase").top, e, "script is placed at the top of the base");
+      assert_approx_equals(getBox("premsubsupSub").middle, getBox("premsubsupBase").bottom, e, "script is placed at the bottom of the base");
+      assert_approx_equals(getBox("premsubsupSup").middle, getBox("premsubsupBase").top, e, "script is placed at the top of the base");
+    }, "Vertical position of scripts");
+
+    test(function() {
+      var e = 3;
+      assert_approx_equals(getBox("msub").width, getBox("msubSub").right - getBox("msubBase").left, e, "width is determined by the left/right sides of base/script (+ some space after script)");
+      assert_approx_equals(getBox("msup").width, getBox("msupSup").right - getBox("msupBase").left, e, "width is determined by the left/right sides of base/script (+ some space after script)");
+      assert_approx_equals(getBox("msubsup").width, Math.max(getBox("msubsupSub").right, getBox("msubsupSup").right) - getBox("msubsupBase").left, e, "width is determined by the left/right sides of base/scripts (+ some space after script)");
+
+      assert_approx_equals(getBox("premsub").width, getBox("premsubBase").right - getBox("premsubSub").left, e, "width is determined by the right/left sides of base/script (+ some space after script)");
+      assert_approx_equals(getBox("premsup").width, getBox("premsupBase").right - getBox("premsupSup").left, e, "width is determined by the right/left sides of base/script (+ some space after script)");
+      assert_approx_equals(getBox("premsubsup").width, getBox("premsubsupBase").right - Math.min(getBox("premsubsupSub").left, getBox("premsubsupSup").left), e, "width is determined by the right/left sides of base/scripts (+ some space after script)");
+    }, "Width of scripted elements");
+
+    test(function() {
+      var e = 1;
+      assert_greater_than_equal(getBox("msub").height, getBox("msubBase").height, e, "height is at least the one of the base");
+      assert_greater_than_equal(getBox("msup").height, getBox("msupBase").height, e, "height is at least the one of the base");
+      assert_greater_than_equal(getBox("msubsup").height, getBox("msubsupBase").height, e, "height is at least the one of the base");
+      assert_greater_than_equal(getBox("premsub").height, getBox("premsubBase").height, e, "height is at least the one of the base");
+      assert_greater_than_equal(getBox("premsup").height, getBox("premsupBase").height, e, "height is at least the one of the base");
+  assert_greater_than_equal(getBox("premsubsup").height, getBox("premsubsupBase").height, e, "height is at least the one of the base");
+
+      e = 3;
+      assert_approx_equals(getBox("msub").height, Math.max(getBox("msubSub").bottom, getBox("msubBase").bottom) - getBox("msubBase").top, e, "msub height is determined by the top/bottom sides of base/scripts");
+      assert_approx_equals(getBox("msup").height, getBox("msupBase").bottom - Math.min(getBox("msupSup").top, getBox("msupBase").top), e, "msup height is determined by the top/bottom sides of base/scripts");
+      assert_approx_equals(getBox("msubsup").height, Math.max(getBox("msubSub").bottom, getBox("msubBase").bottom) - Math.min(getBox("msupSup").top, getBox("msupBase").top), e, "msubsup height is determined by the top/bottom sides of base/scripts");
+      assert_approx_equals(getBox("premsub").height, Math.max(getBox("premsubSub").bottom, getBox("premsubBase").bottom) - getBox("premsubBase").top, e, "msub height is determined by the top/bottom sides of base/scripts");
+      assert_approx_equals(getBox("premsup").height, getBox("premsupBase").bottom - Math.min(getBox("premsupSup").top, getBox("premsupBase").top), e, "msup height is determined by the top/bottom sides of base/scripts");
+      assert_approx_equals(getBox("premsubsup").height, Math.max(getBox("premsubSub").bottom, getBox("premsubBase").bottom) - Math.min(getBox("premsupSup").top, getBox("premsupBase").top), e, "msubsup height is determined by the top/bottom sides of base/scripts");
+    }, "Height of scripted elements");
+
+    test(function() {
+      ["none", "mprescripts"].forEach(function(name) {
+        var elements = document.getElementsByTagName(name);
+        for (var i = 0; i < elements.length; i++) {
+          var box = elements[i].getBoundingClientRect();
+          assert_equals(box.height * box.width, 0, "<" + name + "> " + i + " has zero is empty");
+        }
+      });
+    }, "Size of empty elements");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math>
+      <mspace id="baseline" width="30px" height="2px" depth="0px" mathbackground="blue"/>
+      <mmultiscripts id="msub" mathbackground="green">
+        <mspace id="msubBase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="msubSub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <none/>
+      </mmultiscripts>
+      <mmultiscripts id="msup" mathbackground="green">
+        <mspace id="msupBase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <none/>
+        <mspace id="msupSup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+      <mmultiscripts id="msubsup" mathbackground="green">
+        <mspace id="msubsupBase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="msubsupSub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="msubsupSup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+      <mmultiscripts id="premsub" mathbackground="green">
+        <mspace id="premsubBase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="premsubSub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <none/>
+      </mmultiscripts>
+      <mmultiscripts id="premsup" mathbackground="green">
+        <mspace id="premsupBase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mprescripts/>
+        <none/>
+        <mspace id="premsupSup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+      <mmultiscripts id="premsubsup" mathbackground="green">
+        <mspace id="premsubsupBase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="premsubsupSub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="premsubsupSup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html
new file mode 100644
index 0000000..c497189
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-3.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Subscripts and Superscripts metrics</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Basic metrics for the mmultiscript element with many scripts.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+</style>
+<script>
+  /* This test does not use any specific fonts and so the exact rules are not
+  specified precisely. We assume reasonable values for script shifts and
+  spacing. */
+
+  function getBox(aId) {
+    var box = document.getElementById(aId).getBoundingClientRect();
+    box.middle = (box.bottom + box.top) / 2;
+    return box;
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+    test(function() {
+      var e = 1;
+      for (var i = 0; i < 5; i++)
+        assert_approx_equals(getBox("multi" + i + "base").middle, getBox("baseline").bottom, e, "base " + i + "is placed on the baseline");
+    }, "Alignment of the base on the baseline");
+
+    test(function() {
+      var e = 5;
+      assert_approx_equals(getBox("multi0").width, 30, e, "width of multi0");
+      assert_approx_equals(getBox("multi0").height, 30, e, "height of multi0");
+      assert_approx_equals(getBox("multi1").width, 30, e, "width of multi1");
+      assert_approx_equals(getBox("multi1").height, 30, e, "height of multi1");
+      for (i = 2; i <= 4; i++) {
+        var scriptedBox = getBox("multi" + i);
+        var lastPostScript = getBox("multi" + i + "postsup" + (i - 1));
+        var firstPreScript = getBox("multi" + i + "presub1");
+        assert_approx_equals(scriptedBox.height, firstPreScript.bottom - lastPostScript.top, e, "height of multiscript" + i);
+        assert_approx_equals(scriptedBox.width, lastPostScript.right - firstPreScript.left, e, "width of multiscript" + i);
+      }
+    }, "Dimensions of the scripted elements");
+
+    test(function() {
+      var e = 3;
+      for (var i = 2; i <= 4; i++) {
+        var base = getBox("multi" + i + "base");
+        for (var j = 1; j < i; j++) {
+          var presup = getBox("multi" + i + "presup" + j);
+          var postsup = getBox("multi" + i + "postsup" + j);
+          var presub = getBox("multi" + i + "presub" + j);
+          var postsub = getBox("multi" + i + "postsub" + j);
+          assert_approx_equals(base.top, presup.middle, e, "multi" + i + " " + j + " presup script");
+          assert_approx_equals(base.top, postsup.middle, e, "multi" + i + " " + j + " postsup script");
+          assert_approx_equals(base.bottom, presub.middle, e, "multi" + i + " " + j + " presub script");
+          assert_approx_equals(base.bottom, postsub.middle, e, "multi" + i + " " + j + " postsub script");
+        }
+      }
+    }, "Vertical positions of scripts");
+
+    test(function() {
+      var e = 1;
+      for (var i = 2; i <= 4; i++) {
+        var base = getBox("multi" + i + "base");
+        for (var j = 1; j < i; j++) {
+          var presup = getBox("multi" + i + "presup" + j);
+          var postsup = getBox("multi" + i + "postsup" + j);
+          var presub = getBox("multi" + i + "presub" + j);
+          var postsub = getBox("multi" + i + "postsub" + j);
+          assert_approx_equals(presup.right, presub.right, e, "multi" + i + "pre");
+          assert_approx_equals(postsup.left, postsub.left, e, "multi" + i + "post");
+        }
+      }
+    }, "Horizontal alignment of scripts");
+
+    test(function() {
+      for (var i = 2; i <= 4; i++) {
+        var base = getBox("multi" + i + "base");
+        var firstPostScript = getBox("multi" + i + "postsub1");
+        var lastPreScript = getBox("multi" + i + "presup" + (i - 1));
+        assert_less_than_equal(base.right, firstPostScript.left, 1, "postcripts are after base");
+        assert_less_than_equal(lastPreScript.right, base.left, 1, "prescripts are before base");
+        assert_approx_equals(base.right, firstPostScript.left, 5, "spacing after base is not too large");
+        assert_approx_equals(lastPreScript.right, base.left, 5, "spacing before base is not too large");
+        for (var j = 1; j < i - 1; j++) {
+          var post = getBox("multi" + i + "postsub" + j);
+          var postNext = getBox("multi" + i + "postsub" + (j + 1));
+          var pre = getBox("multi" + i + "presup" + j);
+          var preNext = getBox("multi" + i + "presup" + (j + 1));
+          assert_less_than_equal(post.right, postNext.left, 1, "multi" + i + "order post" + j + " is before its successor");
+          assert_less_than_equal(pre.right, preNext.left, 1, "multi" + i + "order pre" + j + " is before its successor");
+          assert_approx_equals(post.right, postNext.left, 5, "multi" + i + "space after post" + j + " is not too large");
+          assert_approx_equals(pre.right, preNext.left, 5, "multi" + i + "space after pre" + j + " is not too large");
+        }
+      }
+    }, "Horizontal positions of scripts");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math>
+      <mspace id="baseline" width="30px" height="2px" depth="0px" mathbackground="blue"/>
+      <mmultiscripts id="multi0" mathbackground="green">
+        <mspace id="multi0base" width="30px" height="15px" depth="15px" mathbackground="black"/>
+      </mmultiscripts>
+      <mmultiscripts id="multi1" mathbackground="green">
+        <mspace id="multi1base" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mprescripts/>
+      </mmultiscripts>
+      <mmultiscripts id="multi2" mathbackground="green">
+        <mspace id="multi2base" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="multi2postsub1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi2postsup1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="multi2presub1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi2presup1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+      <mmultiscripts id="multi3" mathbackground="green">
+        <mspace id="multi3base" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="multi3postsub1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi3postsup1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi3postsub2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi3postsup2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="multi3presub1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi3presup1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi3presub2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi3presup2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+      <mmultiscripts id="multi4" mathbackground="green">
+        <mspace id="multi4base" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="multi4postsub1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4postsup1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4postsub2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4postsup2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4postsub3" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4postsup3" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="multi4presub1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4presup1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4presub2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4presup2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4presub3" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi4presup3" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+      <mmultiscripts id="multi5" mathbackground="green">
+        <mspace id="multi5base" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="multi5postsub1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5postsup1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5postsub2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5postsup2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5postsub3" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5postsup3" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5postsub4" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5postsup4" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="multi5presub1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5presup1" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5presub2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5presup2" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5presub3" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5presup3" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5presub4" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi5presup4" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html
new file mode 100644
index 0000000..2acc774
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-4.html
@@ -0,0 +1,124 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Subscripts and Superscripts metrics</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Verify metrics of scripted elements for bases of different heights.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+</style>
+<script>
+  /* This test does not use any specific fonts and so the exact rules are not
+  specified precisely. We assume reasonable values for script shifts and
+  spacing. */
+
+  function getBox(aId) {
+    var box = document.getElementById(aId).getBoundingClientRect();
+    box.middle = (box.bottom + box.top) / 2;
+    return box;
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  var sizeArray = [50, 75, 100];
+
+  function runTests() {
+    test(function() {
+      var e = 1;
+      sizeArray.forEach(function(size) {
+         assert_approx_equals(getBox("msub" + size + "base").middle, getBox("baseline").bottom, e, "msub base " + size + "is placed on the baseline");
+         assert_approx_equals(getBox("msup" + size + "base").middle, getBox("baseline").bottom, e, "msup base " + size + "is placed on the baseline");
+         assert_approx_equals(getBox("msubsup" + size + "base").middle, getBox("baseline").bottom, e, "msubsup base " + size + "is placed on the baseline");
+         assert_approx_equals(getBox("multi" + size + "base").middle, getBox("baseline").bottom, e, "mmultiscripts base " + size + "is placed on the baseline");
+      });
+    }, "Alignment on the baseline for bases of different heights");
+
+    test(function() {
+      var e = 5;
+      sizeArray.forEach(function(size) {
+         assert_approx_equals(getBox("msub" + size + "sub").middle, getBox("msub" + size + "base").bottom, e, "msub script " + size + "is placed at the top of of the base");
+      });
+    }, "Vertical position of the scripts for bases of different heights");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math>
+      <mspace id="baseline" width="30px" height="2px" depth="0px" mathbackground="blue"/>
+      <msub id="msub50">
+        <mspace id="msub50base" width="30px" height="50px" depth="50px" mathbackground="black"/>
+        <mspace id="msub50sub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msub>
+      <msup id="msup50">
+        <mspace id="msup50base" width="30px" height="50px" depth="50px" mathbackground="black"/>
+        <mspace id="msup50sup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msup>
+      <msubsup id="msubsup50">
+        <mspace id="msubsup50base" width="30px" height="50px" depth="50px" mathbackground="black"/>
+        <mspace id="msubsup50sub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="msubsup50sup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msubsup>
+      <mmultiscripts id="multi50">
+        <mspace id="multi50base" width="30px" height="50px" depth="50px" mathbackground="black"/>
+        <mspace id="multi50postsub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi50postsup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="multi50presub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi50presup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+      <msub id="msub75">
+        <mspace id="msub75base" width="30px" height="75px" depth="75px" mathbackground="black"/>
+        <mspace id="msub75sub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msub>
+      <msup id="msup75">
+        <mspace id="msup75base" width="30px" height="75px" depth="75px" mathbackground="black"/>
+        <mspace id="msup75sup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msup>
+      <msubsup id="msubsup75">
+        <mspace id="msubsup75base" width="30px" height="75px" depth="75px" mathbackground="black"/>
+        <mspace id="msubsup75sub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="msubsup75sup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msubsup>
+      <mmultiscripts id="multi75">
+        <mspace id="multi75base" width="30px" height="75px" depth="75px" mathbackground="black"/>
+        <mspace id="multi75postsub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi75postsup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="multi75presub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi75presub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+      <msub id="msub100">
+        <mspace id="msub100base" width="30px" height="100px" depth="100px" mathbackground="black"/>
+        <mspace id="msub100sub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msub>
+      <msup id="msup100">
+        <mspace id="msup100base" width="30px" height="100px" depth="100px" mathbackground="black"/>
+        <mspace id="msup100sup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msup>
+      <msubsup id="msubsup100">
+        <mspace id="msubsup100base" width="30px" height="100px" depth="100px" mathbackground="black"/>
+        <mspace id="msubsup100sub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="msubsup100sup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </msubsup>
+      <mmultiscripts id="multi100">
+        <mspace id="multi100base" width="30px" height="100px" depth="100px" mathbackground="black"/>
+        <mspace id="multi100postsub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi100postsup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="multi100presub" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="multi100presup" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mmultiscripts>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html
new file mode 100644
index 0000000..19b6eee66
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-5.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Subscripts and Superscripts metrics</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Verify metrics of scripted elements with tall scripts.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+</style>
+<script>
+  /* This test does not use any specific fonts and so the exact rules are not
+  specified precisely. We assume reasonable values for script shifts and
+  spacing. */
+
+  function getBox(aId) {
+    var box = document.getElementById(aId).getBoundingClientRect();
+    box.middle = (box.bottom + box.top) / 2;
+    return box;
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  var sizeArray = [50, 75, 100];
+
+  function runTests() {
+    test(function() {
+      var e = 1;
+       assert_approx_equals(getBox("msubbase").middle, getBox("baseline").bottom, e, "msub base is placed on the baseline");
+       assert_approx_equals(getBox("msupbase").middle, getBox("baseline").bottom, e, "msup base is placed on the baseline");
+       assert_approx_equals(getBox("msubsupbase").middle, getBox("baseline").bottom, e, "msubsup base is placed on the baseline");
+       assert_approx_equals(getBox("multibase").middle, getBox("baseline").bottom, e, "mmultiscripts baseis placed on the baseline");
+    }, "Alignment on the baseline with different and large script heights");
+
+    test(function() {
+      assert_greater_than(getBox("msubsub").top, getBox("msubbase").top, "msub: subscript is below the top of the base");
+      assert_less_than(getBox("msupsup").bottom, getBox("msupbase").bottom, "msup: supscript is above the bottom of the base");
+      assert_greater_than(getBox("msubsupsub").top, getBox("msubsupbase").top, "msubsup: subscript is below the top of the base");
+      assert_less_than(getBox("msubsupsup").bottom, getBox("msubsupbase").bottom, "msubsup: supscript is above the bottom of the base");
+      assert_greater_than(getBox("multipostsub").top, getBox("multibase").top, "mmultiscripts: postsubscript is below the top of the base");
+      assert_less_than(getBox("multipostsup").bottom, getBox("multibase").bottom, "mmultiscripts: postsupscript is above the bottom of the base");
+      assert_greater_than(getBox("multipresub").top, getBox("multibase").top, "mmultiscripts: presubscript is below the top of the base");
+      assert_less_than(getBox("multipresup").bottom, getBox("multibase").bottom, "mmultiscripts: presupscript is above the bottom of the base");
+    }, "Tall subscripts/superscripts are not placed too high/low");
+
+    test(function() {
+      assert_greater_than(getBox("msubsupsub").top, getBox("msubsupsup").bottom, "msubsup: subscript is below the superscript");
+      assert_greater_than(getBox("multipresub").top, getBox("multipresup").bottom, "mmultiscripts: presubscript is below the presuperscript");
+      assert_greater_than(getBox("multipostsub").top, getBox("multipostsup").bottom, "mmultiscripts: postsubscript is below the postsuperscript");
+    }, "No collisions for tall subscripts and superscripts");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math>
+      <mspace id="baseline" width="30px" height="2px" depth="0px" mathbackground="blue"/>
+      <msub id="msub">
+        <mspace id="msubbase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="msubsub" width="10px" height="50px" depth="50px" mathbackground="black"/>
+      </msub>
+      <msup id="msup">
+        <mspace id="msupbase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="msupsup" width="10px" height="75px" depth="75px" mathbackground="black"/>
+      </msup>
+      <msubsup id="msubsup">
+        <mspace id="msubsupbase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="msubsupsub" width="10px" height="50px" depth="50px" mathbackground="black"/>
+        <mspace id="msubsupsup" width="10px" height="75px" depth="75px" mathbackground="black"/>
+      </msubsup>
+      <mmultiscripts id="multi">
+        <mspace id="multibase" width="30px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="multipostsub" width="10px" height="50px" depth="50px" mathbackground="black"/>
+        <mspace id="multipostsup" width="10px" height="75px" depth="75px" mathbackground="black"/>
+        <mprescripts/>
+        <mspace id="multipresub" width="10px" height="50px" depth="50px" mathbackground="black"/>
+        <mspace id="multipresup" width="10px" height="75px" depth="75px" mathbackground="black"/>
+      </mmultiscripts>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html
new file mode 100644
index 0000000..9bc6bcb
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-1.html
@@ -0,0 +1,335 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Subscripts and Superscripts parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Elements msub, msup, subsup and msubsup correctly use the subscript and superscript parameters from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: spaceafterscript3000;
+    src: url("/fonts/math/scripts-spaceafterscript3000.woff");
+  }
+  @font-face {
+    font-family: superscriptshiftup7000;
+    src: url("/fonts/math/scripts-superscriptshiftup7000.woff");
+  }
+  @font-face {
+    font-family: superscriptshiftupcramped5000;
+    src: url("/fonts/math/scripts-superscriptshiftupcramped5000.woff");
+  }
+  @font-face {
+    font-family: subscriptshiftdown6000;
+    src: url("/fonts/math/scripts-subscriptshiftdown6000.woff");
+  }
+  @font-face {
+    font-family: subsuperscriptgapmin11000;
+    src: url("/fonts/math/scripts-subsuperscriptgapmin11000.woff");
+  }
+  @font-face {
+    font-family: subsuperscriptgapmin11000superscriptbottommaxwithsubscript3000;
+    src: url("/fonts/math/scripts-subsuperscriptgapmin11000-superscriptbottommaxwithsubscript3000.woff");
+  }
+  @font-face {
+    font-family: subscripttopmax4000;
+    src: url("/fonts/math/scripts-subscripttopmax4000.woff");
+  }
+  @font-face {
+    font-family: superscriptbottommin8000;
+    src: url("/fonts/math/scripts-superscriptbottommin8000.woff");
+  }
+  @font-face {
+    font-family: subscriptbaselinedropmin9000;
+    src: url("/fonts/math/scripts-subscriptbaselinedropmin9000.woff");
+  }
+  @font-face {
+    font-family: superscriptbaselinedropmax10000;
+    src: url("/fonts/math/scripts-superscriptbaselinedropmax10000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 1;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      var v = 3000 * emToPx;
+      assert_approx_equals(getBox("ref001").left - getBox("sub001").right, v, epsilon, "msub: Space after subscript");
+      assert_approx_equals(getBox("ref002").left - getBox("sup002").right, v, epsilon, "msup: Space after superscript");
+      assert_approx_equals(getBox("ref003").left - getBox("sup003").right, v, epsilon, "msubsup: Space after superscript");
+      assert_approx_equals(getBox("sup0042").left - getBox("sup0041").right, v, epsilon, "mmultiscripts: Space after first superscript");
+      assert_approx_equals(getBox("sup0043").left - getBox("sup0042").right, v, epsilon, "mmultiscripts: Space after second superscript");
+      assert_approx_equals(getBox("ref004").left - getBox("sup0043").right, v, epsilon, "mmultiscripts: Space after last superscript");
+    }, "SpaceAfterScript");
+
+    test(function() {
+      var v = 7000 * emToPx;
+      assert_approx_equals(getBox("ref101").bottom - getBox("sup102").bottom, v, epsilon, "msup: Superscript shift");
+      assert_approx_equals(getBox("ref101").bottom - getBox("sup103").bottom, v, epsilon, "msubsup: Superscript shift");
+      assert_approx_equals(getBox("ref101").bottom - getBox("sup1041").bottom, v, epsilon, "mmultiscripts: First superscript shift");
+      assert_approx_equals(getBox("ref101").bottom - getBox("sup1042").bottom, v, epsilon, "mmultiscripts: Second superscript shift");
+      assert_approx_equals(getBox("ref101").bottom - getBox("sup1043").bottom, v, epsilon, "mmultiscripts: Last superscript shift");
+    }, "SuperscriptShiftUp");
+
+    test(function() {
+      var v = 5000 * emToPx;
+      assert_approx_equals(getBox("ref201").bottom - getBox("sup202").bottom, v, epsilon, "msup: Superscript shift");
+      assert_approx_equals(getBox("ref201").bottom - getBox("sup203").bottom, v, epsilon, "msubsup: Superscript shift");
+      assert_approx_equals(getBox("ref201").bottom - getBox("sup2041").bottom, v, epsilon, "mmultiscripts: First superscript shift");
+      assert_approx_equals(getBox("ref201").bottom - getBox("sup2042").bottom, v, epsilon, "mmultiscripts: Second superscript shift");
+      assert_approx_equals(getBox("ref201").bottom - getBox("sup2043").bottom, v, epsilon, "mmultiscripts: Last superscript shift");
+    }, "SuperscriptShiftUpCramped");
+
+    test(function() {
+      var v = 6000 * emToPx;
+      assert_approx_equals(getBox("sub301").bottom - getBox("ref300").bottom, v, epsilon, "msup: Subscript shift");
+      assert_approx_equals(getBox("sub302").bottom - getBox("ref300").bottom, v, epsilon, "msubsup: Subscript shift");
+      assert_approx_equals(getBox("sub303").bottom - getBox("ref300").bottom, v, epsilon, "mmultiscripts: First subscript shift");
+      assert_approx_equals(getBox("sub304").bottom - getBox("ref300").bottom, v, epsilon, "mmultiscripts: Second subscript shift");
+    }, "SubscriptShiftDown");
+
+    test(function() {
+      var v = 11000 * emToPx;
+      assert_approx_equals(getBox("sub4011").top - getBox("sup4012").bottom, v, epsilon, "msubsup: SubSuperscript gap");
+      assert_approx_equals(getBox("sub4021").top - getBox("sup4022").bottom, v, epsilon, "mmultiscripts: SubSuperscript gap");
+    }, "SubSuperscriptGapMin");
+
+    test(function() {
+      var v1 = 11000 * emToPx;
+      var v2 = 3000 * emToPx;
+      assert_approx_equals(getBox("sub501").top - getBox("sup501").bottom, v1, epsilon, "msubsup: SubSuperscript gap");
+      assert_approx_equals(getBox("ref500").bottom - getBox("sup501").bottom, v2, epsilon, "msubsup: Superscript bottom");
+      assert_approx_equals(getBox("sub502").top - getBox("sup502").bottom, v1, epsilon, "mmultiscripts: SubSuperscript gap");
+      assert_approx_equals(getBox("ref500").bottom - getBox("sup502").bottom, v2, epsilon, "mmultiscripts: Superscript bottom");
+    }, "SuperscriptBottomMaxWithSubscript");
+
+    test(function() {
+      var v = 4000 * emToPx;
+      assert_approx_equals(getBox("ref600").bottom - getBox("sub601").top, v, epsilon, "msub: Subscript top");
+    }, "SubscriptTopMax");
+
+    test(function() {
+      var v = 8000 * emToPx;
+      assert_approx_equals(getBox("ref700").bottom - getBox("sub701").bottom, v, epsilon, "msub: Superscript bottom");
+    }, "SuperscriptBottomMin");
+
+    test(function() {
+      var v = 9000 * emToPx;
+      assert_approx_equals(getBox("sub801").bottom - getBox("base801").bottom, v, epsilon, "msub: Superscript drop");
+    }, "SubscriptBaselineDrop");
+
+    test(function() {
+      var v = 10000 * emToPx;
+      assert_approx_equals(getBox("sup901").bottom - getBox("base901").top, v, epsilon, "msup: Superscript drop");
+    }, "SuperscriptBaselineDrop");
+
+    done();
+  }
+</script>
+</head>
+<body>
+    <p>
+      <math style="font-family: spaceafterscript3000;">
+        <msub>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sub001" height="1em" width="1em" mathbackground="red"/>
+        </msub>
+        <mspace id="ref001" height="1em" width="1em" mathbackground="green"/>
+      </math>
+      <math style="font-family: spaceafterscript3000;">
+        <msup>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sup002" height="1em" width="1em" mathbackground="red"/>
+        </msup>
+        <mspace id="ref002" height="1em" width="1em" mathbackground="green"/>
+      </math>
+      <math style="font-family: spaceafterscript3000;">
+        <msubsup>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace/>
+          <mspace id="sup003" height="1em" width="1em" mathbackground="red"/>
+        </msubsup>
+        <mspace id="ref003" height="1em" width="1em" mathbackground="green"/>
+      </math>
+      <math style="font-family: spaceafterscript3000;">
+        <mmultiscripts>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <none/>
+          <mspace id="sup0041" height="1em" width="1em" mathbackground="red"/>
+          <none/>
+          <mspace id="sup0042" height="1em" width="1em" mathbackground="red"/>
+          <none/>
+          <mspace id="sup0043" height="1em" width="1em" mathbackground="red"/>
+        </mmultiscripts>
+        <mspace id="ref004" height="1em" width="1em" mathbackground="green"/>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: superscriptshiftup7000;">
+        <mspace id="ref101" height="1em" width="1em" mathbackground="green"/>
+        <msup>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sup102" height="1em" width="1em" mathbackground="red"/>
+        </msup>
+        <msubsup>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace height="1em" width="1em" mathbackground="red"/>
+          <mspace id="sup103" height="1em" width="1em" mathbackground="red"/>
+        </msubsup>
+        <mmultiscripts>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <none/>
+          <mspace id="sup1041" height="1em" width="1em" mathbackground="red"/>
+          <none/>
+          <mspace id="sup1042" height="1em" width="1em" mathbackground="red"/>
+          <none/>
+          <mspace id="sup1043" height="1em" width="1em" mathbackground="red"/>
+        </mmultiscripts>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: superscriptshiftupcramped5000;">
+        <msqrt>
+          <mspace id="ref201" height="1em" width="1em" mathbackground="green"/>
+          <msup>
+            <mspace height="2em" width="2em" mathbackground="blue"/>
+            <mspace id="sup202" height="1em" width="1em" mathbackground="red"/>
+          </msup>
+          <msubsup>
+            <mspace height="2em" width="2em" mathbackground="blue"/>
+            <mspace height="1em" width="1em" mathbackground="blue"/>
+            <mspace id="sup203" height="1em" width="1em" mathbackground="red"/>
+          </msubsup>
+          <mmultiscripts>
+            <mspace height="2em" width="2em" mathbackground="blue"/>
+            <none/>
+            <mspace id="sup2041" height="1em" width="1em" mathbackground="red"/>
+            <none/>
+            <mspace id="sup2042" height="1em" width="1em" mathbackground="red"/>
+            <none/>
+            <mspace id="sup2043" height="1em" width="1em" mathbackground="red"/>
+          </mmultiscripts>
+        </msqrt>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: subscriptshiftdown6000;">
+        <mspace id="ref300" height="1em" width="1em" mathbackground="green"/>
+        <msub>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sub301" height="1em" width="1em" mathbackground="red"/>
+        </msub>
+        <msubsup>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sub302" height="1em" width="1em" mathbackground="red"/>
+          <mspace height="1em" width="1em" mathbackground="blue"/>
+        </msubsup>
+        <mmultiscripts>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sub303" height="1em" width="1em" mathbackground="red"/>
+          <none/>
+          <mspace id="sub304" height="1em" width="1em" mathbackground="red"/>
+          <none/>
+        </mmultiscripts>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: subsuperscriptgapmin11000;">
+        <msubsup>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sub4011" height="1em" width="1em" mathbackground="red"/>
+          <mspace id="sup4012" height="1em" width="1em" mathbackground="red"/>
+        </msubsup>
+        <mmultiscripts>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <none/>
+          <none/>
+          <mspace id="sub4021" height="1em" width="1em" mathbackground="red"/>
+          <mspace id="sup4022" height="1em" width="1em" mathbackground="red"/>
+        </mmultiscripts>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: subsuperscriptgapmin11000superscriptbottommaxwithsubscript3000;">
+        <mspace id="ref500" height="1em" width="1em" mathbackground="green"/>
+        <msubsup>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sub501" height="1em" width="1em" mathbackground="red"/>
+          <mspace id="sup501" height="1em" width="1em" mathbackground="red"/>
+        </msubsup>
+        <mmultiscripts>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <none/>
+          <none/>
+          <mspace id="sub502" height="1em" width="1em" mathbackground="red"/>
+          <mspace id="sup502" height="1em" width="1em" mathbackground="red"/>
+        </mmultiscripts>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: subscripttopmax4000;">
+        <mspace id="ref600" height="1em"
+                width="1em" mathbackground="green"/>
+        <msub>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sub601" height="10em"
+                  width="1em" mathbackground="red"/>
+        </msub>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: superscriptbottommin8000;">
+        <mspace id="ref700" height="1em"
+                width="1em" mathbackground="green"/>
+        <msup>
+          <mspace height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sub701" depth="1em"
+                  width="1em" mathbackground="red"/>
+        </msup>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: subscriptbaselinedropmin9000;">
+        <msub>
+          <mspace id="base801" height="2em" width="2em" mathbackground="blue"/>
+          <mspace id="sub801" height="1em"
+                  width="1em" mathbackground="red"/>
+        </msub>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: superscriptbaselinedropmax10000;">
+        <msup>
+          <mspace id="base901" height="15em" width="2em" mathbackground="blue"/>
+          <mspace id="sup901" height="1em"
+                  width="1em" mathbackground="red"/>
+        </msup>
+      </math>
+    </p>
+    <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html
new file mode 100644
index 0000000..eaa4f0f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/subsup-parameters-2.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Subscripts and Superscripts parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Elements msub, msup, subsup and msubsup correctly use the italic correction from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: largeop-displayoperatorminheight5000;
+    src: url("/fonts/math/largeop-displayoperatorminheight5000.woff");
+  }
+  @font-face {
+    font-family: largeop-displayoperatorminheight2000-2AFF-italiccorrection3000;
+    src: url("/fonts/math/largeop-displayoperatorminheight2000-2AFF-italiccorrection3000.woff");
+  }
+</style>
+<script>
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  /*
+    These two tests verify that:
+    - In msub, the script is at the right of the base minus the italic correction.
+    - In msup, the script is just at the right of the base.
+    - In msubsup, the scripts are shifted by the italic correction.
+    - In mmultiscripts, postscript pairs are shifted by the italic correction.
+    - In mmultiscripts, prescript pairs are vertically aligned.
+  */
+  var epsilon = 1;
+  function runTests() {
+    test(function() {
+      var v = 0;
+      assert_approx_equals(getBox("base001").right - getBox("sub001").left, v, epsilon, "msub");
+      assert_approx_equals(getBox("sup002").left, getBox("base002").right, epsilon, "msup");
+      assert_approx_equals(getBox("sup003").left - getBox("sub003").left, v, epsilon, "msubsup");
+      assert_approx_equals(getBox("sup004").left - getBox("sub004").left, v, epsilon, "mmultiscripts postscripts");
+      assert_approx_equals(getBox("sup005").left - getBox("sub005").left, 0, epsilon, "mmultiscripts prescripts");
+    }, "Null Italic Correction");
+    test(function() {
+      var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+      var v = 3000 * emToPx;
+      assert_approx_equals(getBox("base011").right - getBox("sub011").left, v, epsilon, "msub");
+      assert_approx_equals(getBox("sup012").left, getBox("base012").right, epsilon, "msup");
+      assert_approx_equals(getBox("sup013").left - getBox("sub013").left, v, epsilon, "msubsup");
+      assert_approx_equals(getBox("sup014").left - getBox("sub014").left, v, epsilon, "mmultiscripts postscripts");
+      assert_approx_equals(getBox("sup015").left - getBox("sub015").left, 0, epsilon, "mmultiscripts prescripts");
+    }, "NonNull Italic Correction");
+    done();
+  }
+</script>
+</head>
+<body>
+  <div id="log"></div>
+  <h2>Null Italic Correction</h2>
+  <p>
+    <math displaystyle="true" style="font-family: largeop-displayoperatorminheight5000;">
+      <msub>
+        <mo id="base001" lspace="0px" rspace="0px">&#x2AFF;</mo>
+        <mspace id="sub001" height="1em" width="1em" mathbackground="blue"/>
+      </msub>
+    </math>
+    <math displaystyle="true" style="font-family: largeop-displayoperatorminheight5000;">
+      <msup>
+        <mo id="base002" lspace="0px" rspace="0px">&#x2AFF;</mo>
+        <mspace id="sup002" height="1em" width="1em" mathbackground="blue"/>
+      </msup>
+    </math>
+    <math displaystyle="true" style="font-family: largeop-displayoperatorminheight5000;">
+      <msubsup>
+        <mo lspace="0px" rspace="0px">&#x2AFF;</mo>
+        <mspace id="sub003" height="1em" width="1em" mathbackground="blue"/>
+        <mspace id="sup003" height="1em" width="1em" mathbackground="green"/>
+      </msubsup>
+    </math>
+    <math displaystyle="true" style="font-family: largeop-displayoperatorminheight5000;">
+      <mmultiscripts>
+        <mo lspace="0px" rspace="0px">&#x2AFF;</mo>
+        <mspace id="sub004" height="1em" width="1em" mathbackground="blue"/>
+        <mspace id="sup004" height="1em" width="1em" mathbackground="green"/>
+        <mprescripts/>
+        <mspace id="sub005" height="1em" width="1em" mathbackground="magenta"/>
+        <mspace id="sup005" height="1em" width="1em" mathbackground="cyan"/>
+      </mmultiscripts>
+    </math>
+  </p>
+  <h2>NonNull Italic Correction</h2>
+  <p>
+    <math displaystyle="true" style="font-family: largeop-displayoperatorminheight2000-2AFF-italiccorrection3000;">
+      <msub>
+        <mo id="base011" lspace="0px" rspace="0px">&#x2AFF;</mo>
+        <mspace id="sub011" height="1em" width="1em" mathbackground="blue"/>
+      </msub>
+    </math>
+    <math displaystyle="true" style="font-family: largeop-displayoperatorminheight2000-2AFF-italiccorrection3000;">
+      <msup>
+        <mo id="base012" lspace="0px" rspace="0px">&#x2AFF;</mo>
+        <mspace id="sup012" height="1em" width="1em" mathbackground="blue"/>
+      </msup>
+    </math>
+    <math displaystyle="true" style="font-family: largeop-displayoperatorminheight2000-2AFF-italiccorrection3000;">
+      <msubsup>
+        <mo lspace="0px" rspace="0px">&#x2AFF;</mo>
+        <mspace id="sub013" height="1em" width="1em" mathbackground="blue"/>
+        <mspace id="sup013" height="1em" width="1em" mathbackground="green"/>
+      </msubsup>
+    </math>
+    <math displaystyle="true" style="font-family: largeop-displayoperatorminheight2000-2AFF-italiccorrection3000;">
+      <mmultiscripts>
+        <mo lspace="0px" rspace="0px">&#x2AFF;</mo>
+        <mspace id="sub014" height="1em" width="1em" mathbackground="blue"/>
+        <mspace id="sup014" height="1em" width="1em" mathbackground="green"/>
+        <mprescripts/>
+        <mspace id="sub015" height="1em" width="1em" mathbackground="magenta"/>
+        <mspace id="sup015" height="1em" width="1em" mathbackground="cyan"/>
+      </mmultiscripts>
+    </math>
+  </p>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html
new file mode 100644
index 0000000..6e039b9d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-1.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Underscripts and Overscripts parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Elements munder, mover, munderover correctly .">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace, mo {
+    font-size: 10px;
+  }
+</style>
+<script>
+  /* This test does not use any specific fonts and so the exact rules are not
+  specified precisely. We assume reasonable values for script shifts and
+  spacing. */
+
+  function getBox(aId) {
+    var box = document.getElementById(aId).getBoundingClientRect();
+    box.middle = (box.bottom + box.top) / 2;
+    box.center = (box.left + box.right) / 2;
+    return box;
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+    test(function() {
+      var e = 1;
+      for (var i = 0; i <= 3; i++) {
+        assert_approx_equals(getBox("under" + i + "base").middle, getBox("baseline").bottom, e, "munder " + i + ": base is placed on the baseline");
+        assert_approx_equals(getBox("over" + i + "base").middle, getBox("baseline").bottom, e, "mover " + i + ": base is placed on the baseline");
+      }
+      for (var i = 0; i <= 5; i++) {
+        assert_approx_equals(getBox("underover" + i + "base").middle, getBox("baseline").bottom, e, "munderover " + i + ": base is placed on the baseline");
+      }
+    }, "Alignment of the base on the baseline");
+
+    test(function() {
+      var e = 1;
+      for (var i = 0; i <= 3; i++) {
+        assert_approx_equals(getBox("under" + i + "under").center, getBox("under" + i + "base").center, e, "munder " + i + ": base and script are horizontally centered");
+        assert_approx_equals(getBox("over" + i + "over").center, getBox("over" + i + "base").center, e, "mover " + i + ": base and script are horizontally centered");
+      }
+      for (var i = 0; i <= 5; i++) {
+        assert_approx_equals(getBox("underover" + i + "under").center, getBox("underover" + i + "base").center, e, "munderover " + i + ": base and underscript are horizontally centered");
+        assert_approx_equals(getBox("underover" + i + "over").center, getBox("underover" + i + "base").center, e, "munderover " + i + ": base and overscript are horizontally centered");
+      }
+    }, "Horizontal alignments of base and scripts");
+
+    test(function() {
+      for (var i = 0; i <= 3; i++) {
+        assert_greater_than_equal(getBox("under" + i + "under").top, getBox("under" + i + "base").bottom, "munder " + i + ": script is under base");
+        assert_less_than_equal(getBox("over" + i + "over").bottom, getBox("over" + i + "base").top, "mover " + i + ": script is over base");
+      }
+      for (var i = 0; i <= 5; i++) {
+        assert_greater_than_equal(getBox("underover" + i + "under").top, getBox("underover" + i + "base").bottom, "munderover " + i + ": underscript is under base");
+        assert_less_than_equal(getBox("underover" + i + "over").bottom, getBox("underover" + i + "base").top, "munderover " + i + ": overscript is over base");
+      }
+    }, "Relative vertical positions of base and scripts");
+
+    test(function() {
+      var e = 1;
+      for (var i = 0; i <= 3; i++) {
+        assert_approx_equals(getBox("under" + i).width, Math.max(getBox("under" + i + "base").width, getBox("under" + i + "under").width), e, "munder " + i + ": width is determined by the maximum of width of base and script");
+        assert_approx_equals(getBox("over" + i).width, Math.max(getBox("over" + i + "base").width, getBox("over" + i + "over").width), e, "mover " + i + ": width is determined by the maximum of width of base and script");
+      }
+      for (var i = 0; i <= 5; i++) {
+        assert_approx_equals(getBox("underover" + i).width, Math.max(getBox("underover" + i + "base").width, getBox("underover" + i + "under").width, getBox("underover" + i + "over").width), e, "munderover " + i + ": width is determined by the maximum of width of base and scripts");
+      }
+    }, "Width of scripted elements");
+
+    test(function() {
+      var e = 3.2;
+      for (var i = 0; i <= 3; i++) {
+        assert_approx_equals(getBox("under" + i).height, getBox("under" + i + "base").height + getBox("under" + i + "under").height + e, e, "munder " + i + ": height is determined by the sum of heights of base and script plus some spacing.");
+        assert_approx_equals(getBox("over" + i).height, getBox("over" + i + "base").height + getBox("over" + i + "over").height + e, e, "mover " + i + ": height is determined by the sum of heights of base and script plus some spacing.");
+      }
+      for (var i = 0; i <= 5; i++) {
+        assert_approx_equals(getBox("underover" + i).height, getBox("underover" + i + "base").height + getBox("underover" + i + "under").height + getBox("underover" + i + "over").height + e, e, "munderover " + i + ": height is determined by the sum heights of base and scripts");
+      }
+    }, "Height of scripted elements");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math>
+      <mspace id="baseline" width="30px" height="2px" depth="0px" mathbackground="blue"/>
+      <munder id="under0">
+        <mspace id="under0base" width="30px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="under0under" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </munder>
+      <munder id="under1">
+        <mspace id="under1base" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="under1under" width="30px" height="5px" depth="5px" mathbackground="black"/>
+      </munder>
+      <munder id="under2">
+        <mspace id="under2base" width="10px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="under2under" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </munder>
+      <munder id="under3">
+        <mspace id="under3base" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="under3under" width="10px" height="15px" depth="15px" mathbackground="black"/>
+      </munder>
+      <mover id="over0">
+        <mspace id="over0base" width="30px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="over0over" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mover>
+      <mover id="over1">
+        <mspace id="over1base" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="over1over" width="30px" height="5px" depth="5px" mathbackground="black"/>
+      </mover>
+      <mover id="over2">
+        <mspace id="over2base" width="10px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="over2over" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </mover>
+      <mover id="over3">
+        <mspace id="over3base" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="over3over" width="10px" height="15px" depth="15px" mathbackground="black"/>
+      </mover>
+      <munderover id="underover0">
+        <mspace id="underover0base" width="30px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover0under" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover0over" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </munderover>
+      <munderover id="underover1">
+        <mspace id="underover1base" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover1under" width="30px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover1over" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </munderover>
+      <munderover id="underover2">
+        <mspace id="underover2base" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover2under" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover2over" width="30px" height="5px" depth="5px" mathbackground="black"/>
+      </munderover>
+      <munderover id="underover3">
+        <mspace id="underover3base" width="10px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="underover3under" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover3over" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </munderover>
+      <munderover id="underover4">
+        <mspace id="underover4base" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover4under" width="10px" height="15px" depth="15px" mathbackground="black"/>
+        <mspace id="underover4over" width="10px" height="5px" depth="5px" mathbackground="black"/>
+      </munderover>
+      <munderover id="underover5">
+        <mspace id="underover5base" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover5under" width="10px" height="5px" depth="5px" mathbackground="black"/>
+        <mspace id="underover5over" width="10px" height="15px" depth="15px" mathbackground="black"/>
+      </munderover>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html
new file mode 100644
index 0000000..1e5a6606
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-1.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Underscripts and Overscripts parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Elements munder, mover, munderover correctly use the limit parameters from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace, mo {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: lowerlimitbaselinedropmin3000;
+    src: url("/fonts/math/limits-lowerlimitbaselinedropmin3000.woff");
+  }
+  @font-face {
+    font-family: lowerlimitgapmin11000;
+    src: url("/fonts/math/limits-lowerlimitgapmin11000.woff");
+  }
+  @font-face {
+    font-family: upperlimitbaselinerisemin5000;
+    src: url("/fonts/math/limits-upperlimitbaselinerisemin5000.woff");
+  }
+  @font-face {
+    font-family: upperlimitgapmin7000;
+    src: url("/fonts/math/limits-upperlimitgapmin7000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 1;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      var v = 3000 * emToPx;
+      assert_approx_equals(getBox("under00011").top - getBox("ref0001").bottom,
+                           v, epsilon, "munder: under shift");
+      assert_approx_equals(getBox("under00012").top - getBox("ref0001").bottom,
+                           v, epsilon, "munderover: under shift");
+    }, "LowerLimitBaselineDropMin");
+
+    test(function() {
+      var v = 11000 * emToPx;
+      assert_approx_equals(getBox("under00021").top - getBox("ref0002").bottom,
+                           v, epsilon, "munder: under gap");
+      assert_approx_equals(getBox("under00022").top - getBox("ref0002").bottom,
+                           v, epsilon, "munderover: under gap");
+    }, "LowerLimitGapMin");
+
+    test(function() {
+      var v = 5000 * emToPx;
+      assert_approx_equals(getBox("ref0003").top - getBox("over00031").bottom,
+                           v, epsilon, "mover: over shift");
+      assert_approx_equals(getBox("ref0003").top - getBox("over00032").bottom,
+                           v, epsilon, "munderover: over shift");
+    }, "UpperLimitBaselineRiseMin");
+
+    test(function() {
+      var v = 7000 * emToPx;
+      assert_approx_equals(getBox("ref0004").top - getBox("over00041").bottom,
+                           v, epsilon, "mover: over shift");
+      assert_approx_equals(getBox("ref0004").top - getBox("over00042").bottom,
+                           v, epsilon, "munderover: over shift");
+    }, "UpperLimitGapMin");
+
+    done();
+  }
+</script>
+</head>
+<body>
+    <p>
+      <math style="font-family: lowerlimitbaselinedropmin3000;">
+        <mspace id="ref0001" height="1em" width="3em" mathbackground="green"/>
+        <munder>
+          <mo movablelimits="false">&#x2211;</mo>
+          <mspace id="under00011" depth="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munderover>
+          <mo movablelimits="false">&#x2211;</mo>
+          <mspace id="under00012" depth="1em" width="3em" mathbackground="blue"/>
+          <mspace height="1em" width="3em" mathbackground="black"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: lowerlimitgapmin11000;">
+        <mspace id="ref0002" height="1em" width="3em" mathbackground="green"/>
+        <munder>
+          <mo movablelimits="false">&#x2211;</mo>
+          <mspace id="under00021" depth="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munderover>
+          <mo movablelimits="false">&#x2211;</mo>
+          <mspace id="under00022" depth="1em" width="3em" mathbackground="blue"/>
+          <mspace height="1em" width="3em" mathbackground="black"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: upperlimitbaselinerisemin5000;">
+        <mspace id="ref0003" height="1em" width="3em" mathbackground="green"/>
+        <mover>
+          <mo movablelimits="false">&#x2211;</mo>
+          <mspace id="over00031" height="1em" width="3em" mathbackground="blue"/>
+        </mover>
+        <munderover>
+          <mo movablelimits="false">&#x2211;</mo>
+          <mspace height="1em" width="3em" mathbackground="black"/>
+          <mspace id="over00032" height="1em" width="3em" mathbackground="blue"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: upperlimitgapmin7000;">
+        <mspace id="ref0004" height="1em" width="3em" mathbackground="green"/>
+        <mover>
+          <mo movablelimits="false">&#x2211;</mo>
+          <mspace id="over00041" depth="1em" width="3em" mathbackground="blue"/>
+        </mover>
+        <munderover>
+          <mo movablelimits="false">&#x2211;</mo>
+          <mspace height="1em" width="3em" mathbackground="black"/>
+          <mspace id="over00042" depth="1em" width="3em" mathbackground="blue"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html
new file mode 100644
index 0000000..c28f29c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-2.html
@@ -0,0 +1,144 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Underscripts and Overscripts parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Elements munder, mover, munderover correctly use the stretch stack parameters from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace, mo {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: bottomshiftdown3000;
+    src: url("/fonts/math/stretchstack-bottomshiftdown3000.woff");
+  }
+  @font-face {
+    font-family: gapbelowmin11000;
+    src: url("/fonts/math/stretchstack-gapbelowmin11000.woff");
+  }
+  @font-face {
+    font-family: topshiftup5000;
+    src: url("/fonts/math/stretchstack-topshiftup5000.woff");
+  }
+  @font-face {
+    font-family: gapabovemin7000;
+    src: url("/fonts/math/stretchstack-gapabovemin7000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 1;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      var v = 3000 * emToPx;
+      assert_approx_equals(getBox("under00011").top - getBox("ref0001").bottom,
+                           v, epsilon, "munder: under shift");
+      assert_approx_equals(getBox("under00012").top - getBox("ref0001").bottom,
+                           v, epsilon, "munderover: under shift");
+    }, "StretchStackBottomShiftDown");
+
+    test(function() {
+      var v = 11000 * emToPx;
+      assert_approx_equals(getBox("under00021").top - getBox("ref0002").bottom,
+                           v, epsilon, "munder: under gap");
+      assert_approx_equals(getBox("under00022").top - getBox("ref0002").bottom,
+                           v, epsilon, "munderover: under gap");
+    }, "StretchStackGapBelowMin");
+
+    test(function() {
+      var v = 5000 * emToPx;
+      assert_approx_equals(getBox("ref0003").top - getBox("over00031").bottom,
+                           v, epsilon, "mover: over shift");
+      assert_approx_equals(getBox("ref0003").top - getBox("over00032").bottom,
+                           v, epsilon, "munderover: over shift");
+    }, "StretchStackTopShiftUp");
+
+    test(function() {
+      var v = 7000 * emToPx;
+      assert_approx_equals(getBox("ref0004").top - getBox("over00041").bottom,
+                           v, epsilon, "mover: over shift");
+      assert_approx_equals(getBox("ref0004").top - getBox("over00042").bottom,
+                           v, epsilon, "munderover: over shift");
+    }, "StretchStackGapAboveMin");
+
+    done();
+  }
+</script>
+</head>
+<body>
+    <p>
+      <math style="font-family: bottomshiftdown3000;">
+        <mspace id="ref0001" height="1em" width="3em" mathbackground="green"/>
+        <munder>
+          <mo>&#x2192;</mo>
+          <mspace id="under00011" depth="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munderover>
+          <mo>&#x2192;</mo>
+          <mspace id="under00012" depth="1em" width="3em" mathbackground="blue"/>
+          <mspace height="1em" width="3em" mathbackground="black"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: gapbelowmin11000;">
+        <mspace id="ref0002" height="1em" width="3em" mathbackground="green"/>
+        <munder>
+          <mo>&#x2192;</mo>
+          <mspace id="under00021" depth="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munderover>
+          <mo>&#x2192;</mo>
+          <mspace id="under00022" depth="1em" width="3em" mathbackground="blue"/>
+          <mspace height="1em" width="3em" mathbackground="black"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: topshiftup5000;">
+        <mspace id="ref0003" height="1em" width="3em" mathbackground="green"/>
+        <mover>
+          <mo>&#x2192;</mo>
+          <mspace id="over00031" height="1em" width="3em" mathbackground="blue"/>
+        </mover>
+        <munderover>
+          <mo>&#x2192;</mo>
+          <mspace height="1em" width="3em" mathbackground="black"/>
+          <mspace id="over00032" height="1em" width="3em" mathbackground="blue"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: gapabovemin7000;">
+        <mspace id="ref0004" height="1em" width="3em" mathbackground="green"/>
+        <mover>
+          <mo>&#x2192;</mo>
+          <mspace id="over00041" depth="1em" width="3em" mathbackground="blue"/>
+        </mover>
+        <munderover>
+          <mo>&#x2192;</mo>
+          <mspace height="1em" width="3em" mathbackground="black"/>
+          <mspace id="over00042" depth="1em" width="3em" mathbackground="blue"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html
new file mode 100644
index 0000000..0172ff1c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-3.html
@@ -0,0 +1,323 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Underscripts and Overscripts parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Elements munder, mover, munderover correctly use underbar/overbar and AccentBaseHeight parameters from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace, mo {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: accentbaseheight4000underbarextradescender5000;
+    src: url("/fonts/math/underover-accentbaseheight4000-underbarextradescender5000.woff");
+  }
+  @font-face {
+    font-family: accentbaseheight4000underbarverticalgap7000;
+    src: url("/fonts/math/underover-accentbaseheight4000-underbarverticalgap7000.woff");
+  }
+  @font-face {
+    font-family: accentbaseheight4000overbarextraascender3000;
+    src: url("/fonts/math/underover-accentbaseheight4000-overbarextraascender3000.woff");
+  }
+  @font-face {
+    font-family: accentbaseheight4000overbarverticalgap11000;
+    src: url("/fonts/math/underover-accentbaseheight4000-overbarverticalgap11000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 2;
+  var axisBaseHeight = 4000 *  emToPx;
+  var shortBaseHeight = 3000 * emToPx; // shortBaseHeight < axisBaseHeight
+  var tallBaseHeight = 5000 * emToPx; // tallBaseHeight > axisBaseHeight
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => {   document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      for (var i = 1; i <= 4; i++) {
+        for (var j = 1; j <= 6; j++) {
+           var baseId = ("base00" + i) + j;
+           assert_approx_equals(getBox("ref00" + i).bottom,
+                                getBox(baseId).bottom,
+                                epsilon,
+                                "alignment of " + baseId);
+        }
+      }
+    }, "Baseline alignment");
+
+    test(function() {
+      for (var i = 1; i <= 4; i++) {
+        for (var j = 1; j <= 6; j++) {
+           var baseId = ("base00" + i) + j;
+           assert_approx_equals(getBox(baseId).height,
+                                j == 2 || j == 5 ?
+                                tallBaseHeight :shortBaseHeight,
+                                epsilon,
+                                "height of " + baseId);
+        }
+      }
+    }, "Heights of bases");
+
+    test(function() {
+      var v = 5000 * emToPx;
+      assert_approx_equals(getBox("ref001").bottom - getBox("over0014").bottom,
+                           shortBaseHeight, epsilon,
+                           "munderover: nonaccent over short base");
+      assert_approx_equals(getBox("ref001").bottom - getBox("over0015").bottom,
+                           tallBaseHeight, epsilon,
+                           "munderover: accent over tall base");
+      assert_approx_equals(getBox("ref001").bottom - getBox("over0016").bottom,
+                           axisBaseHeight, epsilon,
+                           "munderover: accent over short base");
+      for (var j = 1; j <= 6; j++) {
+        var elId = "el001" + j;
+        var baseId = "base001" + j;
+        var underId = "under001" + j;
+        assert_approx_equals(getBox(underId).top - getBox(baseId).bottom,
+                             0, epsilon,
+                             "gap between " + baseId + " and " + underId);
+        assert_approx_equals(getBox(elId).bottom - getBox(underId).bottom,
+                             v, epsilon,
+                             "extra descender below " + underId);
+      }
+    }, "AccentBaseHeight, UnderbarExtraDescender");
+
+    test(function() {
+      var v = 7000 * emToPx;
+      assert_approx_equals(getBox("ref002").bottom - getBox("over0024").bottom,
+                           shortBaseHeight, epsilon,
+                           "munderover: nonaccent over short base");
+      assert_approx_equals(getBox("ref002").bottom - getBox("over0025").bottom,
+                           tallBaseHeight, epsilon,
+                           "munderover: accent over tall base");
+      assert_approx_equals(getBox("ref002").bottom - getBox("over0026").bottom,
+                           axisBaseHeight, epsilon,
+                           "munderover: accent over short base");
+      for (var j = 1; j <= 6; j++) {
+        var elId = "el002" + j;
+        var baseId = "base002" + j;
+        var underId = "under002" + j;
+        var gap = (j == 2 || j == 3 ? 0 : v);
+        assert_approx_equals(getBox(underId).top - getBox(baseId).bottom,
+                             gap, epsilon,
+                             "gap between " + baseId + " and " + underId);
+      }
+    }, "AccentBaseHeight, UnderbarVerticalGap");
+
+    test(function() {
+      var v = 3000 * emToPx;
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0031").bottom,
+                           shortBaseHeight, epsilon,
+                           "mover: nonaccent over short base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0032").bottom,
+                           tallBaseHeight, epsilon,
+                           "mover: accent over tall base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0033").bottom,
+                           axisBaseHeight, epsilon,
+                           "mover: accent over short base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0034").bottom,
+                           shortBaseHeight, epsilon,
+                           "munderover: nonaccent over short base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0035").bottom,
+                           tallBaseHeight, epsilon,
+                           "munderover: accent over tall base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0036").bottom,
+                           axisBaseHeight, epsilon,
+                           "munderover: accent over short base");
+      for (var j = 1; j <= 6; j++) {
+        var elId = "el003" + j;
+        var baseId = "base003" + j;
+        if (j >= 4) {
+          var underId = "under003" + j;
+          assert_approx_equals(getBox(underId).top - getBox(baseId).bottom,
+                               0, epsilon,
+                               "gap between " + baseId + " and " + underId);
+        }
+        var overId = "over003" + j;
+        assert_approx_equals(getBox(overId).top - getBox(elId).top,
+                             v, epsilon,
+                             "extra ascender below " + overId);
+      }
+    }, "AccentBaseHeight, OverbarExtraAscender");
+
+    test(function() {
+      v = 11000 * emToPx;
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0041").bottom,
+                           shortBaseHeight + v, epsilon,
+                           "mover: nonaccent over short base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0042").bottom,
+                           tallBaseHeight, epsilon,
+                           "mover: accent over tall base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0043").bottom,
+                           axisBaseHeight, epsilon,
+                           "mover: accent over short base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0044").bottom,
+                           shortBaseHeight + v, epsilon,
+                           "munderover: nonaccent over short base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0045").bottom,
+                           tallBaseHeight, epsilon,
+                           "munderover: accent over tall base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0046").bottom,
+                           axisBaseHeight, epsilon,
+                           "munderover: accent over short base");
+      for (var j = 4; j <= 6; j++) {
+        var baseId = "base004" + j;
+        var underId = "under004" + j;
+        assert_approx_equals(getBox(underId).top - getBox(baseId).bottom,
+                             0, epsilon,
+                             "gap between " + baseId + " and " + underId);
+      }
+    }, "AccentBaseHeight, OverbarVerticalGap");
+
+    done();
+  }
+</script>
+</head>
+<body>
+    <p>
+      <math style="font-family: accentbaseheight4000underbarextradescender5000;">
+        <mspace id="ref001" height="1em" width="3em" mathbackground="green"/>
+        <munder mathbackground="cyan" id="el0011">
+          <mspace id="base0011" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0011" height="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munder mathbackground="cyan" id="el0012" accentunder="true">
+          <mspace id="base0012" height="5em" width="1em" mathbackground="black"/>
+          <mspace id="under0012" height="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munder mathbackground="cyan" id="el0013" accentunder="true">
+          <mspace id="base0013" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0013" height="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munderover mathbackground="cyan" id="el0014">
+          <mspace id="base0014" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0014" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0014" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0015" accent="true">
+          <mspace id="base0015" height="5em" width="1em" mathbackground="black"/>
+          <mspace id="under0015" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0015" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0016" accent="true">
+          <mspace id="base0016" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0016" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0016" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: accentbaseheight4000underbarverticalgap7000;">
+        <mspace id="ref002" height="1em" width="3em" mathbackground="green"/>
+        <munder mathbackground="cyan" id="el0021">
+          <mspace id="base0021" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0021" height="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munder mathbackground="cyan" id="el0022" accentunder="true">
+          <mspace id="base0022" height="5em" width="1em" mathbackground="black"/>
+          <mspace id="under0022" height="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munder mathbackground="cyan" id="el0023" accentunder="true">
+          <mspace id="base0023" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0023" height="1em" width="3em" mathbackground="blue"/>
+        </munder>
+        <munderover mathbackground="cyan" id="el0024">
+          <mspace id="base0024" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0024" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0024" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0025" accent="true">
+          <mspace id="base0025" height="5em" width="1em" mathbackground="black"/>
+          <mspace id="under0025" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0025" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0026" accent="true">
+          <mspace id="base0026" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0026" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0026" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: accentbaseheight4000overbarextraascender3000;">
+        <mspace id="ref003" height="1em" width="3em" mathbackground="green"/>
+        <mover mathbackground="cyan" id="el0031">
+          <mspace id="base0031" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="over0031" height="1em" width="3em" mathbackground="red"/>
+        </mover>
+        <mover mathbackground="cyan" id="el0032" accent="true">
+          <mspace id="base0032" height="5em" width="1em" mathbackground="black"/>
+          <mspace id="over0032" height="1em" width="3em" mathbackground="red"/>
+        </mover>
+        <mover mathbackground="cyan" id="el0033" accent="true">
+          <mspace id="base0033" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="over0033" height="1em" width="3em" mathbackground="red"/>
+        </mover>
+        <munderover mathbackground="cyan" id="el0034">
+          <mspace id="base0034" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0034" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0034" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0035" accent="true">
+          <mspace id="base0035" height="5em" width="1em" mathbackground="black"/>
+          <mspace id="under0035" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0035" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0036" accent="true">
+          <mspace id="base0036" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0036" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0036" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: accentbaseheight4000overbarverticalgap11000;">
+        <mspace id="ref004" height="1em" width="3em" mathbackground="green"/>
+        <mover mathbackground="cyan" id="el0041">
+          <mspace id="base0041" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="over0041" height="1em" width="3em" mathbackground="red"/>
+        </mover>
+        <mover mathbackground="cyan" id="el0042" accent="true">
+          <mspace id="base0042" height="5em" width="1em" mathbackground="black"/>
+          <mspace id="over0042" height="1em" width="3em" mathbackground="red"/>
+        </mover>
+        <mover mathbackground="cyan" id="el0043" accent="true">
+          <mspace id="base0043" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="over0043" height="1em" width="3em" mathbackground="red"/>
+        </mover>
+        <munderover mathbackground="cyan" id="el0044">
+          <mspace id="base0044" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0044" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0044" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0045" accent="true">
+          <mspace id="base0045" height="5em" width="1em" mathbackground="black"/>
+          <mspace id="under0045" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0045" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0046" accent="true">
+          <mspace id="base0046" height="3em" width="1em" mathbackground="black"/>
+          <mspace id="under0046" height="1em" width="3em" mathbackground="blue"/>
+          <mspace id="over0046" height="1em" width="3em" mathbackground="red"/>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html
new file mode 100644
index 0000000..061cda79
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/scripts/underover-parameters-4.html
@@ -0,0 +1,323 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Underscripts and Overscripts parameters</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS4">
+<meta name="assert" content="Elements munder, mover, munderover correctly use underbar/overbar and AccentBaseHeight parameters from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace, mo {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: accentbaseheight4000underbarextradescender5000;
+    src: url("/fonts/math/underover-accentbaseheight4000-underbarextradescender5000.woff");
+  }
+  @font-face {
+    font-family: accentbaseheight4000underbarverticalgap7000;
+    src: url("/fonts/math/underover-accentbaseheight4000-underbarverticalgap7000.woff");
+  }
+  @font-face {
+    font-family: accentbaseheight4000overbarextraascender3000;
+    src: url("/fonts/math/underover-accentbaseheight4000-overbarextraascender3000.woff");
+  }
+  @font-face {
+    font-family: accentbaseheight4000overbarverticalgap11000;
+    src: url("/fonts/math/underover-accentbaseheight4000-overbarverticalgap11000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 2;
+  var axisBaseHeight = 4000 *  emToPx;
+  var shortBaseHeight = 3000 * emToPx; // shortBaseHeight < axisBaseHeight
+  var tallBaseHeight = 5000 * emToPx; // tallBaseHeight > axisBaseHeight
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => {   document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      for (var i = 1; i <= 4; i++) {
+        for (var j = 1; j <= 6; j++) {
+           var baseId = ("base00" + i) + j;
+           assert_approx_equals(getBox("ref00" + i).bottom,
+                                getBox(baseId).bottom,
+                                epsilon,
+                                "alignment of " + baseId);
+        }
+      }
+    }, "Baseline alignment");
+
+    test(function() {
+      for (var i = 1; i <= 4; i++) {
+        for (var j = 1; j <= 6; j++) {
+           var baseId = ("base00" + i) + j;
+           assert_approx_equals(getBox(baseId).height,
+                                j == 2 || j == 5 ?
+                                tallBaseHeight :shortBaseHeight,
+                                epsilon,
+                                "height of " + baseId);
+        }
+      }
+    }, "Heights of bases");
+
+    test(function() {
+      var v = 5000 * emToPx;
+      assert_approx_equals(getBox("ref001").bottom - getBox("over0014").bottom,
+                           shortBaseHeight, epsilon,
+                           "munderover: nonaccent over short base");
+      assert_approx_equals(getBox("ref001").bottom - getBox("over0015").bottom,
+                           tallBaseHeight, epsilon,
+                           "munderover: accent over tall base");
+      assert_approx_equals(getBox("ref001").bottom - getBox("over0016").bottom,
+                           axisBaseHeight, epsilon,
+                           "munderover: accent over short base");
+      for (var j = 1; j <= 6; j++) {
+        var elId = "el001" + j;
+        var baseId = "base001" + j;
+        var underId = "under001" + j;
+        assert_approx_equals(getBox(underId).top - getBox(baseId).bottom,
+                             0, epsilon,
+                             "gap between " + baseId + " and " + underId);
+        assert_approx_equals(getBox(elId).bottom - getBox(underId).bottom,
+                             v, epsilon,
+                             "extra descender below " + underId);
+      }
+    }, "AccentBaseHeight, UnderbarExtraDescender");
+
+    test(function() {
+      var v = 7000 * emToPx;
+      assert_approx_equals(getBox("ref002").bottom - getBox("over0024").bottom,
+                           shortBaseHeight, epsilon,
+                           "munderover: nonaccent over short base");
+      assert_approx_equals(getBox("ref002").bottom - getBox("over0025").bottom,
+                           tallBaseHeight, epsilon,
+                           "munderover: accent over tall base");
+      assert_approx_equals(getBox("ref002").bottom - getBox("over0026").bottom,
+                           axisBaseHeight, epsilon,
+                           "munderover: accent over short base");
+      for (var j = 1; j <= 6; j++) {
+        var elId = "el002" + j;
+        var baseId = "base002" + j;
+        var underId = "under002" + j;
+        var gap = (j == 2 || j == 3 ? 0 : v);
+        assert_approx_equals(getBox(underId).top - getBox(baseId).bottom,
+                             gap, epsilon,
+                             "gap between " + baseId + " and " + underId);
+      }
+    }, "AccentBaseHeight, UnderbarVerticalGap");
+
+    test(function() {
+      var v = 3000 * emToPx;
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0031").bottom,
+                           shortBaseHeight, epsilon,
+                           "mover: nonaccent over short base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0032").bottom,
+                           tallBaseHeight, epsilon,
+                           "mover: accent over tall base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0033").bottom,
+                           axisBaseHeight, epsilon,
+                           "mover: accent over short base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0034").bottom,
+                           shortBaseHeight, epsilon,
+                           "munderover: nonaccent over short base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0035").bottom,
+                           tallBaseHeight, epsilon,
+                           "munderover: accent over tall base");
+      assert_approx_equals(getBox("ref003").bottom - getBox("over0036").bottom,
+                           axisBaseHeight, epsilon,
+                           "munderover: accent over short base");
+      for (var j = 1; j <= 6; j++) {
+        var elId = "el003" + j;
+        var baseId = "base003" + j;
+        if (j >= 4) {
+          var underId = "under003" + j;
+          assert_approx_equals(getBox(underId).top - getBox(baseId).bottom,
+                               0, epsilon,
+                               "gap between " + baseId + " and " + underId);
+        }
+        var overId = "over003" + j;
+        assert_approx_equals(getBox(overId).top - getBox(elId).top,
+                             v, epsilon,
+                             "extra ascender below " + overId);
+      }
+    }, "AccentBaseHeight, OverbarExtraAscender");
+
+    test(function() {
+      v = 11000 * emToPx;
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0041").bottom,
+                           shortBaseHeight + v, epsilon,
+                           "mover: nonaccent over short base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0042").bottom,
+                           tallBaseHeight, epsilon,
+                           "mover: accent over tall base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0043").bottom,
+                           axisBaseHeight, epsilon,
+                           "mover: accent over short base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0044").bottom,
+                           shortBaseHeight + v, epsilon,
+                           "munderover: nonaccent over short base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0045").bottom,
+                           tallBaseHeight, epsilon,
+                           "munderover: accent over tall base");
+      assert_approx_equals(getBox("ref004").bottom - getBox("over0046").bottom,
+                           axisBaseHeight, epsilon,
+                           "munderover: accent over short base");
+      for (var j = 4; j <= 6; j++) {
+        var baseId = "base004" + j;
+        var underId = "under004" + j;
+        assert_approx_equals(getBox(underId).top - getBox(baseId).bottom,
+                             0, epsilon,
+                             "gap between " + baseId + " and " + underId);
+      }
+    }, "AccentBaseHeight, OverbarVerticalGap");
+
+    done();
+  }
+</script>
+</head>
+<body>
+    <p>
+      <math style="font-family: accentbaseheight4000underbarextradescender5000;">
+        <mspace id="ref001" height="1em" width="3em" mathbackground="green"/>
+        <munder mathbackground="cyan" id="el0011">
+          <mspace id="base0011" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0011" mathcolor="blue">&#xB0;</mo>
+        </munder>
+        <munder mathbackground="cyan" id="el0012">
+          <mspace id="base0012" height="5em" width="1em" mathbackground="black"/>
+          <mo id="under0012" mathcolor="blue">&#x2D8;</mo>
+        </munder>
+        <munder mathbackground="cyan" id="el0013">
+          <mspace id="base0013" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0013" mathcolor="blue">&#x2D8;</mo>
+        </munder>
+        <munderover mathbackground="cyan" id="el0014">
+          <mspace id="base0014" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0014" mathcolor="blue">&#xB0;</mo>
+          <mo id="over0014" mathcolor="red">&#xB0;</mo>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0015" accent="true">
+          <mspace id="base0015" height="5em" width="1em" mathbackground="black"/>
+          <mo id="under0015" mathcolor="blue">&#x2D8;</mo>
+          <mo id="over0015" mathcolor="red">&#x2D8;</mo>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0016" accent="true">
+          <mspace id="base0016" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0016" mathcolor="blue">&#x2D8;</mo>
+          <mo id="over0016" mathcolor="red">&#x2D8;</mo>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: accentbaseheight4000underbarverticalgap7000;">
+        <mspace id="ref002" height="1em" width="3em" mathbackground="green"/>
+        <munder mathbackground="cyan" id="el0021" accentunder="false">
+          <mspace id="base0021" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0021" mathcolor="blue">&#x2D8;</mo>
+        </munder>
+        <munder mathbackground="cyan" id="el0022">
+          <mspace id="base0022" height="5em" width="1em" mathbackground="black"/>
+          <mo id="under0022" mathcolor="blue" accent="true">&#x2D8;</mo>
+        </munder>
+        <munder mathbackground="cyan" id="el0023">
+          <mspace id="base0023" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0023" mathcolor="blue" accent="true">&#xB0;</mo>
+        </munder>
+        <munderover mathbackground="cyan" id="el0024">
+          <mspace id="base0024" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0024" mathcolor="blue" accent="false">&#x2D8;</mo>
+          <mo id="over0024" mathcolor="red" accent="false">&#x2D8;</mo>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0025">
+          <mspace id="base0025" height="5em" width="1em" mathbackground="black"/>
+          <mo id="under0025" mathcolor="blue" accent="false">&#x2D8;</mo>
+          <mo id="over0025" mathcolor="red">&#x2D8;</mo>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0026">
+          <mspace id="base0026" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0026" mathcolor="blue" accent="false">&#x2D8;</mo>
+          <mo id="over0026" mathcolor="red">&#x2D8;</mo>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: accentbaseheight4000overbarextraascender3000;">
+        <mspace id="ref003" height="1em" width="3em" mathbackground="green"/>
+        <mover mathbackground="cyan" id="el0031">
+          <mspace id="base0031" height="3em" width="1em" mathbackground="black"/>
+          <mo id="over0031" mathcolor="red">&#xB0;</mo>
+        </mover>
+        <mover mathbackground="cyan" id="el0032" accent="true">
+          <mspace id="base0032" height="5em" width="1em" mathbackground="black"/>
+          <mo id="over0032" mathcolor="red">&#xB0;</mo>
+        </mover>
+        <mover mathbackground="cyan" id="el0033">
+          <mspace id="base0033" height="3em" width="1em" mathbackground="black"/>
+          <mo id="over0033" mathcolor="red">&#x2D8;</mo>
+        </mover>
+        <munderover mathbackground="cyan" id="el0034">
+          <mspace id="base0034" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0034" mathcolor="blue">&#xB0;</mo>
+          <mo id="over0034" mathcolor="red" accent="false">&#x2D8;</mo>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0035" accent="true">
+          <mspace id="base0035" height="5em" width="1em" mathbackground="black"/>
+          <mo id="under0035" mathcolor="blue">&#x2D8;</mo>
+          <mo id="over0035" mathcolor="red">&#x2D8;</mo>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0036" accent="true">
+          <mspace id="base0036" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0036" mathcolor="blue">&#x2D8;</mo>
+          <mo id="over0036" mathcolor="red">&#x2D8;</mo>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+    <p>
+      <math style="font-family: accentbaseheight4000overbarverticalgap11000;">
+        <mspace id="ref004" height="1em" width="3em" mathbackground="green"/>
+        <mover mathbackground="cyan" id="el0041">
+          <mspace id="base0041" height="3em" width="1em" mathbackground="black"/>
+          <mo id="over0041" mathcolor="red">&#xB0;</mo>
+        </mover>
+        <mover mathbackground="cyan" id="el0042" accent="true">
+          <mspace id="base0042" height="5em" width="1em" mathbackground="black"/>
+          <mo id="over0042" mathcolor="red">&#xB0;</mo>
+        </mover>
+        <mover mathbackground="cyan" id="el0043">
+          <mspace id="base0043" height="3em" width="1em" mathbackground="black"/>
+          <mo id="over0043" mathcolor="red">&#x2D8;</mo>
+        </mover>
+        <munderover mathbackground="cyan" id="el0044">
+          <mspace id="base0044" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0044" mathcolor="blue">&#xB0;</mo>
+          <mo id="over0044" mathcolor="red" accent="false">&#x2D8;</mo>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0045" accent="true">
+          <mspace id="base0045" height="5em" width="1em" mathbackground="black"/>
+          <mo id="under0045" mathcolor="blue">&#x2D8;</mo>
+          <mo id="over0045" mathcolor="red">&#x2D8;</mo>
+        </munderover>
+        <munderover mathbackground="cyan" id="el0046" accent="true">
+          <mspace id="base0046" height="3em" width="1em" mathbackground="black"/>
+          <mo id="under0046" mathcolor="blue">&#x2D8;</mo>
+          <mo id="over0046" mathcolor="red">&#x2D8;</mo>
+        </munderover>
+      </math>
+    </p>
+    <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-1.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-1.html
new file mode 100644
index 0000000..adb3637
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-1.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Space</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS2.SSS6">
+<meta name="assert" content="Verify mspace metrics for different values of height, depth and width">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  var epsilon = 1;
+  function getBox(aId) {
+    var box = document.getElementById(aId).getBoundingClientRect();
+    box.middle = (box.bottom + box.top) / 2;
+    box.center = (box.left + box.right) / 2;
+    return box;
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", runTests);
+
+  function runTests() {
+    test(function() {
+      var empty = getBox("empty");
+      assert_equals(empty.width, 0, "zero width");
+      assert_approx_equals(getBox("baseline").bottom - empty.top, 0, epsilon, "zero depth");
+      assert_approx_equals(empty.bottom - getBox("baseline").bottom, 0, epsilon, "zero depth");
+    }, "Empty mspace");
+
+    test(function() {
+      for (var i = 0; i <= 2; i++) {
+        var space = getBox("width" + i);
+        assert_approx_equals(space.width, 25*(i+1), epsilon, "width " + i);
+        assert_approx_equals(getBox("baseline").bottom - space.top, 0, epsilon, "height" + i);
+        assert_approx_equals(space.bottom - getBox("baseline").bottom, 0, epsilon, "depth" + i);
+      }
+    }, "Different widths");
+
+    test(function() {
+      for (var i = 0; i <= 2; i++) {
+        var space = getBox("height" + i);
+        assert_equals(space.width, 0, "width" + i);
+        assert_approx_equals(getBox("baseline").bottom - space.top, 25*(i+1), epsilon, "height" + i);
+        assert_approx_equals(space.bottom - getBox("baseline").bottom, 0, epsilon, "depth" + i);
+      }
+    }, "Different heights");
+
+    test(function() {
+      for (var i = 0; i <= 2; i++) {
+        var space = getBox("depth" + i);
+        assert_equals(space.width, 0, "width" + i);
+        assert_approx_equals(getBox("baseline").bottom - space.top, 0, epsilon, "height" + i);
+        assert_approx_equals(space.bottom - getBox("baseline").bottom, 25*(i+1), epsilon, "depth" + i);
+      }
+    }, "Different depths");
+
+    test(function() {
+      for (var i = 0; i <= 2; i++) {
+        var space = getBox("mspace" + i);
+        assert_approx_equals(space.width, 25*(1+i%3), epsilon, "width" + i);
+        assert_approx_equals(getBox("baseline").bottom - space.top, 25*(1+(i+1)%3), epsilon, "height" + i);
+        assert_approx_equals(space.bottom - getBox("baseline").bottom, 25*(1+(i+2)%3), epsilon, "depth" + i);
+      }
+    }, "Various combinations of height, depth and width.");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <span id="baseline" style="display: inline-block; width: 30px; height: 5px; background: blue"></span>
+    <math>
+      <mspace id="empty"/>
+      <mspace id="width0" width="25px"/>
+      <mspace id="width1" width="50px"/>
+      <mspace id="width2" width="75px"/>
+      <mspace id="height0" height="25px"/>
+      <mspace id="height1" height="50px"/>
+      <mspace id="height2" height="75px"/>
+      <mspace id="depth0" depth="25px"/>
+      <mspace id="depth1" depth="50px"/>
+      <mspace id="depth2" depth="75px"/>
+      <mspace id="mspace0" width="25px" height="50px" depth="75px" mathbackground="green"/>
+      <mspace id="mspace1" width="50px" height="75px" depth="25px" mathbackground="blue"/>
+      <mspace id="mspace2" width="75px" height="25px" depth="50px" mathbackground="green"/>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2-ref.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2-ref.html
new file mode 100644
index 0000000..5a8b39e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>space (reference)</title>
+</head>
+<body>
+  <p>Test passes if you see a green square and no red.</p>
+  <div style="position: relative;">
+    <div style="position: absolute; top: 0px; left: 0px;
+                background: green; width: 200px; height: 200px;">
+    </div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2.html
new file mode 100644
index 0000000..544cfb1f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/spaces/space-2.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>space</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS2.SSS6">
+<link rel="match" href="space-2-ref.html"/>
+<meta name="assert" content="Verify mspace visual rendering for different values of height, depth and width">
+</head>
+<body>
+  <p>Test passes if you see a green square and no red.</p>
+  <div style="position: relative;">
+    <!-- Some green and red mspaces to draw a square -->
+    <div style="position: absolute; top: 0px; left: 0px;
+                width: 200px; height: 200px;">
+      <math style="position: absolute; top: 0px; left: 0px">
+        <mspace width="50px" height="100px" depth="100px" mathbackground="green"/>
+        <mspace width="50px" height="100px" depth="100px" mathbackground="green"/>
+        <mspace width="25px" depth="100px" mathbackground="green"/>
+        <mspace width="25px" depth="100px" mathbackground="green"/>
+        <mspace width="25px" height="100px" mathbackground="green"/>
+        <mspace width="25px" height="100px" mathbackground="green"/>
+      </math>
+      <math style="position: absolute; top: 0px; left: 0px">
+        <mspace width="100px" height="20px" depth="20px" mathbackground="red"/>
+        <mspace width="50px" height="100px" mathbackground="red"/>
+        <mspace width="50px" depth="100px" mathbackground="red"/>
+      </math>
+    </div>
+    <!-- These green divs should cover the red mspace elements -->
+    <div style="position: absolute; top: 0px; left: 0px;
+                width: 200px; height: 200px;">
+      <div style="position: absolute; top: 80px; left: 0px;
+                  width: 100px; height: 40px; background: green"></div>
+      <div style="position: absolute; top: 0px; left: 100px;
+                  width: 50px; height: 100px; background: green"></div>
+      <div style="position: absolute; top: 100px; left: 150px;
+                  width: 50px; height: 100px; background: green"></div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html
new file mode 100644
index 0000000..50c3491
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/tables/table-axis-height.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>table axis height</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S3.html#SS5">
+<meta name="assert" content="Element mtable correctly uses the axis height parameter from the MATH table.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  math, mspace {
+    font-size: 10px;
+  }
+  @font-face {
+    font-family: axisheight5000-verticalarrow14000;
+    src: url("/fonts/math/axisheight5000-verticalarrow14000.woff");
+  }
+</style>
+<script>
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 1;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      var v1 = 5000 * emToPx;
+      var tableMiddle = (getBox("table").bottom + getBox("table").top) / 2;
+      assert_approx_equals(getBox("baseline").bottom - tableMiddle,
+                           v1, epsilon, "mtable: axis height");
+    }, "AxisHeight");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math style="font-family: axisheight5000-verticalarrow14000">
+      <mspace id="baseline" mathbackground="green" width="50px" height="1px"/>
+      <mtable id="table" mathbackground="blue"><mtr><mtd><mspace width="100px" height="1px"/></mtd></mtr></mtable>
+    </math>
+  </p>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1-ref.html
new file mode 100644
index 0000000..0efca48
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>color (reference)</title>
+</head>
+<body>
+  <p>Test passes if you see a green square.</p>
+  <div style="background: green; width: 200px; height: 200px; padding: 1px;">
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1.html
new file mode 100644
index 0000000..4ef8213
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/color-1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>color</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1">
+<link rel="match" href="color-1-ref.html"/>
+<meta name="assert" content="Verify that the color is used for text and graphical elements.">
+</head>
+<body>
+  <p>Test passes if you see a green square.</p>
+  <div style="background: green; color: red; width: 200px; height: 200px; padding: 1px;">
+    <math><mfrac style="color: green"><mn>1</mn><mn>2</mn></mfrac></math>
+    <math><msqrt style="color: green"><mn>3</mn></msqrt></math>
+    <math><mroot style="color: green"><mn>4</mn><mn>5</mn></mroot></math>
+    <math><menclose notation="left" style="color: green"><mn>6</mn></menclose></math>
+    <math><menclose notation="right" style="color: green"><mn>7</mn></menclose></math>
+    <math><menclose notation="top" style="color: green"><mn>8</mn></menclose></math>
+    <math><menclose notation="bottom" style="color: green"><mn>9</mn></menclose></math>
+    <math><menclose notation="box" style="color: green"><mn>10</mn></menclose></math>
+    <math><menclose notation="roundedbox" style="color: green"><mn>11</mn></menclose></math>
+    <math><menclose notation="actuarial" style="color: green"><mn>12</mn></menclose></math>
+    <math><menclose notation="madruwb" style="color: green"><mn>13</mn></menclose></math>
+    <math><menclose notation="horizontalstrike" style="color: green"><mn>14</mn></menclose></math>
+    <math><menclose notation="verticalstrike" style="color: green"><mn>15</mn></menclose></math>
+    <math><menclose notation="updiagonalstrike" style="color: green"><mn>16</mn></menclose></math>
+    <math><menclose notation="downdiagonalstrike" style="color: green"><mn>17</mn></menclose></math>
+    <math><menclose notation="longdiv" style="color: green"><mn>18</mn></menclose></math>
+    <math><menclose notation="circle" style="color: green"><mn>19</mn></menclose></math>
+    <math><mi style="color: green">20</mi></math>
+    <math><mn style="color: green">21</mn></math>
+    <math><mo style="color: green">22</mo></math>
+    <math><mtext style="color: green">23</mtext></math>
+    <math><ms style="color: green">24</ms></math>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1-ref.html
new file mode 100644
index 0000000..ce65aba
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>display (reference)</title>
+</head>
+<body>
+  <p>Test passes if you see a green square.</p>
+  <div style="background: green; width: 200px; height: 200px;">
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1.html
new file mode 100644
index 0000000..551f640
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/display-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>display</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1">
+<link rel="match" href="display-1-ref.html"/>
+<meta name="assert" content="Verify that the 'display: none' property works on MathML elements.">
+</head>
+<body>
+  <p>Test passes if you see a green square.</p>
+  <div style="background: green; color: red; width: 200px; height: 200px;">
+    <math style="display: none;"><mspace width="200px" height="200px" mathbackground="red"/></math>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/displaystyle-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/displaystyle-1.html
new file mode 100644
index 0000000..2749e09c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/displaystyle-1.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>displaystyle</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1">
+<meta name="assert" content="Verify that the correct inheritance of the displaystyle value by measuring the size of large operators.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/largeop-displayoperatorminheight5000.woff");
+  }
+  math  {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  setup({ explicit_done: true });
+  var emToPx = 10 / 1000; // font-size: 10px, font.em = 1000
+  var epsilon = 5;
+  function verify_displaystyle(element, displaystyle, description) {
+    if (typeof element === "string")
+      element = document.getElementById(element);
+    var elementSize = element.getBoundingClientRect().height;
+    if (displaystyle)
+      assert_approx_equals(elementSize, 5000 * emToPx, epsilon, description);
+    else
+      assert_approx_equals(elementSize, 1000 * emToPx, epsilon, description);
+  }
+
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      verify_displaystyle("math_default", false, "default");
+      verify_displaystyle("math_inline", false, "explicit display inline");
+      verify_displaystyle("math_block", true, "explicit display block");
+      verify_displaystyle("math_false", false, "explicit displaystyle false");
+      verify_displaystyle("math_true", true, "explicit displaystyle true");
+    }, "math element");
+    test(function() {
+      verify_displaystyle("mstyle_false", false, "explicit displaystyle false");
+      verify_displaystyle("mstyle_true", true, "explicit displaystyle true");
+    }, "mstyle element");
+    test(function() {
+      verify_displaystyle("mtable_default", false, "default");
+      verify_displaystyle("mtable_false", false, "explicit displaystyle false");
+      verify_displaystyle("mtable_true", true, "explicit displaystyle true");
+    }, "mtable element");
+    test(function() {
+      verify_displaystyle("mfrac_numerator", false, "numerator");
+      verify_displaystyle("mfrac_denominator", false, "denominator");
+    }, "mfrac element");
+    test(function() {
+      verify_displaystyle("mroot_base", true, "base");
+      verify_displaystyle("mroot_index", false, "index");
+    }, "mroot element");
+    test(function() {
+      verify_displaystyle("msub_base", true, "base");
+      verify_displaystyle("msub_subscript", false, "subscript");
+    }, "msub element");
+    test(function() {
+      verify_displaystyle("msup_base", true, "base");
+      verify_displaystyle("msup_supscript", false, "supscript");
+    }, "msup element");
+    test(function() {
+      verify_displaystyle("msubsup_base", true, "base");
+      verify_displaystyle("msubsup_subscript", false, "subscript");
+      verify_displaystyle("msubsup_supscript", false, "supscript");
+    }, "msubsup element");
+    test(function() {
+      verify_displaystyle("munder_base", true, "base");
+      verify_displaystyle("munder_underscript", false, "underscript");
+    }, "munder element");
+    test(function() {
+      verify_displaystyle("mover_base", true, "base");
+      verify_displaystyle("mover_overscript", false, "overscript");
+    }, "mover element");
+    test(function() {
+      verify_displaystyle("munderover_base", true, "base");
+      verify_displaystyle("munderover_underscript", false, "underscript");
+      verify_displaystyle("munderover_overscript", false, "overscript");
+    }, "munderover element");
+    done();
+  }
+</script>
+</head>
+<body>
+  <math><mo id="math_default">&#x2AFF;</mo></math>
+  <math display="inline"><mo id="math_inline">&#x2AFF;</mo></math>
+  <math display="block"><mo id="math_block">&#x2AFF;</mo></math>
+  <math displaystyle="false"><mo id="math_false">&#x2AFF;</mo></math>
+  <math displaystyle="true"><mo id="math_true">&#x2AFF;</mo></math>
+  <math><mstyle displaystyle="false"><mo id="mstyle_false">&#x2AFF;</mo></mstyle></math>
+  <math><mstyle displaystyle="true"><mo id="mstyle_true">&#x2AFF;</mo></mstyle></math>
+  <math displaystyle="true"><mtable><mtr><mtd><mo id="mtable_default">&#x2AFF;</mo></mtd></mtr></mtable></math>
+  <math><mtable displaystyle="true"><mtr><mtd><mo id="mtable_true">&#x2AFF;</mo></mtd></mtr></mtable></math>
+  <math displaystyle="true"><mtable displaystyle="false"><mtr><mtd><mo id="mtable_false">&#x2AFF;</mo></mtd></mtr></mtable></math>
+  <math displaystyle="true"><mfrac><mo id="mfrac_numerator">&#x2AFF;</mo><mo id="mfrac_denominator">&#x2AFF;</mo></mfrac></math>
+  <math displaystyle="true"><mroot><mo id="mroot_base">&#x2AFF;</mo><mo id="mroot_index">&#x2AFF;</mo></mroot></math>
+  <math displaystyle="true"><msub><mo id="msub_base">&#x2AFF;</mo><mo id="msub_subscript">&#x2AFF;</mo></msub></math>
+  <math displaystyle="true"><msup><mo id="msup_base">&#x2AFF;</mo><mo id="msup_supscript">&#x2AFF;</mo></msup></math>
+  <math displaystyle="true"><msubsup><mo id="msubsup_base">&#x2AFF;</mo><mo id="msubsup_subscript">&#x2AFF;</mo><mo id="msubsup_supscript">&#x2AFF;</mo></msubsup></math>
+  <math displaystyle="true"><mmultiscripts><mo id="mmultiscripts_base">&#x2AFF;</mo><mo id="mmultiscripts_subscript">&#x2AFF;</mo><mo id="mmultiscripts_supscript">&#x2AFF;</mo><mprescripts/><mo id="mmultiscripts_presubscript">&#x2AFF;</mo><mo id="mmultiscripts_presupscript">&#x2AFF;</mo></mmultiscripts></math>
+  <math displaystyle="true"><munder><mo id="munder_base">&#x2AFF;</mo><mo id="munder_underscript">&#x2AFF;</mo></munder></math>
+  <math displaystyle="true"><mover><mo id="mover_base">&#x2AFF;</mo><mo id="mover_overscript">&#x2AFF;</mo></mover></math>
+  <math displaystyle="true"><munderover><mo id="munderover_base">&#x2AFF;</mo><mo id="munderover_underscript">&#x2AFF;</mo><mo id="munderover_overscript">&#x2AFF;</mo></munderover></math>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1-ref.html
new file mode 100644
index 0000000..9fca6f496
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML lengths (reference)</title>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+  <div>
+    <div id="red" style="position: absolute; width: 200px; height: 200px; background: green;">
+    </div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1.html
new file mode 100644
index 0000000..e5864cc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-1.html
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML lengths</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1"/>
+<link rel="match" href="lengths-1-ref.html"/>
+<meta name="assert" content="Verify whether the different units are accepted for MathML lengths.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/xheight500.woff");
+  }
+  span, math {
+    font-family: TestFont;
+    font-size: 10px; /* 1em = 10px, 1ex is about 5px */
+  }
+  span {
+    position: absolute;
+    display: inline-block;
+    height: 10px;
+  }
+  #red > span {
+    background: red;
+  }
+  #green > span {
+    background: green;
+  }
+</style>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+  <div>
+    <div id="red" style="position: absolute; width: 200px; height: 200px; background: green;">
+      <!-- px -->
+      <span style="top: 0px"><math><mspace width="200px"/></math></span>
+      <span style="top: 10px; width: 200px"></span>
+
+      <!-- cm -->
+      <span style="top: 20px"><math><mspace width="5.08cm"/></math></span>
+      <span style="top: 30px; width: 192px"></span>
+
+      <!-- em -->
+      <span style="top: 40px"><math><mspace width="20em"/></math></span>
+      <span style="top: 50px; width: 200px"></span>
+
+      <!-- ex -->
+      <span style="top: 60px"><math><mspace width="30ex"/></math></span>
+      <span style="top: 70px; width: 30ex"></span>
+
+      <!-- in -->
+      <span style="top: 80px"><math><mspace width="2in"/></math></span>
+      <span style="top: 90px; width: 192px"></span>
+
+      <!-- mm -->
+      <span style="top: 100px"><math><mspace width="50.8mm"/></math></span>
+      <span style="top: 110px; width: 192px"></span>
+
+      <!-- pc -->
+      <span style="top: 120px"><math><mspace width="12.5pc"/></math></span>
+      <span style="top: 130px; width: 200px"></span>
+
+      <!-- pt -->
+      <span style="top: 140px"><math><mspace width="150pt"/></math></span>
+      <span style="top: 150px; width: 200px"></span>
+
+      <!-- % -->
+      <span style="top: 160px"><math><mstyle mathsize="2000%"><mspace width="1em"/></mstyle></math></span>
+      <span style="top: 170px; width: 200px"></span>
+
+      <!-- unitless -->
+      <span style="top: 180px"><math><mstyle mathsize="20.0"><mspace width="1em"/></mstyle></math></span>
+      <span style="top: 190px; width: 200px"></span>
+    </div>
+
+    <div id="green" style="position: absolute; width: 200px; height: 200px;">
+      <!-- px -->
+      <span style="top: 10px"><math><mspace width="200px"/></math></span>
+      <span style="top: 0px; width: 200px"></span>
+
+      <!-- cm -->
+      <span style="top: 30px"><math><mspace width="5.08cm"/></math></span>
+      <span style="top: 20px; width: 192px"></span>
+
+      <!-- em -->
+      <span title="em" style="top: 50px"><math><mspace width="20em"/></math></span>
+      <span title="em" style="top: 40px; width: 200px"></span>
+
+      <!-- ex -->
+      <span title="ex" style="top: 70px"><math><mspace width="30ex"/></math></span>
+      <span title="ex" style="top: 60px; width: 30ex"></span>
+
+      <!-- in -->
+      <span style="top: 90px"><math><mspace width="2in"/></math></span>
+      <span style="top: 80px; width: 192px"></span>
+
+      <!-- mm -->
+      <span style="top: 110px"><math><mspace width="50.8mm"/></math></span>
+      <span style="top: 100px; width: 192px"></span>
+
+      <!-- pc -->
+      <span style="top: 130px"><math><mspace width="12.5pc"/></math></span>
+      <span style="top: 120px; width: 200px"></span>
+
+      <!-- pt -->
+      <span style="top: 150px"><math><mspace width="150pt"/></math></span>
+      <span style="top: 140px; width: 200px"></span>
+
+      <!-- % -->
+      <span style="top: 170px"><math><mstyle mathsize="2000%"><mspace width="1em"/></mstyle></math></span>
+      <span style="top: 160px; width: 200px"></span>
+
+      <!-- unitless -->
+      <span style="top: 190px"><math><mstyle mathsize="20.0"><mspace width="1em"/></mstyle></math></span>
+      <span style="top: 180px; width: 200px"></span>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2-ref.html
new file mode 100644
index 0000000..9fca6f496
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML lengths (reference)</title>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+  <div>
+    <div id="red" style="position: absolute; width: 200px; height: 200px; background: green;">
+    </div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2.html
new file mode 100644
index 0000000..e88111a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-2.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML lengths</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1"/>
+<link rel="match" href="lengths-2-ref.html"/>
+<meta name="assert" content="Verify whether the different namedspaces are accepted for MathML lengths.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/xheight500.woff");
+  }
+  mpadded {
+    font-family: TestFont;
+    font-size: 200px; /* 1em = 200px */
+  }
+  div {
+    position: absolute;
+  }
+  #red mspace {
+    background: red;
+  }
+  #green mspace {
+    background: green;
+  }
+</style>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+  <div>
+    <div id="red" style="position: absolute; width: 200px; height: 200px; background: green;">
+      <!-- veryverythinmathspace -->
+      <div style="left: 0px;"><math><mpadded height="100px" depth="100px" voffset="1veryverythinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 10px;"><math><mpadded height="100px" depth="100px" voffset="0.05555555555555556em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- verythinmathspace -->
+      <div style="left: 20px;"><math><mpadded height="100px" depth="100px" voffset="1verythinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 30px;"><math><mpadded height="100px" depth="100px" voffset="0.1111111111111111em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- thinmathspace -->
+      <div style="left: 40px;"><math><mpadded height="100px" depth="100px" voffset="1thinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 50px;"><math><mpadded height="100px" depth="100px" voffset="0.1666666666666667em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- mediummathspace -->
+      <div style="left: 60px;"><math><mpadded height="100px" depth="100px" voffset="1mediummathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 70px;"><math><mpadded height="100px" depth="100px" voffset="0.2222222222222222em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- thickmathspace -->
+      <div style="left: 80px;"><math><mpadded height="100px" depth="100px" voffset="1thickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 90px;"><math><mpadded height="100px" depth="100px" voffset="0.2777777777777778em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- verythickmathspace -->
+      <div style="left: 100px;"><math><mpadded height="100px" depth="100px" voffset="1verythickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 110px;"><math><mpadded height="100px" depth="100px" voffset="0.3333333333333333em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- veryverythickmathspace -->
+      <div style="left: 120px;"><math><mpadded height="100px" depth="100px" voffset="1veryverythickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 130px;"><math><mpadded height="100px" depth="100px" voffset="0.3888888888888889em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativeveryverythinmathspace -->
+      <div style="left: 0px;"><math><mpadded height="100px" depth="100px" voffset="1negativeveryverythinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 10px;"><math><mpadded height="100px" depth="100px" voffset="-0.05555555555555556em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativeverythinmathspace -->
+      <div style="left: 20px;"><math><mpadded height="100px" depth="100px" voffset="1negativeverythinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 30px;"><math><mpadded height="100px" depth="100px" voffset="-0.1111111111111111em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativethinmathspace -->
+      <div style="left: 40px;"><math><mpadded height="100px" depth="100px" voffset="1negativethinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 50px;"><math><mpadded height="100px" depth="100px" voffset="-0.1666666666666667em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativemediummathspace -->
+      <div style="left: 60px;"><math><mpadded height="100px" depth="100px" voffset="1negativemediummathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 70px;"><math><mpadded height="100px" depth="100px" voffset="-0.2222222222222222em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativethickmathspace -->
+      <div style="left: 80px;"><math><mpadded height="100px" depth="100px" voffset="1negativethickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 90px;"><math><mpadded height="100px" depth="100px" voffset="-0.2777777777777778em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativeverythickmathspace -->
+      <div style="left: 100px;"><math><mpadded height="100px" depth="100px" voffset="1negativeverythickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 110px;"><math><mpadded height="100px" depth="100px" voffset="-0.3333333333333333em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativeveryverythickmathspace -->
+      <div style="left: 120px;"><math><mpadded height="100px" depth="100px" voffset="1negativeveryverythickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 130px;"><math><mpadded height="100px" depth="100px" voffset="-0.3888888888888889em"><mspace width="10px" height="10px"/></mpadded></math></div>
+    </div>
+    <div id="green" style="position: absolute; width: 200px; height: 200px;">
+      <!-- veryverythinmathspace -->
+      <div style="left: 10px;"><math><mpadded height="100px" depth="100px" voffset="1veryverythinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 0px;"><math><mpadded height="100px" depth="100px" voffset="0.05555555555555556em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- verythinmathspace -->
+      <div style="left: 30px;"><math><mpadded height="100px" depth="100px" voffset="1verythinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 20px;"><math><mpadded height="100px" depth="100px" voffset="0.1111111111111111em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- thinmathspace -->
+      <div style="left: 50px;"><math><mpadded height="100px" depth="100px" voffset="1thinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 40px;"><math><mpadded height="100px" depth="100px" voffset="0.1666666666666667em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- mediummathspace -->
+      <div style="left: 70px;"><math><mpadded height="100px" depth="100px" voffset="1mediummathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 60px;"><math><mpadded height="100px" depth="100px" voffset="0.2222222222222222em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- thickmathspace -->
+      <div style="left: 90px;"><math><mpadded height="100px" depth="100px" voffset="1thickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 80px;"><math><mpadded height="100px" depth="100px" voffset="0.2777777777777778em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- verythickmathspace -->
+      <div style="left: 110px;"><math><mpadded height="100px" depth="100px" voffset="1verythickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 100px;"><math><mpadded height="100px" depth="100px" voffset="0.3333333333333333em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- veryverythickmathspace -->
+      <div style="left: 130px;"><math><mpadded height="100px" depth="100px" voffset="1veryverythickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 120px;"><math><mpadded height="100px" depth="100px" voffset="0.3888888888888889em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativeveryverythinmathspace -->
+      <div style="left: 10px;"><math><mpadded height="100px" depth="100px" voffset="1negativeveryverythinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 0px;"><math><mpadded height="100px" depth="100px" voffset="-0.05555555555555556em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativeverythinmathspace -->
+      <div style="left: 30px;"><math><mpadded height="100px" depth="100px" voffset="1negativeverythinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 20px;"><math><mpadded height="100px" depth="100px" voffset="-0.1111111111111111em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativethinmathspace -->
+      <div style="left: 50px;"><math><mpadded height="100px" depth="100px" voffset="1negativethinmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 40px;"><math><mpadded height="100px" depth="100px" voffset="-0.1666666666666667em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativemediummathspace -->
+      <div style="left: 70px;"><math><mpadded height="100px" depth="100px" voffset="1negativemediummathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 60px;"><math><mpadded height="100px" depth="100px" voffset="-0.2222222222222222em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativethickmathspace -->
+      <div style="left: 90px;"><math><mpadded height="100px" depth="100px" voffset="1negativethickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 80px;"><math><mpadded height="100px" depth="100px" voffset="-0.2777777777777778em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativeverythickmathspace -->
+      <div style="left: 110px;"><math><mpadded height="100px" depth="100px" voffset="1negativeverythickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 100px;"><math><mpadded height="100px" depth="100px" voffset="-0.3333333333333333em"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <!-- negativeveryverythickmathspace -->
+      <div style="left: 130px;"><math><mpadded height="100px" depth="100px" voffset="1negativeveryverythickmathspace"><mspace width="10px" height="10px"/></mpadded></math></div>
+      <div style="left: 120px;"><math><mpadded height="100px" depth="100px" voffset="-0.3888888888888889em"><mspace width="10px" height="10px"/></mpadded></math></div>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-3.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-3.html
new file mode 100644
index 0000000..a7133f8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/lengths-3.html
@@ -0,0 +1,159 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>MathML lengths</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1"/>
+<meta name="assert" content="Verify various cases of the MathML length syntax.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/xheight500.woff");
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<script>
+  var epsilon = .5;
+
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+
+  setup({ explicit_done: true });
+  window.addEventListener("load", function() {
+    // Delay the check to workaround WebKit's bug https://webkit.org/b/174030.
+    requestAnimationFrame(() => { document.fonts.ready.then(runTests); });
+  });
+
+  function runTests() {
+    test(function() {
+      assert_equals(getBox("unitCm").width, 96, "cm");
+      assert_equals(getBox("unitEm").width, 120, "em");
+      assert_equals(getBox("unitEx").width, 500, "ex");
+      assert_equals(getBox("unitIn").width, 288, "in");
+      assert_equals(getBox("unitNamed").width, 700, "namedspace");
+      assert_equals(getBox("unitMm").width, 576, "mm");
+      assert_equals(getBox("unitPc").width, 96, "pc");
+      assert_equals(getBox("unitPercentage").width, 60, "%");
+      assert_equals(getBox("unitPt").width, 96, "pt");
+      assert_equals(getBox("unitPx").width, 123, "px");
+      assert_equals(getBox("unitNone").width, 150, "Unitless");
+    }, "Units");
+
+    test(function() {
+      assert_equals(getBox("spaceCm").width, 96, "cm");
+      assert_equals(getBox("spaceEm").width, 120, "em");
+      assert_equals(getBox("spaceEx").width, 500, "ex");
+      assert_equals(getBox("spaceIn").width, 288, "in");
+      assert_equals(getBox("spaceNamed").width, 700, "namedspace");
+      assert_equals(getBox("spaceMm").width, 576, "mm");
+      assert_equals(getBox("spacePc").width, 96, "pc");
+      assert_equals(getBox("spacePercentage").width, 60, "%");
+      assert_equals(getBox("spacePt").width, 96, "pt");
+      assert_equals(getBox("spacePx").width, 123, "px");
+      assert_equals(getBox("spaceNone").width, 150, "Unitless");
+    }, "Trimming of space");
+
+    test(function() {
+      assert_approx_equals(getBox("n0").width, 0, epsilon, "n0");
+      assert_approx_equals(getBox("n1").width, 90, epsilon, "n1");
+      assert_approx_equals(getBox("n2").width, 8, epsilon, "n2");
+      assert_approx_equals(getBox("n3").width, 70, epsilon, "n3");
+      assert_approx_equals(getBox("n4").width, 650, epsilon, "n4");
+      assert_approx_equals(getBox("n5").width, 4320, epsilon, "n5");
+      assert_approx_equals(getBox("n6").width, 1, epsilon, "n6");
+      assert_approx_equals(getBox("n7").width, 8, epsilon, "n7");
+      assert_approx_equals(getBox("n8").width, 65, epsilon, "n8");
+      assert_approx_equals(getBox("n9").width, 432, epsilon, "n9");
+      assert_approx_equals(getBox("n10").width, 123, epsilon, "n10");
+    }, "Non-negative numbers");
+
+    test(function() {
+      var topRef = getBox("ref").top;
+      assert_approx_equals(getBox("N0").top - topRef, -0, epsilon, "N0");
+      assert_approx_equals(topRef - getBox("N1").top, -90, epsilon, "N1");
+      assert_approx_equals(topRef - getBox("N2").top, -8, epsilon, "N2");
+      assert_approx_equals(topRef - getBox("N3").top, -70, epsilon, "N3");
+      assert_approx_equals(topRef - getBox("N4").top, -650, epsilon, "N4");
+      assert_approx_equals(topRef - getBox("N5").top, -4320, epsilon, "N5");
+      assert_approx_equals(topRef - getBox("N6").top, -1, epsilon, "N6");
+      assert_approx_equals(topRef - getBox("N7").top, -8, epsilon, "N7");
+      assert_approx_equals(topRef - getBox("N8").top, -65, epsilon, "N8");
+      assert_approx_equals(topRef - getBox("N9").top, -432, epsilon, "N9");
+      assert_approx_equals(topRef - getBox("N10").top, -123, epsilon, "N10");
+    }, "Non-positive numbers");
+
+    done();
+  }
+</script>
+</head>
+<body>
+  <p>
+    <math>
+      <mspace id="unitCm" width="2.54cm"/>
+      <mspace id="unitEm" width="12em"/>
+      <mspace id="unitEx" width="100ex"/>
+      <mspace id="unitIn" width="3in"/>
+      <mspace style="font-size: 1800px" id="unitNamed" width="veryverythickmathspace"/>
+      <mspace id="unitMm" width="152.4mm"/>
+      <mspace id="unitPc" width="6pc"/>
+      <mstyle mathsize="200%"><mspace id="unitPercentage" width="3em"/></mstyle>
+      <mspace id="unitPt" width="72pt"/>
+      <mspace id="unitPx" width="123px"/>
+      <mstyle mathsize="5"><mspace id="unitNone" width="3em"/></mstyle>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mspace id="spaceCm" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;2.54cm&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/>
+      <mspace id="spaceEm" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;12em&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/>
+      <mspace id="spaceEx" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;100ex&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/>
+      <mspace id="spaceIn" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;3in&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/>
+      <mspace style="font-size: 1800px" id="spaceNamed" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;veryverythickmathspace&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/>
+      <mspace id="spaceMm" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;152.4mm&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/>
+      <mspace id="spacePc" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;6pc&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/>
+      <mstyle mathsize="200%"><mspace id="spacePercentage" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;3em&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/></mstyle>
+      <mspace id="spacePt" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;72pt&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/>
+      <mspace id="spacePx" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;123px&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/>
+      <mstyle mathsize="5"><mspace id="spaceNone" width="&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;3em&#x20;&#x9;&#xA;&#xD;&#x20;&#x9;&#xA;&#xD;"/></mstyle>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mspace id="n0" width="0em"/>
+      <mspace id="n1" width="9em"/>
+      <mspace id="n2" width=".8em"/>
+      <mspace id="n3" width="7.em"/>
+      <mspace id="n4" width="65em"/>
+      <mspace id="n5" width="432em"/>
+      <mspace id="n6" width=".10em"/>
+      <mspace id="n7" width=".789em"/>
+      <mspace id="n8" width="6.5em"/>
+      <mspace id="n9" width="43.21em"/>
+      <mspace id="n10" width="012.345em"/>
+    </math>
+  </p>
+  <p>
+    <math>
+      <mspace id="ref"></mspace>
+      <mpadded voffset="-0em"><mspace id="N0"/></mpadded>
+      <mpadded voffset="-9em"><mspace id="N1"/></mpadded>
+      <mpadded voffset="-.8em"><mspace id="N2"/></mpadded>
+      <mpadded voffset="-7.em"><mspace id="N3"/></mpadded>
+      <mpadded voffset="-65em"><mspace id="N4"/></mpadded>
+      <mpadded voffset="-432em"><mspace id="N5"/></mpadded>
+      <mpadded voffset="-.10em"><mspace id="N6"/></mpadded>
+      <mpadded voffset="-.789em"><mspace id="N7"/></mpadded>
+      <mpadded voffset="-6.5em"><mspace id="N8"/></mpadded>
+      <mpadded voffset="-43.21em"><mspace id="N9"/></mpadded>
+      <mpadded voffset="-012.345em"><mspace id="N10"/></mpadded>
+    </math>
+  </p>
+  <hr/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur-ref.html
new file mode 100644
index 0000000..b883b12
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur-ref.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold-fraktur (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold-fraktur.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D56C;</mtext></math>=<span>1D56C</span></span>
+  <span><math><mtext>&#x1D56D;</mtext></math>=<span>1D56D</span></span>
+  <span><math><mtext>&#x1D56E;</mtext></math>=<span>1D56E</span></span>
+  <span><math><mtext>&#x1D56F;</mtext></math>=<span>1D56F</span></span>
+  <span><math><mtext>&#x1D570;</mtext></math>=<span>1D570</span></span>
+  <span><math><mtext>&#x1D571;</mtext></math>=<span>1D571</span></span>
+  <span><math><mtext>&#x1D572;</mtext></math>=<span>1D572</span></span>
+  <span><math><mtext>&#x1D573;</mtext></math>=<span>1D573</span></span>
+  <span><math><mtext>&#x1D574;</mtext></math>=<span>1D574</span></span>
+  <span><math><mtext>&#x1D575;</mtext></math>=<span>1D575</span></span><br/>
+  <span><math><mtext>&#x1D576;</mtext></math>=<span>1D576</span></span>
+  <span><math><mtext>&#x1D577;</mtext></math>=<span>1D577</span></span>
+  <span><math><mtext>&#x1D578;</mtext></math>=<span>1D578</span></span>
+  <span><math><mtext>&#x1D579;</mtext></math>=<span>1D579</span></span>
+  <span><math><mtext>&#x1D57A;</mtext></math>=<span>1D57A</span></span>
+  <span><math><mtext>&#x1D57B;</mtext></math>=<span>1D57B</span></span>
+  <span><math><mtext>&#x1D57C;</mtext></math>=<span>1D57C</span></span>
+  <span><math><mtext>&#x1D57D;</mtext></math>=<span>1D57D</span></span>
+  <span><math><mtext>&#x1D57E;</mtext></math>=<span>1D57E</span></span>
+  <span><math><mtext>&#x1D57F;</mtext></math>=<span>1D57F</span></span><br/>
+  <span><math><mtext>&#x1D580;</mtext></math>=<span>1D580</span></span>
+  <span><math><mtext>&#x1D581;</mtext></math>=<span>1D581</span></span>
+  <span><math><mtext>&#x1D582;</mtext></math>=<span>1D582</span></span>
+  <span><math><mtext>&#x1D583;</mtext></math>=<span>1D583</span></span>
+  <span><math><mtext>&#x1D584;</mtext></math>=<span>1D584</span></span>
+  <span><math><mtext>&#x1D585;</mtext></math>=<span>1D585</span></span>
+  <span><math><mtext>&#x1D586;</mtext></math>=<span>1D586</span></span>
+  <span><math><mtext>&#x1D587;</mtext></math>=<span>1D587</span></span>
+  <span><math><mtext>&#x1D588;</mtext></math>=<span>1D588</span></span>
+  <span><math><mtext>&#x1D589;</mtext></math>=<span>1D589</span></span><br/>
+  <span><math><mtext>&#x1D58A;</mtext></math>=<span>1D58A</span></span>
+  <span><math><mtext>&#x1D58B;</mtext></math>=<span>1D58B</span></span>
+  <span><math><mtext>&#x1D58C;</mtext></math>=<span>1D58C</span></span>
+  <span><math><mtext>&#x1D58D;</mtext></math>=<span>1D58D</span></span>
+  <span><math><mtext>&#x1D58E;</mtext></math>=<span>1D58E</span></span>
+  <span><math><mtext>&#x1D58F;</mtext></math>=<span>1D58F</span></span>
+  <span><math><mtext>&#x1D590;</mtext></math>=<span>1D590</span></span>
+  <span><math><mtext>&#x1D591;</mtext></math>=<span>1D591</span></span>
+  <span><math><mtext>&#x1D592;</mtext></math>=<span>1D592</span></span>
+  <span><math><mtext>&#x1D593;</mtext></math>=<span>1D593</span></span><br/>
+  <span><math><mtext>&#x1D594;</mtext></math>=<span>1D594</span></span>
+  <span><math><mtext>&#x1D595;</mtext></math>=<span>1D595</span></span>
+  <span><math><mtext>&#x1D596;</mtext></math>=<span>1D596</span></span>
+  <span><math><mtext>&#x1D597;</mtext></math>=<span>1D597</span></span>
+  <span><math><mtext>&#x1D598;</mtext></math>=<span>1D598</span></span>
+  <span><math><mtext>&#x1D599;</mtext></math>=<span>1D599</span></span>
+  <span><math><mtext>&#x1D59A;</mtext></math>=<span>1D59A</span></span>
+  <span><math><mtext>&#x1D59B;</mtext></math>=<span>1D59B</span></span>
+  <span><math><mtext>&#x1D59C;</mtext></math>=<span>1D59C</span></span>
+  <span><math><mtext>&#x1D59D;</mtext></math>=<span>1D59D</span></span><br/>
+  <span><math><mtext>&#x1D59E;</mtext></math>=<span>1D59E</span></span>
+  <span><math><mtext>&#x1D59F;</mtext></math>=<span>1D59F</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur.html
new file mode 100644
index 0000000..487d61d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-fraktur.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold-fraktur</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-bold-fraktur-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a bold-fraktur mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold-fraktur.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="bold-fraktur">&#x41;</mtext></math>=<span>1D56C</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x42;</mtext></math>=<span>1D56D</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x43;</mtext></math>=<span>1D56E</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x44;</mtext></math>=<span>1D56F</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x45;</mtext></math>=<span>1D570</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x46;</mtext></math>=<span>1D571</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x47;</mtext></math>=<span>1D572</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x48;</mtext></math>=<span>1D573</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x49;</mtext></math>=<span>1D574</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x4A;</mtext></math>=<span>1D575</span></span><br/>
+  <span><math><mtext mathvariant="bold-fraktur">&#x4B;</mtext></math>=<span>1D576</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x4C;</mtext></math>=<span>1D577</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x4D;</mtext></math>=<span>1D578</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x4E;</mtext></math>=<span>1D579</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x4F;</mtext></math>=<span>1D57A</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x50;</mtext></math>=<span>1D57B</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x51;</mtext></math>=<span>1D57C</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x52;</mtext></math>=<span>1D57D</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x53;</mtext></math>=<span>1D57E</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x54;</mtext></math>=<span>1D57F</span></span><br/>
+  <span><math><mtext mathvariant="bold-fraktur">&#x55;</mtext></math>=<span>1D580</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x56;</mtext></math>=<span>1D581</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x57;</mtext></math>=<span>1D582</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x58;</mtext></math>=<span>1D583</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x59;</mtext></math>=<span>1D584</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x5A;</mtext></math>=<span>1D585</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x61;</mtext></math>=<span>1D586</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x62;</mtext></math>=<span>1D587</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x63;</mtext></math>=<span>1D588</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x64;</mtext></math>=<span>1D589</span></span><br/>
+  <span><math><mtext mathvariant="bold-fraktur">&#x65;</mtext></math>=<span>1D58A</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x66;</mtext></math>=<span>1D58B</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x67;</mtext></math>=<span>1D58C</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x68;</mtext></math>=<span>1D58D</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x69;</mtext></math>=<span>1D58E</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x6A;</mtext></math>=<span>1D58F</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x6B;</mtext></math>=<span>1D590</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x6C;</mtext></math>=<span>1D591</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x6D;</mtext></math>=<span>1D592</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x6E;</mtext></math>=<span>1D593</span></span><br/>
+  <span><math><mtext mathvariant="bold-fraktur">&#x6F;</mtext></math>=<span>1D594</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x70;</mtext></math>=<span>1D595</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x71;</mtext></math>=<span>1D596</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x72;</mtext></math>=<span>1D597</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x73;</mtext></math>=<span>1D598</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x74;</mtext></math>=<span>1D599</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x75;</mtext></math>=<span>1D59A</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x76;</mtext></math>=<span>1D59B</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x77;</mtext></math>=<span>1D59C</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x78;</mtext></math>=<span>1D59D</span></span><br/>
+  <span><math><mtext mathvariant="bold-fraktur">&#x79;</mtext></math>=<span>1D59E</span></span>
+  <span><math><mtext mathvariant="bold-fraktur">&#x7A;</mtext></math>=<span>1D59F</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic-ref.html
new file mode 100644
index 0000000..1e71e89
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic-ref.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold-italic (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold-italic.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D74F;</mtext></math>=<span>1D74F</span></span>
+  <span><math><mtext>&#x1D735;</mtext></math>=<span>1D735</span></span>
+  <span><math><mtext>&#x1D468;</mtext></math>=<span>1D468</span></span>
+  <span><math><mtext>&#x1D469;</mtext></math>=<span>1D469</span></span>
+  <span><math><mtext>&#x1D46A;</mtext></math>=<span>1D46A</span></span>
+  <span><math><mtext>&#x1D46B;</mtext></math>=<span>1D46B</span></span>
+  <span><math><mtext>&#x1D46C;</mtext></math>=<span>1D46C</span></span>
+  <span><math><mtext>&#x1D46D;</mtext></math>=<span>1D46D</span></span>
+  <span><math><mtext>&#x1D46E;</mtext></math>=<span>1D46E</span></span>
+  <span><math><mtext>&#x1D46F;</mtext></math>=<span>1D46F</span></span><br/>
+  <span><math><mtext>&#x1D470;</mtext></math>=<span>1D470</span></span>
+  <span><math><mtext>&#x1D471;</mtext></math>=<span>1D471</span></span>
+  <span><math><mtext>&#x1D472;</mtext></math>=<span>1D472</span></span>
+  <span><math><mtext>&#x1D473;</mtext></math>=<span>1D473</span></span>
+  <span><math><mtext>&#x1D474;</mtext></math>=<span>1D474</span></span>
+  <span><math><mtext>&#x1D475;</mtext></math>=<span>1D475</span></span>
+  <span><math><mtext>&#x1D476;</mtext></math>=<span>1D476</span></span>
+  <span><math><mtext>&#x1D477;</mtext></math>=<span>1D477</span></span>
+  <span><math><mtext>&#x1D478;</mtext></math>=<span>1D478</span></span>
+  <span><math><mtext>&#x1D479;</mtext></math>=<span>1D479</span></span><br/>
+  <span><math><mtext>&#x1D47A;</mtext></math>=<span>1D47A</span></span>
+  <span><math><mtext>&#x1D47B;</mtext></math>=<span>1D47B</span></span>
+  <span><math><mtext>&#x1D47C;</mtext></math>=<span>1D47C</span></span>
+  <span><math><mtext>&#x1D47D;</mtext></math>=<span>1D47D</span></span>
+  <span><math><mtext>&#x1D47E;</mtext></math>=<span>1D47E</span></span>
+  <span><math><mtext>&#x1D47F;</mtext></math>=<span>1D47F</span></span>
+  <span><math><mtext>&#x1D480;</mtext></math>=<span>1D480</span></span>
+  <span><math><mtext>&#x1D481;</mtext></math>=<span>1D481</span></span>
+  <span><math><mtext>&#x1D482;</mtext></math>=<span>1D482</span></span>
+  <span><math><mtext>&#x1D483;</mtext></math>=<span>1D483</span></span><br/>
+  <span><math><mtext>&#x1D484;</mtext></math>=<span>1D484</span></span>
+  <span><math><mtext>&#x1D485;</mtext></math>=<span>1D485</span></span>
+  <span><math><mtext>&#x1D486;</mtext></math>=<span>1D486</span></span>
+  <span><math><mtext>&#x1D487;</mtext></math>=<span>1D487</span></span>
+  <span><math><mtext>&#x1D488;</mtext></math>=<span>1D488</span></span>
+  <span><math><mtext>&#x1D489;</mtext></math>=<span>1D489</span></span>
+  <span><math><mtext>&#x1D48A;</mtext></math>=<span>1D48A</span></span>
+  <span><math><mtext>&#x1D48B;</mtext></math>=<span>1D48B</span></span>
+  <span><math><mtext>&#x1D48C;</mtext></math>=<span>1D48C</span></span>
+  <span><math><mtext>&#x1D48D;</mtext></math>=<span>1D48D</span></span><br/>
+  <span><math><mtext>&#x1D48E;</mtext></math>=<span>1D48E</span></span>
+  <span><math><mtext>&#x1D48F;</mtext></math>=<span>1D48F</span></span>
+  <span><math><mtext>&#x1D490;</mtext></math>=<span>1D490</span></span>
+  <span><math><mtext>&#x1D491;</mtext></math>=<span>1D491</span></span>
+  <span><math><mtext>&#x1D492;</mtext></math>=<span>1D492</span></span>
+  <span><math><mtext>&#x1D493;</mtext></math>=<span>1D493</span></span>
+  <span><math><mtext>&#x1D494;</mtext></math>=<span>1D494</span></span>
+  <span><math><mtext>&#x1D495;</mtext></math>=<span>1D495</span></span>
+  <span><math><mtext>&#x1D496;</mtext></math>=<span>1D496</span></span>
+  <span><math><mtext>&#x1D497;</mtext></math>=<span>1D497</span></span><br/>
+  <span><math><mtext>&#x1D498;</mtext></math>=<span>1D498</span></span>
+  <span><math><mtext>&#x1D499;</mtext></math>=<span>1D499</span></span>
+  <span><math><mtext>&#x1D49A;</mtext></math>=<span>1D49A</span></span>
+  <span><math><mtext>&#x1D49B;</mtext></math>=<span>1D49B</span></span>
+  <span><math><mtext>&#x1D71C;</mtext></math>=<span>1D71C</span></span>
+  <span><math><mtext>&#x1D71D;</mtext></math>=<span>1D71D</span></span>
+  <span><math><mtext>&#x1D71E;</mtext></math>=<span>1D71E</span></span>
+  <span><math><mtext>&#x1D71F;</mtext></math>=<span>1D71F</span></span>
+  <span><math><mtext>&#x1D720;</mtext></math>=<span>1D720</span></span>
+  <span><math><mtext>&#x1D721;</mtext></math>=<span>1D721</span></span><br/>
+  <span><math><mtext>&#x1D722;</mtext></math>=<span>1D722</span></span>
+  <span><math><mtext>&#x1D723;</mtext></math>=<span>1D723</span></span>
+  <span><math><mtext>&#x1D724;</mtext></math>=<span>1D724</span></span>
+  <span><math><mtext>&#x1D725;</mtext></math>=<span>1D725</span></span>
+  <span><math><mtext>&#x1D726;</mtext></math>=<span>1D726</span></span>
+  <span><math><mtext>&#x1D727;</mtext></math>=<span>1D727</span></span>
+  <span><math><mtext>&#x1D728;</mtext></math>=<span>1D728</span></span>
+  <span><math><mtext>&#x1D729;</mtext></math>=<span>1D729</span></span>
+  <span><math><mtext>&#x1D72A;</mtext></math>=<span>1D72A</span></span>
+  <span><math><mtext>&#x1D72B;</mtext></math>=<span>1D72B</span></span><br/>
+  <span><math><mtext>&#x1D72C;</mtext></math>=<span>1D72C</span></span>
+  <span><math><mtext>&#x1D72E;</mtext></math>=<span>1D72E</span></span>
+  <span><math><mtext>&#x1D72F;</mtext></math>=<span>1D72F</span></span>
+  <span><math><mtext>&#x1D730;</mtext></math>=<span>1D730</span></span>
+  <span><math><mtext>&#x1D731;</mtext></math>=<span>1D731</span></span>
+  <span><math><mtext>&#x1D732;</mtext></math>=<span>1D732</span></span>
+  <span><math><mtext>&#x1D733;</mtext></math>=<span>1D733</span></span>
+  <span><math><mtext>&#x1D734;</mtext></math>=<span>1D734</span></span>
+  <span><math><mtext>&#x1D736;</mtext></math>=<span>1D736</span></span>
+  <span><math><mtext>&#x1D737;</mtext></math>=<span>1D737</span></span><br/>
+  <span><math><mtext>&#x1D738;</mtext></math>=<span>1D738</span></span>
+  <span><math><mtext>&#x1D739;</mtext></math>=<span>1D739</span></span>
+  <span><math><mtext>&#x1D73A;</mtext></math>=<span>1D73A</span></span>
+  <span><math><mtext>&#x1D73B;</mtext></math>=<span>1D73B</span></span>
+  <span><math><mtext>&#x1D73C;</mtext></math>=<span>1D73C</span></span>
+  <span><math><mtext>&#x1D73D;</mtext></math>=<span>1D73D</span></span>
+  <span><math><mtext>&#x1D73E;</mtext></math>=<span>1D73E</span></span>
+  <span><math><mtext>&#x1D73F;</mtext></math>=<span>1D73F</span></span>
+  <span><math><mtext>&#x1D740;</mtext></math>=<span>1D740</span></span>
+  <span><math><mtext>&#x1D741;</mtext></math>=<span>1D741</span></span><br/>
+  <span><math><mtext>&#x1D742;</mtext></math>=<span>1D742</span></span>
+  <span><math><mtext>&#x1D743;</mtext></math>=<span>1D743</span></span>
+  <span><math><mtext>&#x1D744;</mtext></math>=<span>1D744</span></span>
+  <span><math><mtext>&#x1D745;</mtext></math>=<span>1D745</span></span>
+  <span><math><mtext>&#x1D746;</mtext></math>=<span>1D746</span></span>
+  <span><math><mtext>&#x1D747;</mtext></math>=<span>1D747</span></span>
+  <span><math><mtext>&#x1D748;</mtext></math>=<span>1D748</span></span>
+  <span><math><mtext>&#x1D749;</mtext></math>=<span>1D749</span></span>
+  <span><math><mtext>&#x1D74A;</mtext></math>=<span>1D74A</span></span>
+  <span><math><mtext>&#x1D74B;</mtext></math>=<span>1D74B</span></span><br/>
+  <span><math><mtext>&#x1D74C;</mtext></math>=<span>1D74C</span></span>
+  <span><math><mtext>&#x1D74D;</mtext></math>=<span>1D74D</span></span>
+  <span><math><mtext>&#x1D74E;</mtext></math>=<span>1D74E</span></span>
+  <span><math><mtext>&#x1D751;</mtext></math>=<span>1D751</span></span>
+  <span><math><mtext>&#x1D753;</mtext></math>=<span>1D753</span></span>
+  <span><math><mtext>&#x1D755;</mtext></math>=<span>1D755</span></span>
+  <span><math><mtext>&#x1D752;</mtext></math>=<span>1D752</span></span>
+  <span><math><mtext>&#x1D754;</mtext></math>=<span>1D754</span></span>
+  <span><math><mtext>&#x1D72D;</mtext></math>=<span>1D72D</span></span>
+  <span><math><mtext>&#x1D750;</mtext></math>=<span>1D750</span></span><br/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic.html
new file mode 100644
index 0000000..640baf3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-italic.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold-italic</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-bold-italic-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a bold-italic mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold-italic.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="bold-italic">&#x2202;</mtext></math>=<span>1D74F</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x2207;</mtext></math>=<span>1D735</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x41;</mtext></math>=<span>1D468</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x42;</mtext></math>=<span>1D469</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x43;</mtext></math>=<span>1D46A</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x44;</mtext></math>=<span>1D46B</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x45;</mtext></math>=<span>1D46C</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x46;</mtext></math>=<span>1D46D</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x47;</mtext></math>=<span>1D46E</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x48;</mtext></math>=<span>1D46F</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x49;</mtext></math>=<span>1D470</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x4A;</mtext></math>=<span>1D471</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x4B;</mtext></math>=<span>1D472</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x4C;</mtext></math>=<span>1D473</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x4D;</mtext></math>=<span>1D474</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x4E;</mtext></math>=<span>1D475</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x4F;</mtext></math>=<span>1D476</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x50;</mtext></math>=<span>1D477</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x51;</mtext></math>=<span>1D478</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x52;</mtext></math>=<span>1D479</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x53;</mtext></math>=<span>1D47A</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x54;</mtext></math>=<span>1D47B</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x55;</mtext></math>=<span>1D47C</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x56;</mtext></math>=<span>1D47D</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x57;</mtext></math>=<span>1D47E</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x58;</mtext></math>=<span>1D47F</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x59;</mtext></math>=<span>1D480</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x5A;</mtext></math>=<span>1D481</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x61;</mtext></math>=<span>1D482</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x62;</mtext></math>=<span>1D483</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x63;</mtext></math>=<span>1D484</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x64;</mtext></math>=<span>1D485</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x65;</mtext></math>=<span>1D486</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x66;</mtext></math>=<span>1D487</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x67;</mtext></math>=<span>1D488</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x68;</mtext></math>=<span>1D489</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x69;</mtext></math>=<span>1D48A</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x6A;</mtext></math>=<span>1D48B</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x6B;</mtext></math>=<span>1D48C</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x6C;</mtext></math>=<span>1D48D</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x6D;</mtext></math>=<span>1D48E</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x6E;</mtext></math>=<span>1D48F</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x6F;</mtext></math>=<span>1D490</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x70;</mtext></math>=<span>1D491</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x71;</mtext></math>=<span>1D492</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x72;</mtext></math>=<span>1D493</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x73;</mtext></math>=<span>1D494</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x74;</mtext></math>=<span>1D495</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x75;</mtext></math>=<span>1D496</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x76;</mtext></math>=<span>1D497</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x77;</mtext></math>=<span>1D498</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x78;</mtext></math>=<span>1D499</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x79;</mtext></math>=<span>1D49A</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x7A;</mtext></math>=<span>1D49B</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x391;</mtext></math>=<span>1D71C</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x392;</mtext></math>=<span>1D71D</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x393;</mtext></math>=<span>1D71E</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x394;</mtext></math>=<span>1D71F</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x395;</mtext></math>=<span>1D720</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x396;</mtext></math>=<span>1D721</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x397;</mtext></math>=<span>1D722</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x398;</mtext></math>=<span>1D723</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x399;</mtext></math>=<span>1D724</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x39A;</mtext></math>=<span>1D725</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x39B;</mtext></math>=<span>1D726</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x39C;</mtext></math>=<span>1D727</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x39D;</mtext></math>=<span>1D728</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x39E;</mtext></math>=<span>1D729</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x39F;</mtext></math>=<span>1D72A</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3A0;</mtext></math>=<span>1D72B</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x3A1;</mtext></math>=<span>1D72C</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3A3;</mtext></math>=<span>1D72E</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3A4;</mtext></math>=<span>1D72F</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3A5;</mtext></math>=<span>1D730</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3A6;</mtext></math>=<span>1D731</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3A7;</mtext></math>=<span>1D732</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3A8;</mtext></math>=<span>1D733</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3A9;</mtext></math>=<span>1D734</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3B1;</mtext></math>=<span>1D736</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3B2;</mtext></math>=<span>1D737</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x3B3;</mtext></math>=<span>1D738</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3B4;</mtext></math>=<span>1D739</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3B5;</mtext></math>=<span>1D73A</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3B6;</mtext></math>=<span>1D73B</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3B7;</mtext></math>=<span>1D73C</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3B8;</mtext></math>=<span>1D73D</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3B9;</mtext></math>=<span>1D73E</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3BA;</mtext></math>=<span>1D73F</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3BB;</mtext></math>=<span>1D740</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3BC;</mtext></math>=<span>1D741</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x3BD;</mtext></math>=<span>1D742</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3BE;</mtext></math>=<span>1D743</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3BF;</mtext></math>=<span>1D744</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3C0;</mtext></math>=<span>1D745</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3C1;</mtext></math>=<span>1D746</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3C2;</mtext></math>=<span>1D747</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3C3;</mtext></math>=<span>1D748</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3C4;</mtext></math>=<span>1D749</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3C5;</mtext></math>=<span>1D74A</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3C6;</mtext></math>=<span>1D74B</span></span><br/>
+  <span><math><mtext mathvariant="bold-italic">&#x3C7;</mtext></math>=<span>1D74C</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3C8;</mtext></math>=<span>1D74D</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3C9;</mtext></math>=<span>1D74E</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3D1;</mtext></math>=<span>1D751</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3D5;</mtext></math>=<span>1D753</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3D6;</mtext></math>=<span>1D755</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3F0;</mtext></math>=<span>1D752</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3F1;</mtext></math>=<span>1D754</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3F4;</mtext></math>=<span>1D72D</span></span>
+  <span><math><mtext mathvariant="bold-italic">&#x3F5;</mtext></math>=<span>1D750</span></span><br/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-ref.html
new file mode 100644
index 0000000..11cb2de
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-ref.html
@@ -0,0 +1,149 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D6DB;</mtext></math>=<span>1D6DB</span></span>
+  <span><math><mtext>&#x1D6C1;</mtext></math>=<span>1D6C1</span></span>
+  <span><math><mtext>&#x1D7CE;</mtext></math>=<span>1D7CE</span></span>
+  <span><math><mtext>&#x1D7CF;</mtext></math>=<span>1D7CF</span></span>
+  <span><math><mtext>&#x1D7D0;</mtext></math>=<span>1D7D0</span></span>
+  <span><math><mtext>&#x1D7D1;</mtext></math>=<span>1D7D1</span></span>
+  <span><math><mtext>&#x1D7D2;</mtext></math>=<span>1D7D2</span></span>
+  <span><math><mtext>&#x1D7D3;</mtext></math>=<span>1D7D3</span></span>
+  <span><math><mtext>&#x1D7D4;</mtext></math>=<span>1D7D4</span></span>
+  <span><math><mtext>&#x1D7D5;</mtext></math>=<span>1D7D5</span></span><br/>
+  <span><math><mtext>&#x1D7D6;</mtext></math>=<span>1D7D6</span></span>
+  <span><math><mtext>&#x1D7D7;</mtext></math>=<span>1D7D7</span></span>
+  <span><math><mtext>&#x1D400;</mtext></math>=<span>1D400</span></span>
+  <span><math><mtext>&#x1D401;</mtext></math>=<span>1D401</span></span>
+  <span><math><mtext>&#x1D402;</mtext></math>=<span>1D402</span></span>
+  <span><math><mtext>&#x1D403;</mtext></math>=<span>1D403</span></span>
+  <span><math><mtext>&#x1D404;</mtext></math>=<span>1D404</span></span>
+  <span><math><mtext>&#x1D405;</mtext></math>=<span>1D405</span></span>
+  <span><math><mtext>&#x1D406;</mtext></math>=<span>1D406</span></span>
+  <span><math><mtext>&#x1D407;</mtext></math>=<span>1D407</span></span><br/>
+  <span><math><mtext>&#x1D408;</mtext></math>=<span>1D408</span></span>
+  <span><math><mtext>&#x1D409;</mtext></math>=<span>1D409</span></span>
+  <span><math><mtext>&#x1D40A;</mtext></math>=<span>1D40A</span></span>
+  <span><math><mtext>&#x1D40B;</mtext></math>=<span>1D40B</span></span>
+  <span><math><mtext>&#x1D40C;</mtext></math>=<span>1D40C</span></span>
+  <span><math><mtext>&#x1D40D;</mtext></math>=<span>1D40D</span></span>
+  <span><math><mtext>&#x1D40E;</mtext></math>=<span>1D40E</span></span>
+  <span><math><mtext>&#x1D40F;</mtext></math>=<span>1D40F</span></span>
+  <span><math><mtext>&#x1D410;</mtext></math>=<span>1D410</span></span>
+  <span><math><mtext>&#x1D411;</mtext></math>=<span>1D411</span></span><br/>
+  <span><math><mtext>&#x1D412;</mtext></math>=<span>1D412</span></span>
+  <span><math><mtext>&#x1D413;</mtext></math>=<span>1D413</span></span>
+  <span><math><mtext>&#x1D414;</mtext></math>=<span>1D414</span></span>
+  <span><math><mtext>&#x1D415;</mtext></math>=<span>1D415</span></span>
+  <span><math><mtext>&#x1D416;</mtext></math>=<span>1D416</span></span>
+  <span><math><mtext>&#x1D417;</mtext></math>=<span>1D417</span></span>
+  <span><math><mtext>&#x1D418;</mtext></math>=<span>1D418</span></span>
+  <span><math><mtext>&#x1D419;</mtext></math>=<span>1D419</span></span>
+  <span><math><mtext>&#x1D41A;</mtext></math>=<span>1D41A</span></span>
+  <span><math><mtext>&#x1D41B;</mtext></math>=<span>1D41B</span></span><br/>
+  <span><math><mtext>&#x1D41C;</mtext></math>=<span>1D41C</span></span>
+  <span><math><mtext>&#x1D41D;</mtext></math>=<span>1D41D</span></span>
+  <span><math><mtext>&#x1D41E;</mtext></math>=<span>1D41E</span></span>
+  <span><math><mtext>&#x1D41F;</mtext></math>=<span>1D41F</span></span>
+  <span><math><mtext>&#x1D420;</mtext></math>=<span>1D420</span></span>
+  <span><math><mtext>&#x1D421;</mtext></math>=<span>1D421</span></span>
+  <span><math><mtext>&#x1D422;</mtext></math>=<span>1D422</span></span>
+  <span><math><mtext>&#x1D423;</mtext></math>=<span>1D423</span></span>
+  <span><math><mtext>&#x1D424;</mtext></math>=<span>1D424</span></span>
+  <span><math><mtext>&#x1D425;</mtext></math>=<span>1D425</span></span><br/>
+  <span><math><mtext>&#x1D426;</mtext></math>=<span>1D426</span></span>
+  <span><math><mtext>&#x1D427;</mtext></math>=<span>1D427</span></span>
+  <span><math><mtext>&#x1D428;</mtext></math>=<span>1D428</span></span>
+  <span><math><mtext>&#x1D429;</mtext></math>=<span>1D429</span></span>
+  <span><math><mtext>&#x1D42A;</mtext></math>=<span>1D42A</span></span>
+  <span><math><mtext>&#x1D42B;</mtext></math>=<span>1D42B</span></span>
+  <span><math><mtext>&#x1D42C;</mtext></math>=<span>1D42C</span></span>
+  <span><math><mtext>&#x1D42D;</mtext></math>=<span>1D42D</span></span>
+  <span><math><mtext>&#x1D42E;</mtext></math>=<span>1D42E</span></span>
+  <span><math><mtext>&#x1D42F;</mtext></math>=<span>1D42F</span></span><br/>
+  <span><math><mtext>&#x1D430;</mtext></math>=<span>1D430</span></span>
+  <span><math><mtext>&#x1D431;</mtext></math>=<span>1D431</span></span>
+  <span><math><mtext>&#x1D432;</mtext></math>=<span>1D432</span></span>
+  <span><math><mtext>&#x1D433;</mtext></math>=<span>1D433</span></span>
+  <span><math><mtext>&#x1D6A8;</mtext></math>=<span>1D6A8</span></span>
+  <span><math><mtext>&#x1D6A9;</mtext></math>=<span>1D6A9</span></span>
+  <span><math><mtext>&#x1D6AA;</mtext></math>=<span>1D6AA</span></span>
+  <span><math><mtext>&#x1D6AB;</mtext></math>=<span>1D6AB</span></span>
+  <span><math><mtext>&#x1D6AC;</mtext></math>=<span>1D6AC</span></span>
+  <span><math><mtext>&#x1D6AD;</mtext></math>=<span>1D6AD</span></span><br/>
+  <span><math><mtext>&#x1D6AE;</mtext></math>=<span>1D6AE</span></span>
+  <span><math><mtext>&#x1D6AF;</mtext></math>=<span>1D6AF</span></span>
+  <span><math><mtext>&#x1D6B0;</mtext></math>=<span>1D6B0</span></span>
+  <span><math><mtext>&#x1D6B1;</mtext></math>=<span>1D6B1</span></span>
+  <span><math><mtext>&#x1D6B2;</mtext></math>=<span>1D6B2</span></span>
+  <span><math><mtext>&#x1D6B3;</mtext></math>=<span>1D6B3</span></span>
+  <span><math><mtext>&#x1D6B4;</mtext></math>=<span>1D6B4</span></span>
+  <span><math><mtext>&#x1D6B5;</mtext></math>=<span>1D6B5</span></span>
+  <span><math><mtext>&#x1D6B6;</mtext></math>=<span>1D6B6</span></span>
+  <span><math><mtext>&#x1D6B7;</mtext></math>=<span>1D6B7</span></span><br/>
+  <span><math><mtext>&#x1D6B8;</mtext></math>=<span>1D6B8</span></span>
+  <span><math><mtext>&#x1D6BA;</mtext></math>=<span>1D6BA</span></span>
+  <span><math><mtext>&#x1D6BB;</mtext></math>=<span>1D6BB</span></span>
+  <span><math><mtext>&#x1D6BC;</mtext></math>=<span>1D6BC</span></span>
+  <span><math><mtext>&#x1D6BD;</mtext></math>=<span>1D6BD</span></span>
+  <span><math><mtext>&#x1D6BE;</mtext></math>=<span>1D6BE</span></span>
+  <span><math><mtext>&#x1D6BF;</mtext></math>=<span>1D6BF</span></span>
+  <span><math><mtext>&#x1D6C0;</mtext></math>=<span>1D6C0</span></span>
+  <span><math><mtext>&#x1D6C2;</mtext></math>=<span>1D6C2</span></span>
+  <span><math><mtext>&#x1D6C3;</mtext></math>=<span>1D6C3</span></span><br/>
+  <span><math><mtext>&#x1D6C4;</mtext></math>=<span>1D6C4</span></span>
+  <span><math><mtext>&#x1D6C5;</mtext></math>=<span>1D6C5</span></span>
+  <span><math><mtext>&#x1D6C6;</mtext></math>=<span>1D6C6</span></span>
+  <span><math><mtext>&#x1D6C7;</mtext></math>=<span>1D6C7</span></span>
+  <span><math><mtext>&#x1D6C8;</mtext></math>=<span>1D6C8</span></span>
+  <span><math><mtext>&#x1D6C9;</mtext></math>=<span>1D6C9</span></span>
+  <span><math><mtext>&#x1D6CA;</mtext></math>=<span>1D6CA</span></span>
+  <span><math><mtext>&#x1D6CB;</mtext></math>=<span>1D6CB</span></span>
+  <span><math><mtext>&#x1D6CC;</mtext></math>=<span>1D6CC</span></span>
+  <span><math><mtext>&#x1D6CD;</mtext></math>=<span>1D6CD</span></span><br/>
+  <span><math><mtext>&#x1D6CE;</mtext></math>=<span>1D6CE</span></span>
+  <span><math><mtext>&#x1D6CF;</mtext></math>=<span>1D6CF</span></span>
+  <span><math><mtext>&#x1D6D0;</mtext></math>=<span>1D6D0</span></span>
+  <span><math><mtext>&#x1D6D1;</mtext></math>=<span>1D6D1</span></span>
+  <span><math><mtext>&#x1D6D2;</mtext></math>=<span>1D6D2</span></span>
+  <span><math><mtext>&#x1D6D3;</mtext></math>=<span>1D6D3</span></span>
+  <span><math><mtext>&#x1D6D4;</mtext></math>=<span>1D6D4</span></span>
+  <span><math><mtext>&#x1D6D5;</mtext></math>=<span>1D6D5</span></span>
+  <span><math><mtext>&#x1D6D6;</mtext></math>=<span>1D6D6</span></span>
+  <span><math><mtext>&#x1D6D7;</mtext></math>=<span>1D6D7</span></span><br/>
+  <span><math><mtext>&#x1D6D8;</mtext></math>=<span>1D6D8</span></span>
+  <span><math><mtext>&#x1D6D9;</mtext></math>=<span>1D6D9</span></span>
+  <span><math><mtext>&#x1D6DA;</mtext></math>=<span>1D6DA</span></span>
+  <span><math><mtext>&#x1D6DD;</mtext></math>=<span>1D6DD</span></span>
+  <span><math><mtext>&#x1D6DF;</mtext></math>=<span>1D6DF</span></span>
+  <span><math><mtext>&#x1D6E1;</mtext></math>=<span>1D6E1</span></span>
+  <span><math><mtext>&#x1D7CA;</mtext></math>=<span>1D7CA</span></span>
+  <span><math><mtext>&#x1D7CB;</mtext></math>=<span>1D7CB</span></span>
+  <span><math><mtext>&#x1D6DE;</mtext></math>=<span>1D6DE</span></span>
+  <span><math><mtext>&#x1D6E0;</mtext></math>=<span>1D6E0</span></span><br/>
+  <span><math><mtext>&#x1D6B9;</mtext></math>=<span>1D6B9</span></span>
+  <span><math><mtext>&#x1D6DC;</mtext></math>=<span>1D6DC</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif-ref.html
new file mode 100644
index 0000000..a57fd18
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif-ref.html
@@ -0,0 +1,147 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold-sans-serif (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold-sans-serif.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D789;</mtext></math>=<span>1D789</span></span>
+  <span><math><mtext>&#x1D76F;</mtext></math>=<span>1D76F</span></span>
+  <span><math><mtext>&#x1D7EC;</mtext></math>=<span>1D7EC</span></span>
+  <span><math><mtext>&#x1D7ED;</mtext></math>=<span>1D7ED</span></span>
+  <span><math><mtext>&#x1D7EE;</mtext></math>=<span>1D7EE</span></span>
+  <span><math><mtext>&#x1D7EF;</mtext></math>=<span>1D7EF</span></span>
+  <span><math><mtext>&#x1D7F0;</mtext></math>=<span>1D7F0</span></span>
+  <span><math><mtext>&#x1D7F1;</mtext></math>=<span>1D7F1</span></span>
+  <span><math><mtext>&#x1D7F2;</mtext></math>=<span>1D7F2</span></span>
+  <span><math><mtext>&#x1D7F3;</mtext></math>=<span>1D7F3</span></span><br/>
+  <span><math><mtext>&#x1D7F4;</mtext></math>=<span>1D7F4</span></span>
+  <span><math><mtext>&#x1D7F5;</mtext></math>=<span>1D7F5</span></span>
+  <span><math><mtext>&#x1D5D4;</mtext></math>=<span>1D5D4</span></span>
+  <span><math><mtext>&#x1D5D5;</mtext></math>=<span>1D5D5</span></span>
+  <span><math><mtext>&#x1D5D6;</mtext></math>=<span>1D5D6</span></span>
+  <span><math><mtext>&#x1D5D7;</mtext></math>=<span>1D5D7</span></span>
+  <span><math><mtext>&#x1D5D8;</mtext></math>=<span>1D5D8</span></span>
+  <span><math><mtext>&#x1D5D9;</mtext></math>=<span>1D5D9</span></span>
+  <span><math><mtext>&#x1D5DA;</mtext></math>=<span>1D5DA</span></span>
+  <span><math><mtext>&#x1D5DB;</mtext></math>=<span>1D5DB</span></span><br/>
+  <span><math><mtext>&#x1D5DC;</mtext></math>=<span>1D5DC</span></span>
+  <span><math><mtext>&#x1D5DD;</mtext></math>=<span>1D5DD</span></span>
+  <span><math><mtext>&#x1D5DE;</mtext></math>=<span>1D5DE</span></span>
+  <span><math><mtext>&#x1D5DF;</mtext></math>=<span>1D5DF</span></span>
+  <span><math><mtext>&#x1D5E0;</mtext></math>=<span>1D5E0</span></span>
+  <span><math><mtext>&#x1D5E1;</mtext></math>=<span>1D5E1</span></span>
+  <span><math><mtext>&#x1D5E2;</mtext></math>=<span>1D5E2</span></span>
+  <span><math><mtext>&#x1D5E3;</mtext></math>=<span>1D5E3</span></span>
+  <span><math><mtext>&#x1D5E4;</mtext></math>=<span>1D5E4</span></span>
+  <span><math><mtext>&#x1D5E5;</mtext></math>=<span>1D5E5</span></span><br/>
+  <span><math><mtext>&#x1D5E6;</mtext></math>=<span>1D5E6</span></span>
+  <span><math><mtext>&#x1D5E7;</mtext></math>=<span>1D5E7</span></span>
+  <span><math><mtext>&#x1D5E8;</mtext></math>=<span>1D5E8</span></span>
+  <span><math><mtext>&#x1D5E9;</mtext></math>=<span>1D5E9</span></span>
+  <span><math><mtext>&#x1D5EA;</mtext></math>=<span>1D5EA</span></span>
+  <span><math><mtext>&#x1D5EB;</mtext></math>=<span>1D5EB</span></span>
+  <span><math><mtext>&#x1D5EC;</mtext></math>=<span>1D5EC</span></span>
+  <span><math><mtext>&#x1D5ED;</mtext></math>=<span>1D5ED</span></span>
+  <span><math><mtext>&#x1D5EE;</mtext></math>=<span>1D5EE</span></span>
+  <span><math><mtext>&#x1D5EF;</mtext></math>=<span>1D5EF</span></span><br/>
+  <span><math><mtext>&#x1D5F0;</mtext></math>=<span>1D5F0</span></span>
+  <span><math><mtext>&#x1D5F1;</mtext></math>=<span>1D5F1</span></span>
+  <span><math><mtext>&#x1D5F2;</mtext></math>=<span>1D5F2</span></span>
+  <span><math><mtext>&#x1D5F3;</mtext></math>=<span>1D5F3</span></span>
+  <span><math><mtext>&#x1D5F4;</mtext></math>=<span>1D5F4</span></span>
+  <span><math><mtext>&#x1D5F5;</mtext></math>=<span>1D5F5</span></span>
+  <span><math><mtext>&#x1D5F6;</mtext></math>=<span>1D5F6</span></span>
+  <span><math><mtext>&#x1D5F7;</mtext></math>=<span>1D5F7</span></span>
+  <span><math><mtext>&#x1D5F8;</mtext></math>=<span>1D5F8</span></span>
+  <span><math><mtext>&#x1D5F9;</mtext></math>=<span>1D5F9</span></span><br/>
+  <span><math><mtext>&#x1D5FA;</mtext></math>=<span>1D5FA</span></span>
+  <span><math><mtext>&#x1D5FB;</mtext></math>=<span>1D5FB</span></span>
+  <span><math><mtext>&#x1D5FC;</mtext></math>=<span>1D5FC</span></span>
+  <span><math><mtext>&#x1D5FD;</mtext></math>=<span>1D5FD</span></span>
+  <span><math><mtext>&#x1D5FE;</mtext></math>=<span>1D5FE</span></span>
+  <span><math><mtext>&#x1D5FF;</mtext></math>=<span>1D5FF</span></span>
+  <span><math><mtext>&#x1D600;</mtext></math>=<span>1D600</span></span>
+  <span><math><mtext>&#x1D601;</mtext></math>=<span>1D601</span></span>
+  <span><math><mtext>&#x1D602;</mtext></math>=<span>1D602</span></span>
+  <span><math><mtext>&#x1D603;</mtext></math>=<span>1D603</span></span><br/>
+  <span><math><mtext>&#x1D604;</mtext></math>=<span>1D604</span></span>
+  <span><math><mtext>&#x1D605;</mtext></math>=<span>1D605</span></span>
+  <span><math><mtext>&#x1D606;</mtext></math>=<span>1D606</span></span>
+  <span><math><mtext>&#x1D607;</mtext></math>=<span>1D607</span></span>
+  <span><math><mtext>&#x1D756;</mtext></math>=<span>1D756</span></span>
+  <span><math><mtext>&#x1D757;</mtext></math>=<span>1D757</span></span>
+  <span><math><mtext>&#x1D758;</mtext></math>=<span>1D758</span></span>
+  <span><math><mtext>&#x1D759;</mtext></math>=<span>1D759</span></span>
+  <span><math><mtext>&#x1D75A;</mtext></math>=<span>1D75A</span></span>
+  <span><math><mtext>&#x1D75B;</mtext></math>=<span>1D75B</span></span><br/>
+  <span><math><mtext>&#x1D75C;</mtext></math>=<span>1D75C</span></span>
+  <span><math><mtext>&#x1D75D;</mtext></math>=<span>1D75D</span></span>
+  <span><math><mtext>&#x1D75E;</mtext></math>=<span>1D75E</span></span>
+  <span><math><mtext>&#x1D75F;</mtext></math>=<span>1D75F</span></span>
+  <span><math><mtext>&#x1D760;</mtext></math>=<span>1D760</span></span>
+  <span><math><mtext>&#x1D761;</mtext></math>=<span>1D761</span></span>
+  <span><math><mtext>&#x1D762;</mtext></math>=<span>1D762</span></span>
+  <span><math><mtext>&#x1D763;</mtext></math>=<span>1D763</span></span>
+  <span><math><mtext>&#x1D764;</mtext></math>=<span>1D764</span></span>
+  <span><math><mtext>&#x1D765;</mtext></math>=<span>1D765</span></span><br/>
+  <span><math><mtext>&#x1D766;</mtext></math>=<span>1D766</span></span>
+  <span><math><mtext>&#x1D768;</mtext></math>=<span>1D768</span></span>
+  <span><math><mtext>&#x1D769;</mtext></math>=<span>1D769</span></span>
+  <span><math><mtext>&#x1D76A;</mtext></math>=<span>1D76A</span></span>
+  <span><math><mtext>&#x1D76B;</mtext></math>=<span>1D76B</span></span>
+  <span><math><mtext>&#x1D76C;</mtext></math>=<span>1D76C</span></span>
+  <span><math><mtext>&#x1D76D;</mtext></math>=<span>1D76D</span></span>
+  <span><math><mtext>&#x1D76E;</mtext></math>=<span>1D76E</span></span>
+  <span><math><mtext>&#x1D770;</mtext></math>=<span>1D770</span></span>
+  <span><math><mtext>&#x1D771;</mtext></math>=<span>1D771</span></span><br/>
+  <span><math><mtext>&#x1D772;</mtext></math>=<span>1D772</span></span>
+  <span><math><mtext>&#x1D773;</mtext></math>=<span>1D773</span></span>
+  <span><math><mtext>&#x1D774;</mtext></math>=<span>1D774</span></span>
+  <span><math><mtext>&#x1D775;</mtext></math>=<span>1D775</span></span>
+  <span><math><mtext>&#x1D776;</mtext></math>=<span>1D776</span></span>
+  <span><math><mtext>&#x1D777;</mtext></math>=<span>1D777</span></span>
+  <span><math><mtext>&#x1D778;</mtext></math>=<span>1D778</span></span>
+  <span><math><mtext>&#x1D779;</mtext></math>=<span>1D779</span></span>
+  <span><math><mtext>&#x1D77A;</mtext></math>=<span>1D77A</span></span>
+  <span><math><mtext>&#x1D77B;</mtext></math>=<span>1D77B</span></span><br/>
+  <span><math><mtext>&#x1D77C;</mtext></math>=<span>1D77C</span></span>
+  <span><math><mtext>&#x1D77D;</mtext></math>=<span>1D77D</span></span>
+  <span><math><mtext>&#x1D77E;</mtext></math>=<span>1D77E</span></span>
+  <span><math><mtext>&#x1D77F;</mtext></math>=<span>1D77F</span></span>
+  <span><math><mtext>&#x1D780;</mtext></math>=<span>1D780</span></span>
+  <span><math><mtext>&#x1D781;</mtext></math>=<span>1D781</span></span>
+  <span><math><mtext>&#x1D782;</mtext></math>=<span>1D782</span></span>
+  <span><math><mtext>&#x1D783;</mtext></math>=<span>1D783</span></span>
+  <span><math><mtext>&#x1D784;</mtext></math>=<span>1D784</span></span>
+  <span><math><mtext>&#x1D785;</mtext></math>=<span>1D785</span></span><br/>
+  <span><math><mtext>&#x1D786;</mtext></math>=<span>1D786</span></span>
+  <span><math><mtext>&#x1D787;</mtext></math>=<span>1D787</span></span>
+  <span><math><mtext>&#x1D788;</mtext></math>=<span>1D788</span></span>
+  <span><math><mtext>&#x1D78B;</mtext></math>=<span>1D78B</span></span>
+  <span><math><mtext>&#x1D78D;</mtext></math>=<span>1D78D</span></span>
+  <span><math><mtext>&#x1D78F;</mtext></math>=<span>1D78F</span></span>
+  <span><math><mtext>&#x1D78C;</mtext></math>=<span>1D78C</span></span>
+  <span><math><mtext>&#x1D78E;</mtext></math>=<span>1D78E</span></span>
+  <span><math><mtext>&#x1D767;</mtext></math>=<span>1D767</span></span>
+  <span><math><mtext>&#x1D78A;</mtext></math>=<span>1D78A</span></span><br/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif.html
new file mode 100644
index 0000000..1cab237
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-sans-serif.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold-sans-serif</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-bold-sans-serif-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a bold-sans-serif mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold-sans-serif.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x2202;</mtext></math>=<span>1D789</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x2207;</mtext></math>=<span>1D76F</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x30;</mtext></math>=<span>1D7EC</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x31;</mtext></math>=<span>1D7ED</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x32;</mtext></math>=<span>1D7EE</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x33;</mtext></math>=<span>1D7EF</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x34;</mtext></math>=<span>1D7F0</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x35;</mtext></math>=<span>1D7F1</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x36;</mtext></math>=<span>1D7F2</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x37;</mtext></math>=<span>1D7F3</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x38;</mtext></math>=<span>1D7F4</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x39;</mtext></math>=<span>1D7F5</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x41;</mtext></math>=<span>1D5D4</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x42;</mtext></math>=<span>1D5D5</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x43;</mtext></math>=<span>1D5D6</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x44;</mtext></math>=<span>1D5D7</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x45;</mtext></math>=<span>1D5D8</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x46;</mtext></math>=<span>1D5D9</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x47;</mtext></math>=<span>1D5DA</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x48;</mtext></math>=<span>1D5DB</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x49;</mtext></math>=<span>1D5DC</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x4A;</mtext></math>=<span>1D5DD</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x4B;</mtext></math>=<span>1D5DE</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x4C;</mtext></math>=<span>1D5DF</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x4D;</mtext></math>=<span>1D5E0</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x4E;</mtext></math>=<span>1D5E1</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x4F;</mtext></math>=<span>1D5E2</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x50;</mtext></math>=<span>1D5E3</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x51;</mtext></math>=<span>1D5E4</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x52;</mtext></math>=<span>1D5E5</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x53;</mtext></math>=<span>1D5E6</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x54;</mtext></math>=<span>1D5E7</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x55;</mtext></math>=<span>1D5E8</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x56;</mtext></math>=<span>1D5E9</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x57;</mtext></math>=<span>1D5EA</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x58;</mtext></math>=<span>1D5EB</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x59;</mtext></math>=<span>1D5EC</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x5A;</mtext></math>=<span>1D5ED</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x61;</mtext></math>=<span>1D5EE</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x62;</mtext></math>=<span>1D5EF</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x63;</mtext></math>=<span>1D5F0</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x64;</mtext></math>=<span>1D5F1</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x65;</mtext></math>=<span>1D5F2</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x66;</mtext></math>=<span>1D5F3</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x67;</mtext></math>=<span>1D5F4</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x68;</mtext></math>=<span>1D5F5</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x69;</mtext></math>=<span>1D5F6</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x6A;</mtext></math>=<span>1D5F7</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x6B;</mtext></math>=<span>1D5F8</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x6C;</mtext></math>=<span>1D5F9</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x6D;</mtext></math>=<span>1D5FA</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x6E;</mtext></math>=<span>1D5FB</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x6F;</mtext></math>=<span>1D5FC</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x70;</mtext></math>=<span>1D5FD</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x71;</mtext></math>=<span>1D5FE</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x72;</mtext></math>=<span>1D5FF</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x73;</mtext></math>=<span>1D600</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x74;</mtext></math>=<span>1D601</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x75;</mtext></math>=<span>1D602</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x76;</mtext></math>=<span>1D603</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x77;</mtext></math>=<span>1D604</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x78;</mtext></math>=<span>1D605</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x79;</mtext></math>=<span>1D606</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x7A;</mtext></math>=<span>1D607</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x391;</mtext></math>=<span>1D756</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x392;</mtext></math>=<span>1D757</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x393;</mtext></math>=<span>1D758</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x394;</mtext></math>=<span>1D759</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x395;</mtext></math>=<span>1D75A</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x396;</mtext></math>=<span>1D75B</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x397;</mtext></math>=<span>1D75C</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x398;</mtext></math>=<span>1D75D</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x399;</mtext></math>=<span>1D75E</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x39A;</mtext></math>=<span>1D75F</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x39B;</mtext></math>=<span>1D760</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x39C;</mtext></math>=<span>1D761</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x39D;</mtext></math>=<span>1D762</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x39E;</mtext></math>=<span>1D763</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x39F;</mtext></math>=<span>1D764</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3A0;</mtext></math>=<span>1D765</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3A1;</mtext></math>=<span>1D766</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3A3;</mtext></math>=<span>1D768</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3A4;</mtext></math>=<span>1D769</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3A5;</mtext></math>=<span>1D76A</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3A6;</mtext></math>=<span>1D76B</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3A7;</mtext></math>=<span>1D76C</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3A8;</mtext></math>=<span>1D76D</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3A9;</mtext></math>=<span>1D76E</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3B1;</mtext></math>=<span>1D770</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3B2;</mtext></math>=<span>1D771</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3B3;</mtext></math>=<span>1D772</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3B4;</mtext></math>=<span>1D773</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3B5;</mtext></math>=<span>1D774</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3B6;</mtext></math>=<span>1D775</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3B7;</mtext></math>=<span>1D776</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3B8;</mtext></math>=<span>1D777</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3B9;</mtext></math>=<span>1D778</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3BA;</mtext></math>=<span>1D779</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3BB;</mtext></math>=<span>1D77A</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3BC;</mtext></math>=<span>1D77B</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3BD;</mtext></math>=<span>1D77C</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3BE;</mtext></math>=<span>1D77D</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3BF;</mtext></math>=<span>1D77E</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C0;</mtext></math>=<span>1D77F</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C1;</mtext></math>=<span>1D780</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C2;</mtext></math>=<span>1D781</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C3;</mtext></math>=<span>1D782</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C4;</mtext></math>=<span>1D783</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C5;</mtext></math>=<span>1D784</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C6;</mtext></math>=<span>1D785</span></span><br/>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C7;</mtext></math>=<span>1D786</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C8;</mtext></math>=<span>1D787</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3C9;</mtext></math>=<span>1D788</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3D1;</mtext></math>=<span>1D78B</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3D5;</mtext></math>=<span>1D78D</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3D6;</mtext></math>=<span>1D78F</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3F0;</mtext></math>=<span>1D78C</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3F1;</mtext></math>=<span>1D78E</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3F4;</mtext></math>=<span>1D767</span></span>
+  <span><math><mtext mathvariant="bold-sans-serif">&#x3F5;</mtext></math>=<span>1D78A</span></span><br/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script-ref.html
new file mode 100644
index 0000000..4203861
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script-ref.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold-script (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold-script.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D4D0;</mtext></math>=<span>1D4D0</span></span>
+  <span><math><mtext>&#x1D4D1;</mtext></math>=<span>1D4D1</span></span>
+  <span><math><mtext>&#x1D4D2;</mtext></math>=<span>1D4D2</span></span>
+  <span><math><mtext>&#x1D4D3;</mtext></math>=<span>1D4D3</span></span>
+  <span><math><mtext>&#x1D4D4;</mtext></math>=<span>1D4D4</span></span>
+  <span><math><mtext>&#x1D4D5;</mtext></math>=<span>1D4D5</span></span>
+  <span><math><mtext>&#x1D4D6;</mtext></math>=<span>1D4D6</span></span>
+  <span><math><mtext>&#x1D4D7;</mtext></math>=<span>1D4D7</span></span>
+  <span><math><mtext>&#x1D4D8;</mtext></math>=<span>1D4D8</span></span>
+  <span><math><mtext>&#x1D4D9;</mtext></math>=<span>1D4D9</span></span><br/>
+  <span><math><mtext>&#x1D4DA;</mtext></math>=<span>1D4DA</span></span>
+  <span><math><mtext>&#x1D4DB;</mtext></math>=<span>1D4DB</span></span>
+  <span><math><mtext>&#x1D4DC;</mtext></math>=<span>1D4DC</span></span>
+  <span><math><mtext>&#x1D4DD;</mtext></math>=<span>1D4DD</span></span>
+  <span><math><mtext>&#x1D4DE;</mtext></math>=<span>1D4DE</span></span>
+  <span><math><mtext>&#x1D4DF;</mtext></math>=<span>1D4DF</span></span>
+  <span><math><mtext>&#x1D4E0;</mtext></math>=<span>1D4E0</span></span>
+  <span><math><mtext>&#x1D4E1;</mtext></math>=<span>1D4E1</span></span>
+  <span><math><mtext>&#x1D4E2;</mtext></math>=<span>1D4E2</span></span>
+  <span><math><mtext>&#x1D4E3;</mtext></math>=<span>1D4E3</span></span><br/>
+  <span><math><mtext>&#x1D4E4;</mtext></math>=<span>1D4E4</span></span>
+  <span><math><mtext>&#x1D4E5;</mtext></math>=<span>1D4E5</span></span>
+  <span><math><mtext>&#x1D4E6;</mtext></math>=<span>1D4E6</span></span>
+  <span><math><mtext>&#x1D4E7;</mtext></math>=<span>1D4E7</span></span>
+  <span><math><mtext>&#x1D4E8;</mtext></math>=<span>1D4E8</span></span>
+  <span><math><mtext>&#x1D4E9;</mtext></math>=<span>1D4E9</span></span>
+  <span><math><mtext>&#x1D4EA;</mtext></math>=<span>1D4EA</span></span>
+  <span><math><mtext>&#x1D4EB;</mtext></math>=<span>1D4EB</span></span>
+  <span><math><mtext>&#x1D4EC;</mtext></math>=<span>1D4EC</span></span>
+  <span><math><mtext>&#x1D4ED;</mtext></math>=<span>1D4ED</span></span><br/>
+  <span><math><mtext>&#x1D4EE;</mtext></math>=<span>1D4EE</span></span>
+  <span><math><mtext>&#x1D4EF;</mtext></math>=<span>1D4EF</span></span>
+  <span><math><mtext>&#x1D4F0;</mtext></math>=<span>1D4F0</span></span>
+  <span><math><mtext>&#x1D4F1;</mtext></math>=<span>1D4F1</span></span>
+  <span><math><mtext>&#x1D4F2;</mtext></math>=<span>1D4F2</span></span>
+  <span><math><mtext>&#x1D4F3;</mtext></math>=<span>1D4F3</span></span>
+  <span><math><mtext>&#x1D4F4;</mtext></math>=<span>1D4F4</span></span>
+  <span><math><mtext>&#x1D4F5;</mtext></math>=<span>1D4F5</span></span>
+  <span><math><mtext>&#x1D4F6;</mtext></math>=<span>1D4F6</span></span>
+  <span><math><mtext>&#x1D4F7;</mtext></math>=<span>1D4F7</span></span><br/>
+  <span><math><mtext>&#x1D4F8;</mtext></math>=<span>1D4F8</span></span>
+  <span><math><mtext>&#x1D4F9;</mtext></math>=<span>1D4F9</span></span>
+  <span><math><mtext>&#x1D4FA;</mtext></math>=<span>1D4FA</span></span>
+  <span><math><mtext>&#x1D4FB;</mtext></math>=<span>1D4FB</span></span>
+  <span><math><mtext>&#x1D4FC;</mtext></math>=<span>1D4FC</span></span>
+  <span><math><mtext>&#x1D4FD;</mtext></math>=<span>1D4FD</span></span>
+  <span><math><mtext>&#x1D4FE;</mtext></math>=<span>1D4FE</span></span>
+  <span><math><mtext>&#x1D4FF;</mtext></math>=<span>1D4FF</span></span>
+  <span><math><mtext>&#x1D500;</mtext></math>=<span>1D500</span></span>
+  <span><math><mtext>&#x1D501;</mtext></math>=<span>1D501</span></span><br/>
+  <span><math><mtext>&#x1D502;</mtext></math>=<span>1D502</span></span>
+  <span><math><mtext>&#x1D503;</mtext></math>=<span>1D503</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script.html
new file mode 100644
index 0000000..9083afa8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold-script.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold-script</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-bold-script-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a bold-script mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold-script.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="bold-script">&#x41;</mtext></math>=<span>1D4D0</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x42;</mtext></math>=<span>1D4D1</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x43;</mtext></math>=<span>1D4D2</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x44;</mtext></math>=<span>1D4D3</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x45;</mtext></math>=<span>1D4D4</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x46;</mtext></math>=<span>1D4D5</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x47;</mtext></math>=<span>1D4D6</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x48;</mtext></math>=<span>1D4D7</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x49;</mtext></math>=<span>1D4D8</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x4A;</mtext></math>=<span>1D4D9</span></span><br/>
+  <span><math><mtext mathvariant="bold-script">&#x4B;</mtext></math>=<span>1D4DA</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x4C;</mtext></math>=<span>1D4DB</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x4D;</mtext></math>=<span>1D4DC</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x4E;</mtext></math>=<span>1D4DD</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x4F;</mtext></math>=<span>1D4DE</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x50;</mtext></math>=<span>1D4DF</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x51;</mtext></math>=<span>1D4E0</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x52;</mtext></math>=<span>1D4E1</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x53;</mtext></math>=<span>1D4E2</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x54;</mtext></math>=<span>1D4E3</span></span><br/>
+  <span><math><mtext mathvariant="bold-script">&#x55;</mtext></math>=<span>1D4E4</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x56;</mtext></math>=<span>1D4E5</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x57;</mtext></math>=<span>1D4E6</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x58;</mtext></math>=<span>1D4E7</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x59;</mtext></math>=<span>1D4E8</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x5A;</mtext></math>=<span>1D4E9</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x61;</mtext></math>=<span>1D4EA</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x62;</mtext></math>=<span>1D4EB</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x63;</mtext></math>=<span>1D4EC</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x64;</mtext></math>=<span>1D4ED</span></span><br/>
+  <span><math><mtext mathvariant="bold-script">&#x65;</mtext></math>=<span>1D4EE</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x66;</mtext></math>=<span>1D4EF</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x67;</mtext></math>=<span>1D4F0</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x68;</mtext></math>=<span>1D4F1</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x69;</mtext></math>=<span>1D4F2</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x6A;</mtext></math>=<span>1D4F3</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x6B;</mtext></math>=<span>1D4F4</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x6C;</mtext></math>=<span>1D4F5</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x6D;</mtext></math>=<span>1D4F6</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x6E;</mtext></math>=<span>1D4F7</span></span><br/>
+  <span><math><mtext mathvariant="bold-script">&#x6F;</mtext></math>=<span>1D4F8</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x70;</mtext></math>=<span>1D4F9</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x71;</mtext></math>=<span>1D4FA</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x72;</mtext></math>=<span>1D4FB</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x73;</mtext></math>=<span>1D4FC</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x74;</mtext></math>=<span>1D4FD</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x75;</mtext></math>=<span>1D4FE</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x76;</mtext></math>=<span>1D4FF</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x77;</mtext></math>=<span>1D500</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x78;</mtext></math>=<span>1D501</span></span><br/>
+  <span><math><mtext mathvariant="bold-script">&#x79;</mtext></math>=<span>1D502</span></span>
+  <span><math><mtext mathvariant="bold-script">&#x7A;</mtext></math>=<span>1D503</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold.html
new file mode 100644
index 0000000..b3fe917
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-bold.html
@@ -0,0 +1,152 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant bold</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-bold-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a bold mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-bold.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="bold">&#x2202;</mtext></math>=<span>1D6DB</span></span>
+  <span><math><mtext mathvariant="bold">&#x2207;</mtext></math>=<span>1D6C1</span></span>
+  <span><math><mtext mathvariant="bold">&#x30;</mtext></math>=<span>1D7CE</span></span>
+  <span><math><mtext mathvariant="bold">&#x31;</mtext></math>=<span>1D7CF</span></span>
+  <span><math><mtext mathvariant="bold">&#x32;</mtext></math>=<span>1D7D0</span></span>
+  <span><math><mtext mathvariant="bold">&#x33;</mtext></math>=<span>1D7D1</span></span>
+  <span><math><mtext mathvariant="bold">&#x34;</mtext></math>=<span>1D7D2</span></span>
+  <span><math><mtext mathvariant="bold">&#x35;</mtext></math>=<span>1D7D3</span></span>
+  <span><math><mtext mathvariant="bold">&#x36;</mtext></math>=<span>1D7D4</span></span>
+  <span><math><mtext mathvariant="bold">&#x37;</mtext></math>=<span>1D7D5</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x38;</mtext></math>=<span>1D7D6</span></span>
+  <span><math><mtext mathvariant="bold">&#x39;</mtext></math>=<span>1D7D7</span></span>
+  <span><math><mtext mathvariant="bold">&#x41;</mtext></math>=<span>1D400</span></span>
+  <span><math><mtext mathvariant="bold">&#x42;</mtext></math>=<span>1D401</span></span>
+  <span><math><mtext mathvariant="bold">&#x43;</mtext></math>=<span>1D402</span></span>
+  <span><math><mtext mathvariant="bold">&#x44;</mtext></math>=<span>1D403</span></span>
+  <span><math><mtext mathvariant="bold">&#x45;</mtext></math>=<span>1D404</span></span>
+  <span><math><mtext mathvariant="bold">&#x46;</mtext></math>=<span>1D405</span></span>
+  <span><math><mtext mathvariant="bold">&#x47;</mtext></math>=<span>1D406</span></span>
+  <span><math><mtext mathvariant="bold">&#x48;</mtext></math>=<span>1D407</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x49;</mtext></math>=<span>1D408</span></span>
+  <span><math><mtext mathvariant="bold">&#x4A;</mtext></math>=<span>1D409</span></span>
+  <span><math><mtext mathvariant="bold">&#x4B;</mtext></math>=<span>1D40A</span></span>
+  <span><math><mtext mathvariant="bold">&#x4C;</mtext></math>=<span>1D40B</span></span>
+  <span><math><mtext mathvariant="bold">&#x4D;</mtext></math>=<span>1D40C</span></span>
+  <span><math><mtext mathvariant="bold">&#x4E;</mtext></math>=<span>1D40D</span></span>
+  <span><math><mtext mathvariant="bold">&#x4F;</mtext></math>=<span>1D40E</span></span>
+  <span><math><mtext mathvariant="bold">&#x50;</mtext></math>=<span>1D40F</span></span>
+  <span><math><mtext mathvariant="bold">&#x51;</mtext></math>=<span>1D410</span></span>
+  <span><math><mtext mathvariant="bold">&#x52;</mtext></math>=<span>1D411</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x53;</mtext></math>=<span>1D412</span></span>
+  <span><math><mtext mathvariant="bold">&#x54;</mtext></math>=<span>1D413</span></span>
+  <span><math><mtext mathvariant="bold">&#x55;</mtext></math>=<span>1D414</span></span>
+  <span><math><mtext mathvariant="bold">&#x56;</mtext></math>=<span>1D415</span></span>
+  <span><math><mtext mathvariant="bold">&#x57;</mtext></math>=<span>1D416</span></span>
+  <span><math><mtext mathvariant="bold">&#x58;</mtext></math>=<span>1D417</span></span>
+  <span><math><mtext mathvariant="bold">&#x59;</mtext></math>=<span>1D418</span></span>
+  <span><math><mtext mathvariant="bold">&#x5A;</mtext></math>=<span>1D419</span></span>
+  <span><math><mtext mathvariant="bold">&#x61;</mtext></math>=<span>1D41A</span></span>
+  <span><math><mtext mathvariant="bold">&#x62;</mtext></math>=<span>1D41B</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x63;</mtext></math>=<span>1D41C</span></span>
+  <span><math><mtext mathvariant="bold">&#x64;</mtext></math>=<span>1D41D</span></span>
+  <span><math><mtext mathvariant="bold">&#x65;</mtext></math>=<span>1D41E</span></span>
+  <span><math><mtext mathvariant="bold">&#x66;</mtext></math>=<span>1D41F</span></span>
+  <span><math><mtext mathvariant="bold">&#x67;</mtext></math>=<span>1D420</span></span>
+  <span><math><mtext mathvariant="bold">&#x68;</mtext></math>=<span>1D421</span></span>
+  <span><math><mtext mathvariant="bold">&#x69;</mtext></math>=<span>1D422</span></span>
+  <span><math><mtext mathvariant="bold">&#x6A;</mtext></math>=<span>1D423</span></span>
+  <span><math><mtext mathvariant="bold">&#x6B;</mtext></math>=<span>1D424</span></span>
+  <span><math><mtext mathvariant="bold">&#x6C;</mtext></math>=<span>1D425</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x6D;</mtext></math>=<span>1D426</span></span>
+  <span><math><mtext mathvariant="bold">&#x6E;</mtext></math>=<span>1D427</span></span>
+  <span><math><mtext mathvariant="bold">&#x6F;</mtext></math>=<span>1D428</span></span>
+  <span><math><mtext mathvariant="bold">&#x70;</mtext></math>=<span>1D429</span></span>
+  <span><math><mtext mathvariant="bold">&#x71;</mtext></math>=<span>1D42A</span></span>
+  <span><math><mtext mathvariant="bold">&#x72;</mtext></math>=<span>1D42B</span></span>
+  <span><math><mtext mathvariant="bold">&#x73;</mtext></math>=<span>1D42C</span></span>
+  <span><math><mtext mathvariant="bold">&#x74;</mtext></math>=<span>1D42D</span></span>
+  <span><math><mtext mathvariant="bold">&#x75;</mtext></math>=<span>1D42E</span></span>
+  <span><math><mtext mathvariant="bold">&#x76;</mtext></math>=<span>1D42F</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x77;</mtext></math>=<span>1D430</span></span>
+  <span><math><mtext mathvariant="bold">&#x78;</mtext></math>=<span>1D431</span></span>
+  <span><math><mtext mathvariant="bold">&#x79;</mtext></math>=<span>1D432</span></span>
+  <span><math><mtext mathvariant="bold">&#x7A;</mtext></math>=<span>1D433</span></span>
+  <span><math><mtext mathvariant="bold">&#x391;</mtext></math>=<span>1D6A8</span></span>
+  <span><math><mtext mathvariant="bold">&#x392;</mtext></math>=<span>1D6A9</span></span>
+  <span><math><mtext mathvariant="bold">&#x393;</mtext></math>=<span>1D6AA</span></span>
+  <span><math><mtext mathvariant="bold">&#x394;</mtext></math>=<span>1D6AB</span></span>
+  <span><math><mtext mathvariant="bold">&#x395;</mtext></math>=<span>1D6AC</span></span>
+  <span><math><mtext mathvariant="bold">&#x396;</mtext></math>=<span>1D6AD</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x397;</mtext></math>=<span>1D6AE</span></span>
+  <span><math><mtext mathvariant="bold">&#x398;</mtext></math>=<span>1D6AF</span></span>
+  <span><math><mtext mathvariant="bold">&#x399;</mtext></math>=<span>1D6B0</span></span>
+  <span><math><mtext mathvariant="bold">&#x39A;</mtext></math>=<span>1D6B1</span></span>
+  <span><math><mtext mathvariant="bold">&#x39B;</mtext></math>=<span>1D6B2</span></span>
+  <span><math><mtext mathvariant="bold">&#x39C;</mtext></math>=<span>1D6B3</span></span>
+  <span><math><mtext mathvariant="bold">&#x39D;</mtext></math>=<span>1D6B4</span></span>
+  <span><math><mtext mathvariant="bold">&#x39E;</mtext></math>=<span>1D6B5</span></span>
+  <span><math><mtext mathvariant="bold">&#x39F;</mtext></math>=<span>1D6B6</span></span>
+  <span><math><mtext mathvariant="bold">&#x3A0;</mtext></math>=<span>1D6B7</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x3A1;</mtext></math>=<span>1D6B8</span></span>
+  <span><math><mtext mathvariant="bold">&#x3A3;</mtext></math>=<span>1D6BA</span></span>
+  <span><math><mtext mathvariant="bold">&#x3A4;</mtext></math>=<span>1D6BB</span></span>
+  <span><math><mtext mathvariant="bold">&#x3A5;</mtext></math>=<span>1D6BC</span></span>
+  <span><math><mtext mathvariant="bold">&#x3A6;</mtext></math>=<span>1D6BD</span></span>
+  <span><math><mtext mathvariant="bold">&#x3A7;</mtext></math>=<span>1D6BE</span></span>
+  <span><math><mtext mathvariant="bold">&#x3A8;</mtext></math>=<span>1D6BF</span></span>
+  <span><math><mtext mathvariant="bold">&#x3A9;</mtext></math>=<span>1D6C0</span></span>
+  <span><math><mtext mathvariant="bold">&#x3B1;</mtext></math>=<span>1D6C2</span></span>
+  <span><math><mtext mathvariant="bold">&#x3B2;</mtext></math>=<span>1D6C3</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x3B3;</mtext></math>=<span>1D6C4</span></span>
+  <span><math><mtext mathvariant="bold">&#x3B4;</mtext></math>=<span>1D6C5</span></span>
+  <span><math><mtext mathvariant="bold">&#x3B5;</mtext></math>=<span>1D6C6</span></span>
+  <span><math><mtext mathvariant="bold">&#x3B6;</mtext></math>=<span>1D6C7</span></span>
+  <span><math><mtext mathvariant="bold">&#x3B7;</mtext></math>=<span>1D6C8</span></span>
+  <span><math><mtext mathvariant="bold">&#x3B8;</mtext></math>=<span>1D6C9</span></span>
+  <span><math><mtext mathvariant="bold">&#x3B9;</mtext></math>=<span>1D6CA</span></span>
+  <span><math><mtext mathvariant="bold">&#x3BA;</mtext></math>=<span>1D6CB</span></span>
+  <span><math><mtext mathvariant="bold">&#x3BB;</mtext></math>=<span>1D6CC</span></span>
+  <span><math><mtext mathvariant="bold">&#x3BC;</mtext></math>=<span>1D6CD</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x3BD;</mtext></math>=<span>1D6CE</span></span>
+  <span><math><mtext mathvariant="bold">&#x3BE;</mtext></math>=<span>1D6CF</span></span>
+  <span><math><mtext mathvariant="bold">&#x3BF;</mtext></math>=<span>1D6D0</span></span>
+  <span><math><mtext mathvariant="bold">&#x3C0;</mtext></math>=<span>1D6D1</span></span>
+  <span><math><mtext mathvariant="bold">&#x3C1;</mtext></math>=<span>1D6D2</span></span>
+  <span><math><mtext mathvariant="bold">&#x3C2;</mtext></math>=<span>1D6D3</span></span>
+  <span><math><mtext mathvariant="bold">&#x3C3;</mtext></math>=<span>1D6D4</span></span>
+  <span><math><mtext mathvariant="bold">&#x3C4;</mtext></math>=<span>1D6D5</span></span>
+  <span><math><mtext mathvariant="bold">&#x3C5;</mtext></math>=<span>1D6D6</span></span>
+  <span><math><mtext mathvariant="bold">&#x3C6;</mtext></math>=<span>1D6D7</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x3C7;</mtext></math>=<span>1D6D8</span></span>
+  <span><math><mtext mathvariant="bold">&#x3C8;</mtext></math>=<span>1D6D9</span></span>
+  <span><math><mtext mathvariant="bold">&#x3C9;</mtext></math>=<span>1D6DA</span></span>
+  <span><math><mtext mathvariant="bold">&#x3D1;</mtext></math>=<span>1D6DD</span></span>
+  <span><math><mtext mathvariant="bold">&#x3D5;</mtext></math>=<span>1D6DF</span></span>
+  <span><math><mtext mathvariant="bold">&#x3D6;</mtext></math>=<span>1D6E1</span></span>
+  <span><math><mtext mathvariant="bold">&#x3DC;</mtext></math>=<span>1D7CA</span></span>
+  <span><math><mtext mathvariant="bold">&#x3DD;</mtext></math>=<span>1D7CB</span></span>
+  <span><math><mtext mathvariant="bold">&#x3F0;</mtext></math>=<span>1D6DE</span></span>
+  <span><math><mtext mathvariant="bold">&#x3F1;</mtext></math>=<span>1D6E0</span></span><br/>
+  <span><math><mtext mathvariant="bold">&#x3F4;</mtext></math>=<span>1D6B9</span></span>
+  <span><math><mtext mathvariant="bold">&#x3F5;</mtext></math>=<span>1D6DC</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck-ref.html
new file mode 100644
index 0000000..56db2591
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck-ref.html
@@ -0,0 +1,114 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant double-struck (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-double-struck.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1EEA1;</mtext></math>=<span>1EEA1</span></span>
+  <span><math><mtext>&#x1EEB5;</mtext></math>=<span>1EEB5</span></span>
+  <span><math><mtext>&#x1EEB6;</mtext></math>=<span>1EEB6</span></span>
+  <span><math><mtext>&#x1EEA2;</mtext></math>=<span>1EEA2</span></span>
+  <span><math><mtext>&#x1EEA7;</mtext></math>=<span>1EEA7</span></span>
+  <span><math><mtext>&#x1EEB7;</mtext></math>=<span>1EEB7</span></span>
+  <span><math><mtext>&#x1EEA3;</mtext></math>=<span>1EEA3</span></span>
+  <span><math><mtext>&#x1EEB8;</mtext></math>=<span>1EEB8</span></span>
+  <span><math><mtext>&#x1EEB3;</mtext></math>=<span>1EEB3</span></span>
+  <span><math><mtext>&#x1EEA6;</mtext></math>=<span>1EEA6</span></span><br/>
+  <span><math><mtext>&#x1D7DB;</mtext></math>=<span>1D7DB</span></span>
+  <span><math><mtext>&#x1D7DC;</mtext></math>=<span>1D7DC</span></span>
+  <span><math><mtext>&#x1D7DD;</mtext></math>=<span>1D7DD</span></span>
+  <span><math><mtext>&#x1EEB9;</mtext></math>=<span>1EEB9</span></span>
+  <span><math><mtext>&#x1EEA8;</mtext></math>=<span>1EEA8</span></span>
+  <span><math><mtext>&#x1EEBA;</mtext></math>=<span>1EEBA</span></span>
+  <span><math><mtext>&#x1D7E1;</mtext></math>=<span>1D7E1</span></span>
+  <span><math><mtext>&#x1EEBB;</mtext></math>=<span>1EEBB</span></span>
+  <span><math><mtext>&#x1EEB0;</mtext></math>=<span>1EEB0</span></span>
+  <span><math><mtext>&#x1EEB2;</mtext></math>=<span>1EEB2</span></span><br/>
+  <span><math><mtext>&#x2102;</mtext></math>=<span>02102</span></span>
+  <span><math><mtext>&#x1EEAB;</mtext></math>=<span>1EEAB</span></span>
+  <span><math><mtext>&#x1EEAC;</mtext></math>=<span>1EEAC</span></span>
+  <span><math><mtext>&#x1EEAD;</mtext></math>=<span>1EEAD</span></span>
+  <span><math><mtext>&#x1D53E;</mtext></math>=<span>1D53E</span></span>
+  <span><math><mtext>&#x1EEA5;</mtext></math>=<span>1EEA5</span></span>
+  <span><math><mtext>&#x1D540;</mtext></math>=<span>1D540</span></span>
+  <span><math><mtext>&#x1EEA9;</mtext></math>=<span>1EEA9</span></span>
+  <span><math><mtext>&#x1D542;</mtext></math>=<span>1D542</span></span>
+  <span><math><mtext>&#x1D543;</mtext></math>=<span>1D543</span></span><br/>
+  <span><math><mtext>&#x1D544;</mtext></math>=<span>1D544</span></span>
+  <span><math><mtext>&#x2115;</mtext></math>=<span>02115</span></span>
+  <span><math><mtext>&#x1D546;</mtext></math>=<span>1D546</span></span>
+  <span><math><mtext>&#x2119;</mtext></math>=<span>02119</span></span>
+  <span><math><mtext>&#x211A;</mtext></math>=<span>0211A</span></span>
+  <span><math><mtext>&#x211D;</mtext></math>=<span>0211D</span></span>
+  <span><math><mtext>&#x1D54A;</mtext></math>=<span>1D54A</span></span>
+  <span><math><mtext>&#x1D54B;</mtext></math>=<span>1D54B</span></span>
+  <span><math><mtext>&#x1D54C;</mtext></math>=<span>1D54C</span></span>
+  <span><math><mtext>&#x1D54D;</mtext></math>=<span>1D54D</span></span><br/>
+  <span><math><mtext>&#x1D54E;</mtext></math>=<span>1D54E</span></span>
+  <span><math><mtext>&#x1D54F;</mtext></math>=<span>1D54F</span></span>
+  <span><math><mtext>&#x1D550;</mtext></math>=<span>1D550</span></span>
+  <span><math><mtext>&#x2124;</mtext></math>=<span>02124</span></span>
+  <span><math><mtext>&#x1D552;</mtext></math>=<span>1D552</span></span>
+  <span><math><mtext>&#x1D553;</mtext></math>=<span>1D553</span></span>
+  <span><math><mtext>&#x1D554;</mtext></math>=<span>1D554</span></span>
+  <span><math><mtext>&#x1D555;</mtext></math>=<span>1D555</span></span>
+  <span><math><mtext>&#x1D556;</mtext></math>=<span>1D556</span></span>
+  <span><math><mtext>&#x1D557;</mtext></math>=<span>1D557</span></span><br/>
+  <span><math><mtext>&#x1D558;</mtext></math>=<span>1D558</span></span>
+  <span><math><mtext>&#x1D559;</mtext></math>=<span>1D559</span></span>
+  <span><math><mtext>&#x1D55A;</mtext></math>=<span>1D55A</span></span>
+  <span><math><mtext>&#x1D55B;</mtext></math>=<span>1D55B</span></span>
+  <span><math><mtext>&#x1D55C;</mtext></math>=<span>1D55C</span></span>
+  <span><math><mtext>&#x1D55D;</mtext></math>=<span>1D55D</span></span>
+  <span><math><mtext>&#x1D55E;</mtext></math>=<span>1D55E</span></span>
+  <span><math><mtext>&#x1D55F;</mtext></math>=<span>1D55F</span></span>
+  <span><math><mtext>&#x1D560;</mtext></math>=<span>1D560</span></span>
+  <span><math><mtext>&#x1D561;</mtext></math>=<span>1D561</span></span><br/>
+  <span><math><mtext>&#x1D562;</mtext></math>=<span>1D562</span></span>
+  <span><math><mtext>&#x1D563;</mtext></math>=<span>1D563</span></span>
+  <span><math><mtext>&#x1D564;</mtext></math>=<span>1D564</span></span>
+  <span><math><mtext>&#x1D565;</mtext></math>=<span>1D565</span></span>
+  <span><math><mtext>&#x1D566;</mtext></math>=<span>1D566</span></span>
+  <span><math><mtext>&#x1D567;</mtext></math>=<span>1D567</span></span>
+  <span><math><mtext>&#x1D568;</mtext></math>=<span>1D568</span></span>
+  <span><math><mtext>&#x1D569;</mtext></math>=<span>1D569</span></span>
+  <span><math><mtext>&#x1D56A;</mtext></math>=<span>1D56A</span></span>
+  <span><math><mtext>&#x1D56B;</mtext></math>=<span>1D56B</span></span><br/>
+  <span><math><mtext>&#x1D7D8;</mtext></math>=<span>1D7D8</span></span>
+  <span><math><mtext>&#x1D7D9;</mtext></math>=<span>1D7D9</span></span>
+  <span><math><mtext>&#x1D7DA;</mtext></math>=<span>1D7DA</span></span>
+  <span><math><mtext>&#x1EEAE;</mtext></math>=<span>1EEAE</span></span>
+  <span><math><mtext>&#x1EEB4;</mtext></math>=<span>1EEB4</span></span>
+  <span><math><mtext>&#x1EEB1;</mtext></math>=<span>1EEB1</span></span>
+  <span><math><mtext>&#x1D7DE;</mtext></math>=<span>1D7DE</span></span>
+  <span><math><mtext>&#x1D7DF;</mtext></math>=<span>1D7DF</span></span>
+  <span><math><mtext>&#x1D7E0;</mtext></math>=<span>1D7E0</span></span>
+  <span><math><mtext>&#x1EEAF;</mtext></math>=<span>1EEAF</span></span><br/>
+  <span><math><mtext>&#x1D538;</mtext></math>=<span>1D538</span></span>
+  <span><math><mtext>&#x1D539;</mtext></math>=<span>1D539</span></span>
+  <span><math><mtext>&#x1D53B;</mtext></math>=<span>1D53B</span></span>
+  <span><math><mtext>&#x1D53C;</mtext></math>=<span>1D53C</span></span>
+  <span><math><mtext>&#x1D53D;</mtext></math>=<span>1D53D</span></span>
+  <span><math><mtext>&#x210D;</mtext></math>=<span>0210D</span></span>
+  <span><math><mtext>&#x1D541;</mtext></math>=<span>1D541</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck.html
new file mode 100644
index 0000000..e51908e1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-double-struck.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant double-struck</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-double-struck-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a double-struck mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-double-struck.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="double-struck">&#x628;</mtext></math>=<span>1EEA1</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x62A;</mtext></math>=<span>1EEB5</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x62B;</mtext></math>=<span>1EEB6</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x62C;</mtext></math>=<span>1EEA2</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x62D;</mtext></math>=<span>1EEA7</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x62E;</mtext></math>=<span>1EEB7</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x62F;</mtext></math>=<span>1EEA3</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x630;</mtext></math>=<span>1EEB8</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x631;</mtext></math>=<span>1EEB3</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x632;</mtext></math>=<span>1EEA6</span></span><br/>
+  <span><math><mtext mathvariant="double-struck">&#x33;</mtext></math>=<span>1D7DB</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x34;</mtext></math>=<span>1D7DC</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x35;</mtext></math>=<span>1D7DD</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x636;</mtext></math>=<span>1EEB9</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x637;</mtext></math>=<span>1EEA8</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x638;</mtext></math>=<span>1EEBA</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x39;</mtext></math>=<span>1D7E1</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x63A;</mtext></math>=<span>1EEBB</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x641;</mtext></math>=<span>1EEB0</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x642;</mtext></math>=<span>1EEB2</span></span><br/>
+  <span><math><mtext mathvariant="double-struck">&#x43;</mtext></math>=<span>02102</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x644;</mtext></math>=<span>1EEAB</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x645;</mtext></math>=<span>1EEAC</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x646;</mtext></math>=<span>1EEAD</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x47;</mtext></math>=<span>1D53E</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x648;</mtext></math>=<span>1EEA5</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x49;</mtext></math>=<span>1D540</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x64A;</mtext></math>=<span>1EEA9</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x4B;</mtext></math>=<span>1D542</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x4C;</mtext></math>=<span>1D543</span></span><br/>
+  <span><math><mtext mathvariant="double-struck">&#x4D;</mtext></math>=<span>1D544</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x4E;</mtext></math>=<span>02115</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x4F;</mtext></math>=<span>1D546</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x50;</mtext></math>=<span>02119</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x51;</mtext></math>=<span>0211A</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x52;</mtext></math>=<span>0211D</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x53;</mtext></math>=<span>1D54A</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x54;</mtext></math>=<span>1D54B</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x55;</mtext></math>=<span>1D54C</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x56;</mtext></math>=<span>1D54D</span></span><br/>
+  <span><math><mtext mathvariant="double-struck">&#x57;</mtext></math>=<span>1D54E</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x58;</mtext></math>=<span>1D54F</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x59;</mtext></math>=<span>1D550</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x5A;</mtext></math>=<span>02124</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x61;</mtext></math>=<span>1D552</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x62;</mtext></math>=<span>1D553</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x63;</mtext></math>=<span>1D554</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x64;</mtext></math>=<span>1D555</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x65;</mtext></math>=<span>1D556</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x66;</mtext></math>=<span>1D557</span></span><br/>
+  <span><math><mtext mathvariant="double-struck">&#x67;</mtext></math>=<span>1D558</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x68;</mtext></math>=<span>1D559</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x69;</mtext></math>=<span>1D55A</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x6A;</mtext></math>=<span>1D55B</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x6B;</mtext></math>=<span>1D55C</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x6C;</mtext></math>=<span>1D55D</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x6D;</mtext></math>=<span>1D55E</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x6E;</mtext></math>=<span>1D55F</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x6F;</mtext></math>=<span>1D560</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x70;</mtext></math>=<span>1D561</span></span><br/>
+  <span><math><mtext mathvariant="double-struck">&#x71;</mtext></math>=<span>1D562</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x72;</mtext></math>=<span>1D563</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x73;</mtext></math>=<span>1D564</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x74;</mtext></math>=<span>1D565</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x75;</mtext></math>=<span>1D566</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x76;</mtext></math>=<span>1D567</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x77;</mtext></math>=<span>1D568</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x78;</mtext></math>=<span>1D569</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x79;</mtext></math>=<span>1D56A</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x7A;</mtext></math>=<span>1D56B</span></span><br/>
+  <span><math><mtext mathvariant="double-struck">&#x30;</mtext></math>=<span>1D7D8</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x31;</mtext></math>=<span>1D7D9</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x32;</mtext></math>=<span>1D7DA</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x633;</mtext></math>=<span>1EEAE</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x634;</mtext></math>=<span>1EEB4</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x635;</mtext></math>=<span>1EEB1</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x36;</mtext></math>=<span>1D7DE</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x37;</mtext></math>=<span>1D7DF</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x38;</mtext></math>=<span>1D7E0</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x639;</mtext></math>=<span>1EEAF</span></span><br/>
+  <span><math><mtext mathvariant="double-struck">&#x41;</mtext></math>=<span>1D538</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x42;</mtext></math>=<span>1D539</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x44;</mtext></math>=<span>1D53B</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x45;</mtext></math>=<span>1D53C</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x46;</mtext></math>=<span>1D53D</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x48;</mtext></math>=<span>0210D</span></span>
+  <span><math><mtext mathvariant="double-struck">&#x4A;</mtext></math>=<span>1D541</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur-ref.html
new file mode 100644
index 0000000..5e0721c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur-ref.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant fraktur (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-fraktur.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D504;</mtext></math>=<span>1D504</span></span>
+  <span><math><mtext>&#x1D505;</mtext></math>=<span>1D505</span></span>
+  <span><math><mtext>&#x212D;</mtext></math>=<span>0212D</span></span>
+  <span><math><mtext>&#x1D507;</mtext></math>=<span>1D507</span></span>
+  <span><math><mtext>&#x1D508;</mtext></math>=<span>1D508</span></span>
+  <span><math><mtext>&#x1D509;</mtext></math>=<span>1D509</span></span>
+  <span><math><mtext>&#x1D50A;</mtext></math>=<span>1D50A</span></span>
+  <span><math><mtext>&#x210C;</mtext></math>=<span>0210C</span></span>
+  <span><math><mtext>&#x2111;</mtext></math>=<span>02111</span></span>
+  <span><math><mtext>&#x1D50D;</mtext></math>=<span>1D50D</span></span><br/>
+  <span><math><mtext>&#x1D50E;</mtext></math>=<span>1D50E</span></span>
+  <span><math><mtext>&#x1D50F;</mtext></math>=<span>1D50F</span></span>
+  <span><math><mtext>&#x1D510;</mtext></math>=<span>1D510</span></span>
+  <span><math><mtext>&#x1D511;</mtext></math>=<span>1D511</span></span>
+  <span><math><mtext>&#x1D512;</mtext></math>=<span>1D512</span></span>
+  <span><math><mtext>&#x1D513;</mtext></math>=<span>1D513</span></span>
+  <span><math><mtext>&#x1D514;</mtext></math>=<span>1D514</span></span>
+  <span><math><mtext>&#x211C;</mtext></math>=<span>0211C</span></span>
+  <span><math><mtext>&#x1D516;</mtext></math>=<span>1D516</span></span>
+  <span><math><mtext>&#x1D517;</mtext></math>=<span>1D517</span></span><br/>
+  <span><math><mtext>&#x1D518;</mtext></math>=<span>1D518</span></span>
+  <span><math><mtext>&#x1D519;</mtext></math>=<span>1D519</span></span>
+  <span><math><mtext>&#x1D51A;</mtext></math>=<span>1D51A</span></span>
+  <span><math><mtext>&#x1D51B;</mtext></math>=<span>1D51B</span></span>
+  <span><math><mtext>&#x1D51C;</mtext></math>=<span>1D51C</span></span>
+  <span><math><mtext>&#x2128;</mtext></math>=<span>02128</span></span>
+  <span><math><mtext>&#x1D51E;</mtext></math>=<span>1D51E</span></span>
+  <span><math><mtext>&#x1D51F;</mtext></math>=<span>1D51F</span></span>
+  <span><math><mtext>&#x1D520;</mtext></math>=<span>1D520</span></span>
+  <span><math><mtext>&#x1D521;</mtext></math>=<span>1D521</span></span><br/>
+  <span><math><mtext>&#x1D522;</mtext></math>=<span>1D522</span></span>
+  <span><math><mtext>&#x1D523;</mtext></math>=<span>1D523</span></span>
+  <span><math><mtext>&#x1D524;</mtext></math>=<span>1D524</span></span>
+  <span><math><mtext>&#x1D525;</mtext></math>=<span>1D525</span></span>
+  <span><math><mtext>&#x1D526;</mtext></math>=<span>1D526</span></span>
+  <span><math><mtext>&#x1D527;</mtext></math>=<span>1D527</span></span>
+  <span><math><mtext>&#x1D528;</mtext></math>=<span>1D528</span></span>
+  <span><math><mtext>&#x1D529;</mtext></math>=<span>1D529</span></span>
+  <span><math><mtext>&#x1D52A;</mtext></math>=<span>1D52A</span></span>
+  <span><math><mtext>&#x1D52B;</mtext></math>=<span>1D52B</span></span><br/>
+  <span><math><mtext>&#x1D52C;</mtext></math>=<span>1D52C</span></span>
+  <span><math><mtext>&#x1D52D;</mtext></math>=<span>1D52D</span></span>
+  <span><math><mtext>&#x1D52E;</mtext></math>=<span>1D52E</span></span>
+  <span><math><mtext>&#x1D52F;</mtext></math>=<span>1D52F</span></span>
+  <span><math><mtext>&#x1D530;</mtext></math>=<span>1D530</span></span>
+  <span><math><mtext>&#x1D531;</mtext></math>=<span>1D531</span></span>
+  <span><math><mtext>&#x1D532;</mtext></math>=<span>1D532</span></span>
+  <span><math><mtext>&#x1D533;</mtext></math>=<span>1D533</span></span>
+  <span><math><mtext>&#x1D534;</mtext></math>=<span>1D534</span></span>
+  <span><math><mtext>&#x1D535;</mtext></math>=<span>1D535</span></span><br/>
+  <span><math><mtext>&#x1D536;</mtext></math>=<span>1D536</span></span>
+  <span><math><mtext>&#x1D537;</mtext></math>=<span>1D537</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur.html
new file mode 100644
index 0000000..12489172
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-fraktur.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant fraktur</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-fraktur-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a fraktur mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-fraktur.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="fraktur">&#x41;</mtext></math>=<span>1D504</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x42;</mtext></math>=<span>1D505</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x43;</mtext></math>=<span>0212D</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x44;</mtext></math>=<span>1D507</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x45;</mtext></math>=<span>1D508</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x46;</mtext></math>=<span>1D509</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x47;</mtext></math>=<span>1D50A</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x48;</mtext></math>=<span>0210C</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x49;</mtext></math>=<span>02111</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x4A;</mtext></math>=<span>1D50D</span></span><br/>
+  <span><math><mtext mathvariant="fraktur">&#x4B;</mtext></math>=<span>1D50E</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x4C;</mtext></math>=<span>1D50F</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x4D;</mtext></math>=<span>1D510</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x4E;</mtext></math>=<span>1D511</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x4F;</mtext></math>=<span>1D512</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x50;</mtext></math>=<span>1D513</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x51;</mtext></math>=<span>1D514</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x52;</mtext></math>=<span>0211C</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x53;</mtext></math>=<span>1D516</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x54;</mtext></math>=<span>1D517</span></span><br/>
+  <span><math><mtext mathvariant="fraktur">&#x55;</mtext></math>=<span>1D518</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x56;</mtext></math>=<span>1D519</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x57;</mtext></math>=<span>1D51A</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x58;</mtext></math>=<span>1D51B</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x59;</mtext></math>=<span>1D51C</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x5A;</mtext></math>=<span>02128</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x61;</mtext></math>=<span>1D51E</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x62;</mtext></math>=<span>1D51F</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x63;</mtext></math>=<span>1D520</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x64;</mtext></math>=<span>1D521</span></span><br/>
+  <span><math><mtext mathvariant="fraktur">&#x65;</mtext></math>=<span>1D522</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x66;</mtext></math>=<span>1D523</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x67;</mtext></math>=<span>1D524</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x68;</mtext></math>=<span>1D525</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x69;</mtext></math>=<span>1D526</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x6A;</mtext></math>=<span>1D527</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x6B;</mtext></math>=<span>1D528</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x6C;</mtext></math>=<span>1D529</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x6D;</mtext></math>=<span>1D52A</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x6E;</mtext></math>=<span>1D52B</span></span><br/>
+  <span><math><mtext mathvariant="fraktur">&#x6F;</mtext></math>=<span>1D52C</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x70;</mtext></math>=<span>1D52D</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x71;</mtext></math>=<span>1D52E</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x72;</mtext></math>=<span>1D52F</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x73;</mtext></math>=<span>1D530</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x74;</mtext></math>=<span>1D531</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x75;</mtext></math>=<span>1D532</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x76;</mtext></math>=<span>1D533</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x77;</mtext></math>=<span>1D534</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x78;</mtext></math>=<span>1D535</span></span><br/>
+  <span><math><mtext mathvariant="fraktur">&#x79;</mtext></math>=<span>1D536</span></span>
+  <span><math><mtext mathvariant="fraktur">&#x7A;</mtext></math>=<span>1D537</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial-ref.html
new file mode 100644
index 0000000..722ce5f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial-ref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant initial (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-initial.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1EE30;</mtext></math>=<span>1EE30</span></span>
+  <span><math><mtext>&#x1EE32;</mtext></math>=<span>1EE32</span></span>
+  <span><math><mtext>&#x1EE2A;</mtext></math>=<span>1EE2A</span></span>
+  <span><math><mtext>&#x1EE2B;</mtext></math>=<span>1EE2B</span></span>
+  <span><math><mtext>&#x1EE2C;</mtext></math>=<span>1EE2C</span></span>
+  <span><math><mtext>&#x1EE2D;</mtext></math>=<span>1EE2D</span></span>
+  <span><math><mtext>&#x1EE24;</mtext></math>=<span>1EE24</span></span>
+  <span><math><mtext>&#x1EE21;</mtext></math>=<span>1EE21</span></span>
+  <span><math><mtext>&#x1EE29;</mtext></math>=<span>1EE29</span></span>
+  <span><math><mtext>&#x1EE36;</mtext></math>=<span>1EE36</span></span><br/>
+  <span><math><mtext>&#x1EE22;</mtext></math>=<span>1EE22</span></span>
+  <span><math><mtext>&#x1EE27;</mtext></math>=<span>1EE27</span></span>
+  <span><math><mtext>&#x1EE37;</mtext></math>=<span>1EE37</span></span>
+  <span><math><mtext>&#x1EE2E;</mtext></math>=<span>1EE2E</span></span>
+  <span><math><mtext>&#x1EE34;</mtext></math>=<span>1EE34</span></span>
+  <span><math><mtext>&#x1EE31;</mtext></math>=<span>1EE31</span></span>
+  <span><math><mtext>&#x1EE39;</mtext></math>=<span>1EE39</span></span>
+  <span><math><mtext>&#x1EE2F;</mtext></math>=<span>1EE2F</span></span>
+  <span><math><mtext>&#x1EE3B;</mtext></math>=<span>1EE3B</span></span>
+  <span><math><mtext>&#x1EE35;</mtext></math>=<span>1EE35</span></span><br/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial.html
new file mode 100644
index 0000000..df9e0af7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-initial.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant initial</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-initial-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a initial mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-initial.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="initial">&#x641;</mtext></math>=<span>1EE30</span></span>
+  <span><math><mtext mathvariant="initial">&#x642;</mtext></math>=<span>1EE32</span></span>
+  <span><math><mtext mathvariant="initial">&#x643;</mtext></math>=<span>1EE2A</span></span>
+  <span><math><mtext mathvariant="initial">&#x644;</mtext></math>=<span>1EE2B</span></span>
+  <span><math><mtext mathvariant="initial">&#x645;</mtext></math>=<span>1EE2C</span></span>
+  <span><math><mtext mathvariant="initial">&#x646;</mtext></math>=<span>1EE2D</span></span>
+  <span><math><mtext mathvariant="initial">&#x647;</mtext></math>=<span>1EE24</span></span>
+  <span><math><mtext mathvariant="initial">&#x628;</mtext></math>=<span>1EE21</span></span>
+  <span><math><mtext mathvariant="initial">&#x64A;</mtext></math>=<span>1EE29</span></span>
+  <span><math><mtext mathvariant="initial">&#x62B;</mtext></math>=<span>1EE36</span></span><br/>
+  <span><math><mtext mathvariant="initial">&#x62C;</mtext></math>=<span>1EE22</span></span>
+  <span><math><mtext mathvariant="initial">&#x62D;</mtext></math>=<span>1EE27</span></span>
+  <span><math><mtext mathvariant="initial">&#x62E;</mtext></math>=<span>1EE37</span></span>
+  <span><math><mtext mathvariant="initial">&#x633;</mtext></math>=<span>1EE2E</span></span>
+  <span><math><mtext mathvariant="initial">&#x634;</mtext></math>=<span>1EE34</span></span>
+  <span><math><mtext mathvariant="initial">&#x635;</mtext></math>=<span>1EE31</span></span>
+  <span><math><mtext mathvariant="initial">&#x636;</mtext></math>=<span>1EE39</span></span>
+  <span><math><mtext mathvariant="initial">&#x639;</mtext></math>=<span>1EE2F</span></span>
+  <span><math><mtext mathvariant="initial">&#x63A;</mtext></math>=<span>1EE3B</span></span>
+  <span><math><mtext mathvariant="initial">&#x62A;</mtext></math>=<span>1EE35</span></span><br/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic-ref.html
new file mode 100644
index 0000000..70643b3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic-ref.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant italic (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-italic.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D715;</mtext></math>=<span>1D715</span></span>
+  <span><math><mtext>&#x1D6FB;</mtext></math>=<span>1D6FB</span></span>
+  <span><math><mtext>&#x1D6A5;</mtext></math>=<span>1D6A5</span></span>
+  <span><math><mtext>&#x1D434;</mtext></math>=<span>1D434</span></span>
+  <span><math><mtext>&#x1D435;</mtext></math>=<span>1D435</span></span>
+  <span><math><mtext>&#x1D436;</mtext></math>=<span>1D436</span></span>
+  <span><math><mtext>&#x1D437;</mtext></math>=<span>1D437</span></span>
+  <span><math><mtext>&#x1D438;</mtext></math>=<span>1D438</span></span>
+  <span><math><mtext>&#x1D439;</mtext></math>=<span>1D439</span></span>
+  <span><math><mtext>&#x1D43A;</mtext></math>=<span>1D43A</span></span><br/>
+  <span><math><mtext>&#x1D43B;</mtext></math>=<span>1D43B</span></span>
+  <span><math><mtext>&#x1D43C;</mtext></math>=<span>1D43C</span></span>
+  <span><math><mtext>&#x1D43D;</mtext></math>=<span>1D43D</span></span>
+  <span><math><mtext>&#x1D43E;</mtext></math>=<span>1D43E</span></span>
+  <span><math><mtext>&#x1D43F;</mtext></math>=<span>1D43F</span></span>
+  <span><math><mtext>&#x1D440;</mtext></math>=<span>1D440</span></span>
+  <span><math><mtext>&#x1D441;</mtext></math>=<span>1D441</span></span>
+  <span><math><mtext>&#x1D442;</mtext></math>=<span>1D442</span></span>
+  <span><math><mtext>&#x1D443;</mtext></math>=<span>1D443</span></span>
+  <span><math><mtext>&#x1D444;</mtext></math>=<span>1D444</span></span><br/>
+  <span><math><mtext>&#x1D445;</mtext></math>=<span>1D445</span></span>
+  <span><math><mtext>&#x1D446;</mtext></math>=<span>1D446</span></span>
+  <span><math><mtext>&#x1D447;</mtext></math>=<span>1D447</span></span>
+  <span><math><mtext>&#x1D448;</mtext></math>=<span>1D448</span></span>
+  <span><math><mtext>&#x1D449;</mtext></math>=<span>1D449</span></span>
+  <span><math><mtext>&#x1D44A;</mtext></math>=<span>1D44A</span></span>
+  <span><math><mtext>&#x1D44B;</mtext></math>=<span>1D44B</span></span>
+  <span><math><mtext>&#x1D44C;</mtext></math>=<span>1D44C</span></span>
+  <span><math><mtext>&#x1D44D;</mtext></math>=<span>1D44D</span></span>
+  <span><math><mtext>&#x1D44E;</mtext></math>=<span>1D44E</span></span><br/>
+  <span><math><mtext>&#x1D44F;</mtext></math>=<span>1D44F</span></span>
+  <span><math><mtext>&#x1D450;</mtext></math>=<span>1D450</span></span>
+  <span><math><mtext>&#x1D451;</mtext></math>=<span>1D451</span></span>
+  <span><math><mtext>&#x1D452;</mtext></math>=<span>1D452</span></span>
+  <span><math><mtext>&#x1D453;</mtext></math>=<span>1D453</span></span>
+  <span><math><mtext>&#x1D454;</mtext></math>=<span>1D454</span></span>
+  <span><math><mtext>&#x210E;</mtext></math>=<span>0210E</span></span>
+  <span><math><mtext>&#x1D456;</mtext></math>=<span>1D456</span></span>
+  <span><math><mtext>&#x1D457;</mtext></math>=<span>1D457</span></span>
+  <span><math><mtext>&#x1D458;</mtext></math>=<span>1D458</span></span><br/>
+  <span><math><mtext>&#x1D459;</mtext></math>=<span>1D459</span></span>
+  <span><math><mtext>&#x1D45A;</mtext></math>=<span>1D45A</span></span>
+  <span><math><mtext>&#x1D45B;</mtext></math>=<span>1D45B</span></span>
+  <span><math><mtext>&#x1D45C;</mtext></math>=<span>1D45C</span></span>
+  <span><math><mtext>&#x1D45D;</mtext></math>=<span>1D45D</span></span>
+  <span><math><mtext>&#x1D45E;</mtext></math>=<span>1D45E</span></span>
+  <span><math><mtext>&#x1D45F;</mtext></math>=<span>1D45F</span></span>
+  <span><math><mtext>&#x1D460;</mtext></math>=<span>1D460</span></span>
+  <span><math><mtext>&#x1D461;</mtext></math>=<span>1D461</span></span>
+  <span><math><mtext>&#x1D462;</mtext></math>=<span>1D462</span></span><br/>
+  <span><math><mtext>&#x1D463;</mtext></math>=<span>1D463</span></span>
+  <span><math><mtext>&#x1D464;</mtext></math>=<span>1D464</span></span>
+  <span><math><mtext>&#x1D465;</mtext></math>=<span>1D465</span></span>
+  <span><math><mtext>&#x1D466;</mtext></math>=<span>1D466</span></span>
+  <span><math><mtext>&#x1D467;</mtext></math>=<span>1D467</span></span>
+  <span><math><mtext>&#x1D6A4;</mtext></math>=<span>1D6A4</span></span>
+  <span><math><mtext>&#x1D6E2;</mtext></math>=<span>1D6E2</span></span>
+  <span><math><mtext>&#x1D6E3;</mtext></math>=<span>1D6E3</span></span>
+  <span><math><mtext>&#x1D6E4;</mtext></math>=<span>1D6E4</span></span>
+  <span><math><mtext>&#x1D6E5;</mtext></math>=<span>1D6E5</span></span><br/>
+  <span><math><mtext>&#x1D6E6;</mtext></math>=<span>1D6E6</span></span>
+  <span><math><mtext>&#x1D6E7;</mtext></math>=<span>1D6E7</span></span>
+  <span><math><mtext>&#x1D6E8;</mtext></math>=<span>1D6E8</span></span>
+  <span><math><mtext>&#x1D6E9;</mtext></math>=<span>1D6E9</span></span>
+  <span><math><mtext>&#x1D6EA;</mtext></math>=<span>1D6EA</span></span>
+  <span><math><mtext>&#x1D6EB;</mtext></math>=<span>1D6EB</span></span>
+  <span><math><mtext>&#x1D6EC;</mtext></math>=<span>1D6EC</span></span>
+  <span><math><mtext>&#x1D6ED;</mtext></math>=<span>1D6ED</span></span>
+  <span><math><mtext>&#x1D6EE;</mtext></math>=<span>1D6EE</span></span>
+  <span><math><mtext>&#x1D6EF;</mtext></math>=<span>1D6EF</span></span><br/>
+  <span><math><mtext>&#x1D6F0;</mtext></math>=<span>1D6F0</span></span>
+  <span><math><mtext>&#x1D6F1;</mtext></math>=<span>1D6F1</span></span>
+  <span><math><mtext>&#x1D6F2;</mtext></math>=<span>1D6F2</span></span>
+  <span><math><mtext>&#x1D6F4;</mtext></math>=<span>1D6F4</span></span>
+  <span><math><mtext>&#x1D6F5;</mtext></math>=<span>1D6F5</span></span>
+  <span><math><mtext>&#x1D6F6;</mtext></math>=<span>1D6F6</span></span>
+  <span><math><mtext>&#x1D6F7;</mtext></math>=<span>1D6F7</span></span>
+  <span><math><mtext>&#x1D6F8;</mtext></math>=<span>1D6F8</span></span>
+  <span><math><mtext>&#x1D6F9;</mtext></math>=<span>1D6F9</span></span>
+  <span><math><mtext>&#x1D6FA;</mtext></math>=<span>1D6FA</span></span><br/>
+  <span><math><mtext>&#x1D6FC;</mtext></math>=<span>1D6FC</span></span>
+  <span><math><mtext>&#x1D6FD;</mtext></math>=<span>1D6FD</span></span>
+  <span><math><mtext>&#x1D6FE;</mtext></math>=<span>1D6FE</span></span>
+  <span><math><mtext>&#x1D6FF;</mtext></math>=<span>1D6FF</span></span>
+  <span><math><mtext>&#x1D700;</mtext></math>=<span>1D700</span></span>
+  <span><math><mtext>&#x1D701;</mtext></math>=<span>1D701</span></span>
+  <span><math><mtext>&#x1D702;</mtext></math>=<span>1D702</span></span>
+  <span><math><mtext>&#x1D703;</mtext></math>=<span>1D703</span></span>
+  <span><math><mtext>&#x1D704;</mtext></math>=<span>1D704</span></span>
+  <span><math><mtext>&#x1D705;</mtext></math>=<span>1D705</span></span><br/>
+  <span><math><mtext>&#x1D706;</mtext></math>=<span>1D706</span></span>
+  <span><math><mtext>&#x1D707;</mtext></math>=<span>1D707</span></span>
+  <span><math><mtext>&#x1D708;</mtext></math>=<span>1D708</span></span>
+  <span><math><mtext>&#x1D709;</mtext></math>=<span>1D709</span></span>
+  <span><math><mtext>&#x1D70A;</mtext></math>=<span>1D70A</span></span>
+  <span><math><mtext>&#x1D70B;</mtext></math>=<span>1D70B</span></span>
+  <span><math><mtext>&#x1D70C;</mtext></math>=<span>1D70C</span></span>
+  <span><math><mtext>&#x1D70D;</mtext></math>=<span>1D70D</span></span>
+  <span><math><mtext>&#x1D70E;</mtext></math>=<span>1D70E</span></span>
+  <span><math><mtext>&#x1D70F;</mtext></math>=<span>1D70F</span></span><br/>
+  <span><math><mtext>&#x1D710;</mtext></math>=<span>1D710</span></span>
+  <span><math><mtext>&#x1D711;</mtext></math>=<span>1D711</span></span>
+  <span><math><mtext>&#x1D712;</mtext></math>=<span>1D712</span></span>
+  <span><math><mtext>&#x1D713;</mtext></math>=<span>1D713</span></span>
+  <span><math><mtext>&#x1D714;</mtext></math>=<span>1D714</span></span>
+  <span><math><mtext>&#x1D717;</mtext></math>=<span>1D717</span></span>
+  <span><math><mtext>&#x1D719;</mtext></math>=<span>1D719</span></span>
+  <span><math><mtext>&#x1D71B;</mtext></math>=<span>1D71B</span></span>
+  <span><math><mtext>&#x1D718;</mtext></math>=<span>1D718</span></span>
+  <span><math><mtext>&#x1D71A;</mtext></math>=<span>1D71A</span></span><br/>
+  <span><math><mtext>&#x1D6F3;</mtext></math>=<span>1D6F3</span></span>
+  <span><math><mtext>&#x1D716;</mtext></math>=<span>1D716</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic.html
new file mode 100644
index 0000000..e1612a5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-italic.html
@@ -0,0 +1,142 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant italic</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-italic-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a italic mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-italic.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="italic">&#x2202;</mtext></math>=<span>1D715</span></span>
+  <span><math><mtext mathvariant="italic">&#x2207;</mtext></math>=<span>1D6FB</span></span>
+  <span><math><mtext mathvariant="italic">&#x237;</mtext></math>=<span>1D6A5</span></span>
+  <span><math><mtext mathvariant="italic">&#x41;</mtext></math>=<span>1D434</span></span>
+  <span><math><mtext mathvariant="italic">&#x42;</mtext></math>=<span>1D435</span></span>
+  <span><math><mtext mathvariant="italic">&#x43;</mtext></math>=<span>1D436</span></span>
+  <span><math><mtext mathvariant="italic">&#x44;</mtext></math>=<span>1D437</span></span>
+  <span><math><mtext mathvariant="italic">&#x45;</mtext></math>=<span>1D438</span></span>
+  <span><math><mtext mathvariant="italic">&#x46;</mtext></math>=<span>1D439</span></span>
+  <span><math><mtext mathvariant="italic">&#x47;</mtext></math>=<span>1D43A</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x48;</mtext></math>=<span>1D43B</span></span>
+  <span><math><mtext mathvariant="italic">&#x49;</mtext></math>=<span>1D43C</span></span>
+  <span><math><mtext mathvariant="italic">&#x4A;</mtext></math>=<span>1D43D</span></span>
+  <span><math><mtext mathvariant="italic">&#x4B;</mtext></math>=<span>1D43E</span></span>
+  <span><math><mtext mathvariant="italic">&#x4C;</mtext></math>=<span>1D43F</span></span>
+  <span><math><mtext mathvariant="italic">&#x4D;</mtext></math>=<span>1D440</span></span>
+  <span><math><mtext mathvariant="italic">&#x4E;</mtext></math>=<span>1D441</span></span>
+  <span><math><mtext mathvariant="italic">&#x4F;</mtext></math>=<span>1D442</span></span>
+  <span><math><mtext mathvariant="italic">&#x50;</mtext></math>=<span>1D443</span></span>
+  <span><math><mtext mathvariant="italic">&#x51;</mtext></math>=<span>1D444</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x52;</mtext></math>=<span>1D445</span></span>
+  <span><math><mtext mathvariant="italic">&#x53;</mtext></math>=<span>1D446</span></span>
+  <span><math><mtext mathvariant="italic">&#x54;</mtext></math>=<span>1D447</span></span>
+  <span><math><mtext mathvariant="italic">&#x55;</mtext></math>=<span>1D448</span></span>
+  <span><math><mtext mathvariant="italic">&#x56;</mtext></math>=<span>1D449</span></span>
+  <span><math><mtext mathvariant="italic">&#x57;</mtext></math>=<span>1D44A</span></span>
+  <span><math><mtext mathvariant="italic">&#x58;</mtext></math>=<span>1D44B</span></span>
+  <span><math><mtext mathvariant="italic">&#x59;</mtext></math>=<span>1D44C</span></span>
+  <span><math><mtext mathvariant="italic">&#x5A;</mtext></math>=<span>1D44D</span></span>
+  <span><math><mtext mathvariant="italic">&#x61;</mtext></math>=<span>1D44E</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x62;</mtext></math>=<span>1D44F</span></span>
+  <span><math><mtext mathvariant="italic">&#x63;</mtext></math>=<span>1D450</span></span>
+  <span><math><mtext mathvariant="italic">&#x64;</mtext></math>=<span>1D451</span></span>
+  <span><math><mtext mathvariant="italic">&#x65;</mtext></math>=<span>1D452</span></span>
+  <span><math><mtext mathvariant="italic">&#x66;</mtext></math>=<span>1D453</span></span>
+  <span><math><mtext mathvariant="italic">&#x67;</mtext></math>=<span>1D454</span></span>
+  <span><math><mtext mathvariant="italic">&#x68;</mtext></math>=<span>0210E</span></span>
+  <span><math><mtext mathvariant="italic">&#x69;</mtext></math>=<span>1D456</span></span>
+  <span><math><mtext mathvariant="italic">&#x6A;</mtext></math>=<span>1D457</span></span>
+  <span><math><mtext mathvariant="italic">&#x6B;</mtext></math>=<span>1D458</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x6C;</mtext></math>=<span>1D459</span></span>
+  <span><math><mtext mathvariant="italic">&#x6D;</mtext></math>=<span>1D45A</span></span>
+  <span><math><mtext mathvariant="italic">&#x6E;</mtext></math>=<span>1D45B</span></span>
+  <span><math><mtext mathvariant="italic">&#x6F;</mtext></math>=<span>1D45C</span></span>
+  <span><math><mtext mathvariant="italic">&#x70;</mtext></math>=<span>1D45D</span></span>
+  <span><math><mtext mathvariant="italic">&#x71;</mtext></math>=<span>1D45E</span></span>
+  <span><math><mtext mathvariant="italic">&#x72;</mtext></math>=<span>1D45F</span></span>
+  <span><math><mtext mathvariant="italic">&#x73;</mtext></math>=<span>1D460</span></span>
+  <span><math><mtext mathvariant="italic">&#x74;</mtext></math>=<span>1D461</span></span>
+  <span><math><mtext mathvariant="italic">&#x75;</mtext></math>=<span>1D462</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x76;</mtext></math>=<span>1D463</span></span>
+  <span><math><mtext mathvariant="italic">&#x77;</mtext></math>=<span>1D464</span></span>
+  <span><math><mtext mathvariant="italic">&#x78;</mtext></math>=<span>1D465</span></span>
+  <span><math><mtext mathvariant="italic">&#x79;</mtext></math>=<span>1D466</span></span>
+  <span><math><mtext mathvariant="italic">&#x7A;</mtext></math>=<span>1D467</span></span>
+  <span><math><mtext mathvariant="italic">&#x131;</mtext></math>=<span>1D6A4</span></span>
+  <span><math><mtext mathvariant="italic">&#x391;</mtext></math>=<span>1D6E2</span></span>
+  <span><math><mtext mathvariant="italic">&#x392;</mtext></math>=<span>1D6E3</span></span>
+  <span><math><mtext mathvariant="italic">&#x393;</mtext></math>=<span>1D6E4</span></span>
+  <span><math><mtext mathvariant="italic">&#x394;</mtext></math>=<span>1D6E5</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x395;</mtext></math>=<span>1D6E6</span></span>
+  <span><math><mtext mathvariant="italic">&#x396;</mtext></math>=<span>1D6E7</span></span>
+  <span><math><mtext mathvariant="italic">&#x397;</mtext></math>=<span>1D6E8</span></span>
+  <span><math><mtext mathvariant="italic">&#x398;</mtext></math>=<span>1D6E9</span></span>
+  <span><math><mtext mathvariant="italic">&#x399;</mtext></math>=<span>1D6EA</span></span>
+  <span><math><mtext mathvariant="italic">&#x39A;</mtext></math>=<span>1D6EB</span></span>
+  <span><math><mtext mathvariant="italic">&#x39B;</mtext></math>=<span>1D6EC</span></span>
+  <span><math><mtext mathvariant="italic">&#x39C;</mtext></math>=<span>1D6ED</span></span>
+  <span><math><mtext mathvariant="italic">&#x39D;</mtext></math>=<span>1D6EE</span></span>
+  <span><math><mtext mathvariant="italic">&#x39E;</mtext></math>=<span>1D6EF</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x39F;</mtext></math>=<span>1D6F0</span></span>
+  <span><math><mtext mathvariant="italic">&#x3A0;</mtext></math>=<span>1D6F1</span></span>
+  <span><math><mtext mathvariant="italic">&#x3A1;</mtext></math>=<span>1D6F2</span></span>
+  <span><math><mtext mathvariant="italic">&#x3A3;</mtext></math>=<span>1D6F4</span></span>
+  <span><math><mtext mathvariant="italic">&#x3A4;</mtext></math>=<span>1D6F5</span></span>
+  <span><math><mtext mathvariant="italic">&#x3A5;</mtext></math>=<span>1D6F6</span></span>
+  <span><math><mtext mathvariant="italic">&#x3A6;</mtext></math>=<span>1D6F7</span></span>
+  <span><math><mtext mathvariant="italic">&#x3A7;</mtext></math>=<span>1D6F8</span></span>
+  <span><math><mtext mathvariant="italic">&#x3A8;</mtext></math>=<span>1D6F9</span></span>
+  <span><math><mtext mathvariant="italic">&#x3A9;</mtext></math>=<span>1D6FA</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x3B1;</mtext></math>=<span>1D6FC</span></span>
+  <span><math><mtext mathvariant="italic">&#x3B2;</mtext></math>=<span>1D6FD</span></span>
+  <span><math><mtext mathvariant="italic">&#x3B3;</mtext></math>=<span>1D6FE</span></span>
+  <span><math><mtext mathvariant="italic">&#x3B4;</mtext></math>=<span>1D6FF</span></span>
+  <span><math><mtext mathvariant="italic">&#x3B5;</mtext></math>=<span>1D700</span></span>
+  <span><math><mtext mathvariant="italic">&#x3B6;</mtext></math>=<span>1D701</span></span>
+  <span><math><mtext mathvariant="italic">&#x3B7;</mtext></math>=<span>1D702</span></span>
+  <span><math><mtext mathvariant="italic">&#x3B8;</mtext></math>=<span>1D703</span></span>
+  <span><math><mtext mathvariant="italic">&#x3B9;</mtext></math>=<span>1D704</span></span>
+  <span><math><mtext mathvariant="italic">&#x3BA;</mtext></math>=<span>1D705</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x3BB;</mtext></math>=<span>1D706</span></span>
+  <span><math><mtext mathvariant="italic">&#x3BC;</mtext></math>=<span>1D707</span></span>
+  <span><math><mtext mathvariant="italic">&#x3BD;</mtext></math>=<span>1D708</span></span>
+  <span><math><mtext mathvariant="italic">&#x3BE;</mtext></math>=<span>1D709</span></span>
+  <span><math><mtext mathvariant="italic">&#x3BF;</mtext></math>=<span>1D70A</span></span>
+  <span><math><mtext mathvariant="italic">&#x3C0;</mtext></math>=<span>1D70B</span></span>
+  <span><math><mtext mathvariant="italic">&#x3C1;</mtext></math>=<span>1D70C</span></span>
+  <span><math><mtext mathvariant="italic">&#x3C2;</mtext></math>=<span>1D70D</span></span>
+  <span><math><mtext mathvariant="italic">&#x3C3;</mtext></math>=<span>1D70E</span></span>
+  <span><math><mtext mathvariant="italic">&#x3C4;</mtext></math>=<span>1D70F</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x3C5;</mtext></math>=<span>1D710</span></span>
+  <span><math><mtext mathvariant="italic">&#x3C6;</mtext></math>=<span>1D711</span></span>
+  <span><math><mtext mathvariant="italic">&#x3C7;</mtext></math>=<span>1D712</span></span>
+  <span><math><mtext mathvariant="italic">&#x3C8;</mtext></math>=<span>1D713</span></span>
+  <span><math><mtext mathvariant="italic">&#x3C9;</mtext></math>=<span>1D714</span></span>
+  <span><math><mtext mathvariant="italic">&#x3D1;</mtext></math>=<span>1D717</span></span>
+  <span><math><mtext mathvariant="italic">&#x3D5;</mtext></math>=<span>1D719</span></span>
+  <span><math><mtext mathvariant="italic">&#x3D6;</mtext></math>=<span>1D71B</span></span>
+  <span><math><mtext mathvariant="italic">&#x3F0;</mtext></math>=<span>1D718</span></span>
+  <span><math><mtext mathvariant="italic">&#x3F1;</mtext></math>=<span>1D71A</span></span><br/>
+  <span><math><mtext mathvariant="italic">&#x3F4;</mtext></math>=<span>1D6F3</span></span>
+  <span><math><mtext mathvariant="italic">&#x3F5;</mtext></math>=<span>1D716</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped-ref.html
new file mode 100644
index 0000000..49bafef
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant looped (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-looped.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1EE80;</mtext></math>=<span>1EE80</span></span>
+  <span><math><mtext>&#x1EE81;</mtext></math>=<span>1EE81</span></span>
+  <span><math><mtext>&#x1EE95;</mtext></math>=<span>1EE95</span></span>
+  <span><math><mtext>&#x1EE96;</mtext></math>=<span>1EE96</span></span>
+  <span><math><mtext>&#x1EE82;</mtext></math>=<span>1EE82</span></span>
+  <span><math><mtext>&#x1EE87;</mtext></math>=<span>1EE87</span></span>
+  <span><math><mtext>&#x1EE97;</mtext></math>=<span>1EE97</span></span>
+  <span><math><mtext>&#x1EE83;</mtext></math>=<span>1EE83</span></span>
+  <span><math><mtext>&#x1EE98;</mtext></math>=<span>1EE98</span></span>
+  <span><math><mtext>&#x1EE93;</mtext></math>=<span>1EE93</span></span><br/>
+  <span><math><mtext>&#x1EE86;</mtext></math>=<span>1EE86</span></span>
+  <span><math><mtext>&#x1EE8E;</mtext></math>=<span>1EE8E</span></span>
+  <span><math><mtext>&#x1EE94;</mtext></math>=<span>1EE94</span></span>
+  <span><math><mtext>&#x1EE91;</mtext></math>=<span>1EE91</span></span>
+  <span><math><mtext>&#x1EE99;</mtext></math>=<span>1EE99</span></span>
+  <span><math><mtext>&#x1EE88;</mtext></math>=<span>1EE88</span></span>
+  <span><math><mtext>&#x1EE9A;</mtext></math>=<span>1EE9A</span></span>
+  <span><math><mtext>&#x1EE8F;</mtext></math>=<span>1EE8F</span></span>
+  <span><math><mtext>&#x1EE9B;</mtext></math>=<span>1EE9B</span></span>
+  <span><math><mtext>&#x1EE90;</mtext></math>=<span>1EE90</span></span><br/>
+  <span><math><mtext>&#x1EE92;</mtext></math>=<span>1EE92</span></span>
+  <span><math><mtext>&#x1EE8B;</mtext></math>=<span>1EE8B</span></span>
+  <span><math><mtext>&#x1EE8C;</mtext></math>=<span>1EE8C</span></span>
+  <span><math><mtext>&#x1EE8D;</mtext></math>=<span>1EE8D</span></span>
+  <span><math><mtext>&#x1EE84;</mtext></math>=<span>1EE84</span></span>
+  <span><math><mtext>&#x1EE85;</mtext></math>=<span>1EE85</span></span>
+  <span><math><mtext>&#x1EE89;</mtext></math>=<span>1EE89</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped.html
new file mode 100644
index 0000000..81206e1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-looped.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant looped</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-looped-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a looped mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-looped.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="looped">&#x627;</mtext></math>=<span>1EE80</span></span>
+  <span><math><mtext mathvariant="looped">&#x628;</mtext></math>=<span>1EE81</span></span>
+  <span><math><mtext mathvariant="looped">&#x62A;</mtext></math>=<span>1EE95</span></span>
+  <span><math><mtext mathvariant="looped">&#x62B;</mtext></math>=<span>1EE96</span></span>
+  <span><math><mtext mathvariant="looped">&#x62C;</mtext></math>=<span>1EE82</span></span>
+  <span><math><mtext mathvariant="looped">&#x62D;</mtext></math>=<span>1EE87</span></span>
+  <span><math><mtext mathvariant="looped">&#x62E;</mtext></math>=<span>1EE97</span></span>
+  <span><math><mtext mathvariant="looped">&#x62F;</mtext></math>=<span>1EE83</span></span>
+  <span><math><mtext mathvariant="looped">&#x630;</mtext></math>=<span>1EE98</span></span>
+  <span><math><mtext mathvariant="looped">&#x631;</mtext></math>=<span>1EE93</span></span><br/>
+  <span><math><mtext mathvariant="looped">&#x632;</mtext></math>=<span>1EE86</span></span>
+  <span><math><mtext mathvariant="looped">&#x633;</mtext></math>=<span>1EE8E</span></span>
+  <span><math><mtext mathvariant="looped">&#x634;</mtext></math>=<span>1EE94</span></span>
+  <span><math><mtext mathvariant="looped">&#x635;</mtext></math>=<span>1EE91</span></span>
+  <span><math><mtext mathvariant="looped">&#x636;</mtext></math>=<span>1EE99</span></span>
+  <span><math><mtext mathvariant="looped">&#x637;</mtext></math>=<span>1EE88</span></span>
+  <span><math><mtext mathvariant="looped">&#x638;</mtext></math>=<span>1EE9A</span></span>
+  <span><math><mtext mathvariant="looped">&#x639;</mtext></math>=<span>1EE8F</span></span>
+  <span><math><mtext mathvariant="looped">&#x63A;</mtext></math>=<span>1EE9B</span></span>
+  <span><math><mtext mathvariant="looped">&#x641;</mtext></math>=<span>1EE90</span></span><br/>
+  <span><math><mtext mathvariant="looped">&#x642;</mtext></math>=<span>1EE92</span></span>
+  <span><math><mtext mathvariant="looped">&#x644;</mtext></math>=<span>1EE8B</span></span>
+  <span><math><mtext mathvariant="looped">&#x645;</mtext></math>=<span>1EE8C</span></span>
+  <span><math><mtext mathvariant="looped">&#x646;</mtext></math>=<span>1EE8D</span></span>
+  <span><math><mtext mathvariant="looped">&#x647;</mtext></math>=<span>1EE84</span></span>
+  <span><math><mtext mathvariant="looped">&#x648;</mtext></math>=<span>1EE85</span></span>
+  <span><math><mtext mathvariant="looped">&#x64A;</mtext></math>=<span>1EE89</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace-ref.html
new file mode 100644
index 0000000..9b695a1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace-ref.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant monospace (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-monospace.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D7F6;</mtext></math>=<span>1D7F6</span></span>
+  <span><math><mtext>&#x1D7F7;</mtext></math>=<span>1D7F7</span></span>
+  <span><math><mtext>&#x1D7F8;</mtext></math>=<span>1D7F8</span></span>
+  <span><math><mtext>&#x1D7F9;</mtext></math>=<span>1D7F9</span></span>
+  <span><math><mtext>&#x1D7FA;</mtext></math>=<span>1D7FA</span></span>
+  <span><math><mtext>&#x1D7FB;</mtext></math>=<span>1D7FB</span></span>
+  <span><math><mtext>&#x1D7FC;</mtext></math>=<span>1D7FC</span></span>
+  <span><math><mtext>&#x1D7FD;</mtext></math>=<span>1D7FD</span></span>
+  <span><math><mtext>&#x1D7FE;</mtext></math>=<span>1D7FE</span></span>
+  <span><math><mtext>&#x1D7FF;</mtext></math>=<span>1D7FF</span></span><br/>
+  <span><math><mtext>&#x1D670;</mtext></math>=<span>1D670</span></span>
+  <span><math><mtext>&#x1D671;</mtext></math>=<span>1D671</span></span>
+  <span><math><mtext>&#x1D672;</mtext></math>=<span>1D672</span></span>
+  <span><math><mtext>&#x1D673;</mtext></math>=<span>1D673</span></span>
+  <span><math><mtext>&#x1D674;</mtext></math>=<span>1D674</span></span>
+  <span><math><mtext>&#x1D675;</mtext></math>=<span>1D675</span></span>
+  <span><math><mtext>&#x1D676;</mtext></math>=<span>1D676</span></span>
+  <span><math><mtext>&#x1D677;</mtext></math>=<span>1D677</span></span>
+  <span><math><mtext>&#x1D678;</mtext></math>=<span>1D678</span></span>
+  <span><math><mtext>&#x1D679;</mtext></math>=<span>1D679</span></span><br/>
+  <span><math><mtext>&#x1D67A;</mtext></math>=<span>1D67A</span></span>
+  <span><math><mtext>&#x1D67B;</mtext></math>=<span>1D67B</span></span>
+  <span><math><mtext>&#x1D67C;</mtext></math>=<span>1D67C</span></span>
+  <span><math><mtext>&#x1D67D;</mtext></math>=<span>1D67D</span></span>
+  <span><math><mtext>&#x1D67E;</mtext></math>=<span>1D67E</span></span>
+  <span><math><mtext>&#x1D67F;</mtext></math>=<span>1D67F</span></span>
+  <span><math><mtext>&#x1D680;</mtext></math>=<span>1D680</span></span>
+  <span><math><mtext>&#x1D681;</mtext></math>=<span>1D681</span></span>
+  <span><math><mtext>&#x1D682;</mtext></math>=<span>1D682</span></span>
+  <span><math><mtext>&#x1D683;</mtext></math>=<span>1D683</span></span><br/>
+  <span><math><mtext>&#x1D684;</mtext></math>=<span>1D684</span></span>
+  <span><math><mtext>&#x1D685;</mtext></math>=<span>1D685</span></span>
+  <span><math><mtext>&#x1D686;</mtext></math>=<span>1D686</span></span>
+  <span><math><mtext>&#x1D687;</mtext></math>=<span>1D687</span></span>
+  <span><math><mtext>&#x1D688;</mtext></math>=<span>1D688</span></span>
+  <span><math><mtext>&#x1D689;</mtext></math>=<span>1D689</span></span>
+  <span><math><mtext>&#x1D68A;</mtext></math>=<span>1D68A</span></span>
+  <span><math><mtext>&#x1D68B;</mtext></math>=<span>1D68B</span></span>
+  <span><math><mtext>&#x1D68C;</mtext></math>=<span>1D68C</span></span>
+  <span><math><mtext>&#x1D68D;</mtext></math>=<span>1D68D</span></span><br/>
+  <span><math><mtext>&#x1D68E;</mtext></math>=<span>1D68E</span></span>
+  <span><math><mtext>&#x1D68F;</mtext></math>=<span>1D68F</span></span>
+  <span><math><mtext>&#x1D690;</mtext></math>=<span>1D690</span></span>
+  <span><math><mtext>&#x1D691;</mtext></math>=<span>1D691</span></span>
+  <span><math><mtext>&#x1D692;</mtext></math>=<span>1D692</span></span>
+  <span><math><mtext>&#x1D693;</mtext></math>=<span>1D693</span></span>
+  <span><math><mtext>&#x1D694;</mtext></math>=<span>1D694</span></span>
+  <span><math><mtext>&#x1D695;</mtext></math>=<span>1D695</span></span>
+  <span><math><mtext>&#x1D696;</mtext></math>=<span>1D696</span></span>
+  <span><math><mtext>&#x1D697;</mtext></math>=<span>1D697</span></span><br/>
+  <span><math><mtext>&#x1D698;</mtext></math>=<span>1D698</span></span>
+  <span><math><mtext>&#x1D699;</mtext></math>=<span>1D699</span></span>
+  <span><math><mtext>&#x1D69A;</mtext></math>=<span>1D69A</span></span>
+  <span><math><mtext>&#x1D69B;</mtext></math>=<span>1D69B</span></span>
+  <span><math><mtext>&#x1D69C;</mtext></math>=<span>1D69C</span></span>
+  <span><math><mtext>&#x1D69D;</mtext></math>=<span>1D69D</span></span>
+  <span><math><mtext>&#x1D69E;</mtext></math>=<span>1D69E</span></span>
+  <span><math><mtext>&#x1D69F;</mtext></math>=<span>1D69F</span></span>
+  <span><math><mtext>&#x1D6A0;</mtext></math>=<span>1D6A0</span></span>
+  <span><math><mtext>&#x1D6A1;</mtext></math>=<span>1D6A1</span></span><br/>
+  <span><math><mtext>&#x1D6A2;</mtext></math>=<span>1D6A2</span></span>
+  <span><math><mtext>&#x1D6A3;</mtext></math>=<span>1D6A3</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace.html
new file mode 100644
index 0000000..8d4a4ad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-monospace.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant monospace</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-monospace-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a monospace mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-monospace.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="monospace">&#x30;</mtext></math>=<span>1D7F6</span></span>
+  <span><math><mtext mathvariant="monospace">&#x31;</mtext></math>=<span>1D7F7</span></span>
+  <span><math><mtext mathvariant="monospace">&#x32;</mtext></math>=<span>1D7F8</span></span>
+  <span><math><mtext mathvariant="monospace">&#x33;</mtext></math>=<span>1D7F9</span></span>
+  <span><math><mtext mathvariant="monospace">&#x34;</mtext></math>=<span>1D7FA</span></span>
+  <span><math><mtext mathvariant="monospace">&#x35;</mtext></math>=<span>1D7FB</span></span>
+  <span><math><mtext mathvariant="monospace">&#x36;</mtext></math>=<span>1D7FC</span></span>
+  <span><math><mtext mathvariant="monospace">&#x37;</mtext></math>=<span>1D7FD</span></span>
+  <span><math><mtext mathvariant="monospace">&#x38;</mtext></math>=<span>1D7FE</span></span>
+  <span><math><mtext mathvariant="monospace">&#x39;</mtext></math>=<span>1D7FF</span></span><br/>
+  <span><math><mtext mathvariant="monospace">&#x41;</mtext></math>=<span>1D670</span></span>
+  <span><math><mtext mathvariant="monospace">&#x42;</mtext></math>=<span>1D671</span></span>
+  <span><math><mtext mathvariant="monospace">&#x43;</mtext></math>=<span>1D672</span></span>
+  <span><math><mtext mathvariant="monospace">&#x44;</mtext></math>=<span>1D673</span></span>
+  <span><math><mtext mathvariant="monospace">&#x45;</mtext></math>=<span>1D674</span></span>
+  <span><math><mtext mathvariant="monospace">&#x46;</mtext></math>=<span>1D675</span></span>
+  <span><math><mtext mathvariant="monospace">&#x47;</mtext></math>=<span>1D676</span></span>
+  <span><math><mtext mathvariant="monospace">&#x48;</mtext></math>=<span>1D677</span></span>
+  <span><math><mtext mathvariant="monospace">&#x49;</mtext></math>=<span>1D678</span></span>
+  <span><math><mtext mathvariant="monospace">&#x4A;</mtext></math>=<span>1D679</span></span><br/>
+  <span><math><mtext mathvariant="monospace">&#x4B;</mtext></math>=<span>1D67A</span></span>
+  <span><math><mtext mathvariant="monospace">&#x4C;</mtext></math>=<span>1D67B</span></span>
+  <span><math><mtext mathvariant="monospace">&#x4D;</mtext></math>=<span>1D67C</span></span>
+  <span><math><mtext mathvariant="monospace">&#x4E;</mtext></math>=<span>1D67D</span></span>
+  <span><math><mtext mathvariant="monospace">&#x4F;</mtext></math>=<span>1D67E</span></span>
+  <span><math><mtext mathvariant="monospace">&#x50;</mtext></math>=<span>1D67F</span></span>
+  <span><math><mtext mathvariant="monospace">&#x51;</mtext></math>=<span>1D680</span></span>
+  <span><math><mtext mathvariant="monospace">&#x52;</mtext></math>=<span>1D681</span></span>
+  <span><math><mtext mathvariant="monospace">&#x53;</mtext></math>=<span>1D682</span></span>
+  <span><math><mtext mathvariant="monospace">&#x54;</mtext></math>=<span>1D683</span></span><br/>
+  <span><math><mtext mathvariant="monospace">&#x55;</mtext></math>=<span>1D684</span></span>
+  <span><math><mtext mathvariant="monospace">&#x56;</mtext></math>=<span>1D685</span></span>
+  <span><math><mtext mathvariant="monospace">&#x57;</mtext></math>=<span>1D686</span></span>
+  <span><math><mtext mathvariant="monospace">&#x58;</mtext></math>=<span>1D687</span></span>
+  <span><math><mtext mathvariant="monospace">&#x59;</mtext></math>=<span>1D688</span></span>
+  <span><math><mtext mathvariant="monospace">&#x5A;</mtext></math>=<span>1D689</span></span>
+  <span><math><mtext mathvariant="monospace">&#x61;</mtext></math>=<span>1D68A</span></span>
+  <span><math><mtext mathvariant="monospace">&#x62;</mtext></math>=<span>1D68B</span></span>
+  <span><math><mtext mathvariant="monospace">&#x63;</mtext></math>=<span>1D68C</span></span>
+  <span><math><mtext mathvariant="monospace">&#x64;</mtext></math>=<span>1D68D</span></span><br/>
+  <span><math><mtext mathvariant="monospace">&#x65;</mtext></math>=<span>1D68E</span></span>
+  <span><math><mtext mathvariant="monospace">&#x66;</mtext></math>=<span>1D68F</span></span>
+  <span><math><mtext mathvariant="monospace">&#x67;</mtext></math>=<span>1D690</span></span>
+  <span><math><mtext mathvariant="monospace">&#x68;</mtext></math>=<span>1D691</span></span>
+  <span><math><mtext mathvariant="monospace">&#x69;</mtext></math>=<span>1D692</span></span>
+  <span><math><mtext mathvariant="monospace">&#x6A;</mtext></math>=<span>1D693</span></span>
+  <span><math><mtext mathvariant="monospace">&#x6B;</mtext></math>=<span>1D694</span></span>
+  <span><math><mtext mathvariant="monospace">&#x6C;</mtext></math>=<span>1D695</span></span>
+  <span><math><mtext mathvariant="monospace">&#x6D;</mtext></math>=<span>1D696</span></span>
+  <span><math><mtext mathvariant="monospace">&#x6E;</mtext></math>=<span>1D697</span></span><br/>
+  <span><math><mtext mathvariant="monospace">&#x6F;</mtext></math>=<span>1D698</span></span>
+  <span><math><mtext mathvariant="monospace">&#x70;</mtext></math>=<span>1D699</span></span>
+  <span><math><mtext mathvariant="monospace">&#x71;</mtext></math>=<span>1D69A</span></span>
+  <span><math><mtext mathvariant="monospace">&#x72;</mtext></math>=<span>1D69B</span></span>
+  <span><math><mtext mathvariant="monospace">&#x73;</mtext></math>=<span>1D69C</span></span>
+  <span><math><mtext mathvariant="monospace">&#x74;</mtext></math>=<span>1D69D</span></span>
+  <span><math><mtext mathvariant="monospace">&#x75;</mtext></math>=<span>1D69E</span></span>
+  <span><math><mtext mathvariant="monospace">&#x76;</mtext></math>=<span>1D69F</span></span>
+  <span><math><mtext mathvariant="monospace">&#x77;</mtext></math>=<span>1D6A0</span></span>
+  <span><math><mtext mathvariant="monospace">&#x78;</mtext></math>=<span>1D6A1</span></span><br/>
+  <span><math><mtext mathvariant="monospace">&#x79;</mtext></math>=<span>1D6A2</span></span>
+  <span><math><mtext mathvariant="monospace">&#x7A;</mtext></math>=<span>1D6A3</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic-ref.html
new file mode 100644
index 0000000..f8ef62b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic-ref.html
@@ -0,0 +1,137 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant sans-serif-bold-italic (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-sans-serif-bold-italic.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D7C3;</mtext></math>=<span>1D7C3</span></span>
+  <span><math><mtext>&#x1D7A9;</mtext></math>=<span>1D7A9</span></span>
+  <span><math><mtext>&#x1D63C;</mtext></math>=<span>1D63C</span></span>
+  <span><math><mtext>&#x1D63D;</mtext></math>=<span>1D63D</span></span>
+  <span><math><mtext>&#x1D63E;</mtext></math>=<span>1D63E</span></span>
+  <span><math><mtext>&#x1D63F;</mtext></math>=<span>1D63F</span></span>
+  <span><math><mtext>&#x1D640;</mtext></math>=<span>1D640</span></span>
+  <span><math><mtext>&#x1D641;</mtext></math>=<span>1D641</span></span>
+  <span><math><mtext>&#x1D642;</mtext></math>=<span>1D642</span></span>
+  <span><math><mtext>&#x1D643;</mtext></math>=<span>1D643</span></span><br/>
+  <span><math><mtext>&#x1D644;</mtext></math>=<span>1D644</span></span>
+  <span><math><mtext>&#x1D645;</mtext></math>=<span>1D645</span></span>
+  <span><math><mtext>&#x1D646;</mtext></math>=<span>1D646</span></span>
+  <span><math><mtext>&#x1D647;</mtext></math>=<span>1D647</span></span>
+  <span><math><mtext>&#x1D648;</mtext></math>=<span>1D648</span></span>
+  <span><math><mtext>&#x1D649;</mtext></math>=<span>1D649</span></span>
+  <span><math><mtext>&#x1D64A;</mtext></math>=<span>1D64A</span></span>
+  <span><math><mtext>&#x1D64B;</mtext></math>=<span>1D64B</span></span>
+  <span><math><mtext>&#x1D64C;</mtext></math>=<span>1D64C</span></span>
+  <span><math><mtext>&#x1D64D;</mtext></math>=<span>1D64D</span></span><br/>
+  <span><math><mtext>&#x1D64E;</mtext></math>=<span>1D64E</span></span>
+  <span><math><mtext>&#x1D64F;</mtext></math>=<span>1D64F</span></span>
+  <span><math><mtext>&#x1D650;</mtext></math>=<span>1D650</span></span>
+  <span><math><mtext>&#x1D651;</mtext></math>=<span>1D651</span></span>
+  <span><math><mtext>&#x1D652;</mtext></math>=<span>1D652</span></span>
+  <span><math><mtext>&#x1D653;</mtext></math>=<span>1D653</span></span>
+  <span><math><mtext>&#x1D654;</mtext></math>=<span>1D654</span></span>
+  <span><math><mtext>&#x1D655;</mtext></math>=<span>1D655</span></span>
+  <span><math><mtext>&#x1D656;</mtext></math>=<span>1D656</span></span>
+  <span><math><mtext>&#x1D657;</mtext></math>=<span>1D657</span></span><br/>
+  <span><math><mtext>&#x1D658;</mtext></math>=<span>1D658</span></span>
+  <span><math><mtext>&#x1D659;</mtext></math>=<span>1D659</span></span>
+  <span><math><mtext>&#x1D65A;</mtext></math>=<span>1D65A</span></span>
+  <span><math><mtext>&#x1D65B;</mtext></math>=<span>1D65B</span></span>
+  <span><math><mtext>&#x1D65C;</mtext></math>=<span>1D65C</span></span>
+  <span><math><mtext>&#x1D65D;</mtext></math>=<span>1D65D</span></span>
+  <span><math><mtext>&#x1D65E;</mtext></math>=<span>1D65E</span></span>
+  <span><math><mtext>&#x1D65F;</mtext></math>=<span>1D65F</span></span>
+  <span><math><mtext>&#x1D660;</mtext></math>=<span>1D660</span></span>
+  <span><math><mtext>&#x1D661;</mtext></math>=<span>1D661</span></span><br/>
+  <span><math><mtext>&#x1D662;</mtext></math>=<span>1D662</span></span>
+  <span><math><mtext>&#x1D663;</mtext></math>=<span>1D663</span></span>
+  <span><math><mtext>&#x1D664;</mtext></math>=<span>1D664</span></span>
+  <span><math><mtext>&#x1D665;</mtext></math>=<span>1D665</span></span>
+  <span><math><mtext>&#x1D666;</mtext></math>=<span>1D666</span></span>
+  <span><math><mtext>&#x1D667;</mtext></math>=<span>1D667</span></span>
+  <span><math><mtext>&#x1D668;</mtext></math>=<span>1D668</span></span>
+  <span><math><mtext>&#x1D669;</mtext></math>=<span>1D669</span></span>
+  <span><math><mtext>&#x1D66A;</mtext></math>=<span>1D66A</span></span>
+  <span><math><mtext>&#x1D66B;</mtext></math>=<span>1D66B</span></span><br/>
+  <span><math><mtext>&#x1D66C;</mtext></math>=<span>1D66C</span></span>
+  <span><math><mtext>&#x1D66D;</mtext></math>=<span>1D66D</span></span>
+  <span><math><mtext>&#x1D66E;</mtext></math>=<span>1D66E</span></span>
+  <span><math><mtext>&#x1D66F;</mtext></math>=<span>1D66F</span></span>
+  <span><math><mtext>&#x1D790;</mtext></math>=<span>1D790</span></span>
+  <span><math><mtext>&#x1D791;</mtext></math>=<span>1D791</span></span>
+  <span><math><mtext>&#x1D792;</mtext></math>=<span>1D792</span></span>
+  <span><math><mtext>&#x1D793;</mtext></math>=<span>1D793</span></span>
+  <span><math><mtext>&#x1D794;</mtext></math>=<span>1D794</span></span>
+  <span><math><mtext>&#x1D795;</mtext></math>=<span>1D795</span></span><br/>
+  <span><math><mtext>&#x1D796;</mtext></math>=<span>1D796</span></span>
+  <span><math><mtext>&#x1D797;</mtext></math>=<span>1D797</span></span>
+  <span><math><mtext>&#x1D798;</mtext></math>=<span>1D798</span></span>
+  <span><math><mtext>&#x1D799;</mtext></math>=<span>1D799</span></span>
+  <span><math><mtext>&#x1D79A;</mtext></math>=<span>1D79A</span></span>
+  <span><math><mtext>&#x1D79B;</mtext></math>=<span>1D79B</span></span>
+  <span><math><mtext>&#x1D79C;</mtext></math>=<span>1D79C</span></span>
+  <span><math><mtext>&#x1D79D;</mtext></math>=<span>1D79D</span></span>
+  <span><math><mtext>&#x1D79E;</mtext></math>=<span>1D79E</span></span>
+  <span><math><mtext>&#x1D79F;</mtext></math>=<span>1D79F</span></span><br/>
+  <span><math><mtext>&#x1D7A0;</mtext></math>=<span>1D7A0</span></span>
+  <span><math><mtext>&#x1D7A2;</mtext></math>=<span>1D7A2</span></span>
+  <span><math><mtext>&#x1D7A3;</mtext></math>=<span>1D7A3</span></span>
+  <span><math><mtext>&#x1D7A4;</mtext></math>=<span>1D7A4</span></span>
+  <span><math><mtext>&#x1D7A5;</mtext></math>=<span>1D7A5</span></span>
+  <span><math><mtext>&#x1D7A6;</mtext></math>=<span>1D7A6</span></span>
+  <span><math><mtext>&#x1D7A7;</mtext></math>=<span>1D7A7</span></span>
+  <span><math><mtext>&#x1D7A8;</mtext></math>=<span>1D7A8</span></span>
+  <span><math><mtext>&#x1D7AA;</mtext></math>=<span>1D7AA</span></span>
+  <span><math><mtext>&#x1D7AB;</mtext></math>=<span>1D7AB</span></span><br/>
+  <span><math><mtext>&#x1D7AC;</mtext></math>=<span>1D7AC</span></span>
+  <span><math><mtext>&#x1D7AD;</mtext></math>=<span>1D7AD</span></span>
+  <span><math><mtext>&#x1D7AE;</mtext></math>=<span>1D7AE</span></span>
+  <span><math><mtext>&#x1D7AF;</mtext></math>=<span>1D7AF</span></span>
+  <span><math><mtext>&#x1D7B0;</mtext></math>=<span>1D7B0</span></span>
+  <span><math><mtext>&#x1D7B1;</mtext></math>=<span>1D7B1</span></span>
+  <span><math><mtext>&#x1D7B2;</mtext></math>=<span>1D7B2</span></span>
+  <span><math><mtext>&#x1D7B3;</mtext></math>=<span>1D7B3</span></span>
+  <span><math><mtext>&#x1D7B4;</mtext></math>=<span>1D7B4</span></span>
+  <span><math><mtext>&#x1D7B5;</mtext></math>=<span>1D7B5</span></span><br/>
+  <span><math><mtext>&#x1D7B6;</mtext></math>=<span>1D7B6</span></span>
+  <span><math><mtext>&#x1D7B7;</mtext></math>=<span>1D7B7</span></span>
+  <span><math><mtext>&#x1D7B8;</mtext></math>=<span>1D7B8</span></span>
+  <span><math><mtext>&#x1D7B9;</mtext></math>=<span>1D7B9</span></span>
+  <span><math><mtext>&#x1D7BA;</mtext></math>=<span>1D7BA</span></span>
+  <span><math><mtext>&#x1D7BB;</mtext></math>=<span>1D7BB</span></span>
+  <span><math><mtext>&#x1D7BC;</mtext></math>=<span>1D7BC</span></span>
+  <span><math><mtext>&#x1D7BD;</mtext></math>=<span>1D7BD</span></span>
+  <span><math><mtext>&#x1D7BE;</mtext></math>=<span>1D7BE</span></span>
+  <span><math><mtext>&#x1D7BF;</mtext></math>=<span>1D7BF</span></span><br/>
+  <span><math><mtext>&#x1D7C0;</mtext></math>=<span>1D7C0</span></span>
+  <span><math><mtext>&#x1D7C1;</mtext></math>=<span>1D7C1</span></span>
+  <span><math><mtext>&#x1D7C2;</mtext></math>=<span>1D7C2</span></span>
+  <span><math><mtext>&#x1D7C5;</mtext></math>=<span>1D7C5</span></span>
+  <span><math><mtext>&#x1D7C7;</mtext></math>=<span>1D7C7</span></span>
+  <span><math><mtext>&#x1D7C9;</mtext></math>=<span>1D7C9</span></span>
+  <span><math><mtext>&#x1D7C6;</mtext></math>=<span>1D7C6</span></span>
+  <span><math><mtext>&#x1D7C8;</mtext></math>=<span>1D7C8</span></span>
+  <span><math><mtext>&#x1D7A1;</mtext></math>=<span>1D7A1</span></span>
+  <span><math><mtext>&#x1D7C4;</mtext></math>=<span>1D7C4</span></span><br/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html
new file mode 100644
index 0000000..7020736
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-bold-italic.html
@@ -0,0 +1,140 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant sans-serif-bold-italic</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-sans-serif-bold-italic-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a sans-serif-bold-italic mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-sans-serif-bold-italic.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x2202;</mtext></math>=<span>1D7C3</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x2207;</mtext></math>=<span>1D7A9</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x41;</mtext></math>=<span>1D63C</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x42;</mtext></math>=<span>1D63D</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x43;</mtext></math>=<span>1D63E</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x44;</mtext></math>=<span>1D63F</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x45;</mtext></math>=<span>1D640</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x46;</mtext></math>=<span>1D641</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x47;</mtext></math>=<span>1D642</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x48;</mtext></math>=<span>1D643</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x49;</mtext></math>=<span>1D644</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x4A;</mtext></math>=<span>1D645</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x4B;</mtext></math>=<span>1D646</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x4C;</mtext></math>=<span>1D647</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x4D;</mtext></math>=<span>1D648</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x4E;</mtext></math>=<span>1D649</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x4F;</mtext></math>=<span>1D64A</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x50;</mtext></math>=<span>1D64B</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x51;</mtext></math>=<span>1D64C</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x52;</mtext></math>=<span>1D64D</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x53;</mtext></math>=<span>1D64E</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x54;</mtext></math>=<span>1D64F</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x55;</mtext></math>=<span>1D650</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x56;</mtext></math>=<span>1D651</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x57;</mtext></math>=<span>1D652</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x58;</mtext></math>=<span>1D653</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x59;</mtext></math>=<span>1D654</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x5A;</mtext></math>=<span>1D655</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x61;</mtext></math>=<span>1D656</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x62;</mtext></math>=<span>1D657</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x63;</mtext></math>=<span>1D658</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x64;</mtext></math>=<span>1D659</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x65;</mtext></math>=<span>1D65A</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x66;</mtext></math>=<span>1D65B</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x67;</mtext></math>=<span>1D65C</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x68;</mtext></math>=<span>1D65D</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x69;</mtext></math>=<span>1D65E</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x6A;</mtext></math>=<span>1D65F</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x6B;</mtext></math>=<span>1D660</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x6C;</mtext></math>=<span>1D661</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x6D;</mtext></math>=<span>1D662</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x6E;</mtext></math>=<span>1D663</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x6F;</mtext></math>=<span>1D664</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x70;</mtext></math>=<span>1D665</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x71;</mtext></math>=<span>1D666</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x72;</mtext></math>=<span>1D667</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x73;</mtext></math>=<span>1D668</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x74;</mtext></math>=<span>1D669</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x75;</mtext></math>=<span>1D66A</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x76;</mtext></math>=<span>1D66B</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x77;</mtext></math>=<span>1D66C</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x78;</mtext></math>=<span>1D66D</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x79;</mtext></math>=<span>1D66E</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x7A;</mtext></math>=<span>1D66F</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x391;</mtext></math>=<span>1D790</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x392;</mtext></math>=<span>1D791</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x393;</mtext></math>=<span>1D792</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x394;</mtext></math>=<span>1D793</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x395;</mtext></math>=<span>1D794</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x396;</mtext></math>=<span>1D795</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x397;</mtext></math>=<span>1D796</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x398;</mtext></math>=<span>1D797</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x399;</mtext></math>=<span>1D798</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x39A;</mtext></math>=<span>1D799</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x39B;</mtext></math>=<span>1D79A</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x39C;</mtext></math>=<span>1D79B</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x39D;</mtext></math>=<span>1D79C</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x39E;</mtext></math>=<span>1D79D</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x39F;</mtext></math>=<span>1D79E</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3A0;</mtext></math>=<span>1D79F</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3A1;</mtext></math>=<span>1D7A0</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3A3;</mtext></math>=<span>1D7A2</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3A4;</mtext></math>=<span>1D7A3</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3A5;</mtext></math>=<span>1D7A4</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3A6;</mtext></math>=<span>1D7A5</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3A7;</mtext></math>=<span>1D7A6</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3A8;</mtext></math>=<span>1D7A7</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3A9;</mtext></math>=<span>1D7A8</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3B1;</mtext></math>=<span>1D7AA</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3B2;</mtext></math>=<span>1D7AB</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3B3;</mtext></math>=<span>1D7AC</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3B4;</mtext></math>=<span>1D7AD</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3B5;</mtext></math>=<span>1D7AE</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3B6;</mtext></math>=<span>1D7AF</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3B7;</mtext></math>=<span>1D7B0</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3B8;</mtext></math>=<span>1D7B1</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3B9;</mtext></math>=<span>1D7B2</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3BA;</mtext></math>=<span>1D7B3</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3BB;</mtext></math>=<span>1D7B4</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3BC;</mtext></math>=<span>1D7B5</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3BD;</mtext></math>=<span>1D7B6</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3BE;</mtext></math>=<span>1D7B7</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3BF;</mtext></math>=<span>1D7B8</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C0;</mtext></math>=<span>1D7B9</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C1;</mtext></math>=<span>1D7BA</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C2;</mtext></math>=<span>1D7BB</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C3;</mtext></math>=<span>1D7BC</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C4;</mtext></math>=<span>1D7BD</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C5;</mtext></math>=<span>1D7BE</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C6;</mtext></math>=<span>1D7BF</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C7;</mtext></math>=<span>1D7C0</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C8;</mtext></math>=<span>1D7C1</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3C9;</mtext></math>=<span>1D7C2</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3D1;</mtext></math>=<span>1D7C5</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3D5;</mtext></math>=<span>1D7C7</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3D6;</mtext></math>=<span>1D7C9</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3F0;</mtext></math>=<span>1D7C6</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3F1;</mtext></math>=<span>1D7C8</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3F4;</mtext></math>=<span>1D7A1</span></span>
+  <span><math><mtext mathvariant="sans-serif-bold-italic">&#x3F5;</mtext></math>=<span>1D7C4</span></span><br/>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic-ref.html
new file mode 100644
index 0000000..b82cde8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic-ref.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant sans-serif-italic (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-sans-serif-italic.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D608;</mtext></math>=<span>1D608</span></span>
+  <span><math><mtext>&#x1D609;</mtext></math>=<span>1D609</span></span>
+  <span><math><mtext>&#x1D60A;</mtext></math>=<span>1D60A</span></span>
+  <span><math><mtext>&#x1D60B;</mtext></math>=<span>1D60B</span></span>
+  <span><math><mtext>&#x1D60C;</mtext></math>=<span>1D60C</span></span>
+  <span><math><mtext>&#x1D60D;</mtext></math>=<span>1D60D</span></span>
+  <span><math><mtext>&#x1D60E;</mtext></math>=<span>1D60E</span></span>
+  <span><math><mtext>&#x1D60F;</mtext></math>=<span>1D60F</span></span>
+  <span><math><mtext>&#x1D610;</mtext></math>=<span>1D610</span></span>
+  <span><math><mtext>&#x1D611;</mtext></math>=<span>1D611</span></span><br/>
+  <span><math><mtext>&#x1D612;</mtext></math>=<span>1D612</span></span>
+  <span><math><mtext>&#x1D613;</mtext></math>=<span>1D613</span></span>
+  <span><math><mtext>&#x1D614;</mtext></math>=<span>1D614</span></span>
+  <span><math><mtext>&#x1D615;</mtext></math>=<span>1D615</span></span>
+  <span><math><mtext>&#x1D616;</mtext></math>=<span>1D616</span></span>
+  <span><math><mtext>&#x1D617;</mtext></math>=<span>1D617</span></span>
+  <span><math><mtext>&#x1D618;</mtext></math>=<span>1D618</span></span>
+  <span><math><mtext>&#x1D619;</mtext></math>=<span>1D619</span></span>
+  <span><math><mtext>&#x1D61A;</mtext></math>=<span>1D61A</span></span>
+  <span><math><mtext>&#x1D61B;</mtext></math>=<span>1D61B</span></span><br/>
+  <span><math><mtext>&#x1D61C;</mtext></math>=<span>1D61C</span></span>
+  <span><math><mtext>&#x1D61D;</mtext></math>=<span>1D61D</span></span>
+  <span><math><mtext>&#x1D61E;</mtext></math>=<span>1D61E</span></span>
+  <span><math><mtext>&#x1D61F;</mtext></math>=<span>1D61F</span></span>
+  <span><math><mtext>&#x1D620;</mtext></math>=<span>1D620</span></span>
+  <span><math><mtext>&#x1D621;</mtext></math>=<span>1D621</span></span>
+  <span><math><mtext>&#x1D622;</mtext></math>=<span>1D622</span></span>
+  <span><math><mtext>&#x1D623;</mtext></math>=<span>1D623</span></span>
+  <span><math><mtext>&#x1D624;</mtext></math>=<span>1D624</span></span>
+  <span><math><mtext>&#x1D625;</mtext></math>=<span>1D625</span></span><br/>
+  <span><math><mtext>&#x1D626;</mtext></math>=<span>1D626</span></span>
+  <span><math><mtext>&#x1D627;</mtext></math>=<span>1D627</span></span>
+  <span><math><mtext>&#x1D628;</mtext></math>=<span>1D628</span></span>
+  <span><math><mtext>&#x1D629;</mtext></math>=<span>1D629</span></span>
+  <span><math><mtext>&#x1D62A;</mtext></math>=<span>1D62A</span></span>
+  <span><math><mtext>&#x1D62B;</mtext></math>=<span>1D62B</span></span>
+  <span><math><mtext>&#x1D62C;</mtext></math>=<span>1D62C</span></span>
+  <span><math><mtext>&#x1D62D;</mtext></math>=<span>1D62D</span></span>
+  <span><math><mtext>&#x1D62E;</mtext></math>=<span>1D62E</span></span>
+  <span><math><mtext>&#x1D62F;</mtext></math>=<span>1D62F</span></span><br/>
+  <span><math><mtext>&#x1D630;</mtext></math>=<span>1D630</span></span>
+  <span><math><mtext>&#x1D631;</mtext></math>=<span>1D631</span></span>
+  <span><math><mtext>&#x1D632;</mtext></math>=<span>1D632</span></span>
+  <span><math><mtext>&#x1D633;</mtext></math>=<span>1D633</span></span>
+  <span><math><mtext>&#x1D634;</mtext></math>=<span>1D634</span></span>
+  <span><math><mtext>&#x1D635;</mtext></math>=<span>1D635</span></span>
+  <span><math><mtext>&#x1D636;</mtext></math>=<span>1D636</span></span>
+  <span><math><mtext>&#x1D637;</mtext></math>=<span>1D637</span></span>
+  <span><math><mtext>&#x1D638;</mtext></math>=<span>1D638</span></span>
+  <span><math><mtext>&#x1D639;</mtext></math>=<span>1D639</span></span><br/>
+  <span><math><mtext>&#x1D63A;</mtext></math>=<span>1D63A</span></span>
+  <span><math><mtext>&#x1D63B;</mtext></math>=<span>1D63B</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic.html
new file mode 100644
index 0000000..0576236d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-italic.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant sans-serif-italic</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-sans-serif-italic-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a sans-serif-italic mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-sans-serif-italic.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x41;</mtext></math>=<span>1D608</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x42;</mtext></math>=<span>1D609</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x43;</mtext></math>=<span>1D60A</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x44;</mtext></math>=<span>1D60B</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x45;</mtext></math>=<span>1D60C</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x46;</mtext></math>=<span>1D60D</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x47;</mtext></math>=<span>1D60E</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x48;</mtext></math>=<span>1D60F</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x49;</mtext></math>=<span>1D610</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x4A;</mtext></math>=<span>1D611</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x4B;</mtext></math>=<span>1D612</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x4C;</mtext></math>=<span>1D613</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x4D;</mtext></math>=<span>1D614</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x4E;</mtext></math>=<span>1D615</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x4F;</mtext></math>=<span>1D616</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x50;</mtext></math>=<span>1D617</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x51;</mtext></math>=<span>1D618</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x52;</mtext></math>=<span>1D619</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x53;</mtext></math>=<span>1D61A</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x54;</mtext></math>=<span>1D61B</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x55;</mtext></math>=<span>1D61C</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x56;</mtext></math>=<span>1D61D</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x57;</mtext></math>=<span>1D61E</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x58;</mtext></math>=<span>1D61F</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x59;</mtext></math>=<span>1D620</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x5A;</mtext></math>=<span>1D621</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x61;</mtext></math>=<span>1D622</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x62;</mtext></math>=<span>1D623</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x63;</mtext></math>=<span>1D624</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x64;</mtext></math>=<span>1D625</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x65;</mtext></math>=<span>1D626</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x66;</mtext></math>=<span>1D627</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x67;</mtext></math>=<span>1D628</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x68;</mtext></math>=<span>1D629</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x69;</mtext></math>=<span>1D62A</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x6A;</mtext></math>=<span>1D62B</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x6B;</mtext></math>=<span>1D62C</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x6C;</mtext></math>=<span>1D62D</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x6D;</mtext></math>=<span>1D62E</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x6E;</mtext></math>=<span>1D62F</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x6F;</mtext></math>=<span>1D630</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x70;</mtext></math>=<span>1D631</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x71;</mtext></math>=<span>1D632</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x72;</mtext></math>=<span>1D633</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x73;</mtext></math>=<span>1D634</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x74;</mtext></math>=<span>1D635</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x75;</mtext></math>=<span>1D636</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x76;</mtext></math>=<span>1D637</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x77;</mtext></math>=<span>1D638</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x78;</mtext></math>=<span>1D639</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x79;</mtext></math>=<span>1D63A</span></span>
+  <span><math><mtext mathvariant="sans-serif-italic">&#x7A;</mtext></math>=<span>1D63B</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-ref.html
new file mode 100644
index 0000000..35e4b656
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif-ref.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant sans-serif (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-sans-serif.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D7E2;</mtext></math>=<span>1D7E2</span></span>
+  <span><math><mtext>&#x1D7E3;</mtext></math>=<span>1D7E3</span></span>
+  <span><math><mtext>&#x1D7E4;</mtext></math>=<span>1D7E4</span></span>
+  <span><math><mtext>&#x1D7E5;</mtext></math>=<span>1D7E5</span></span>
+  <span><math><mtext>&#x1D7E6;</mtext></math>=<span>1D7E6</span></span>
+  <span><math><mtext>&#x1D7E7;</mtext></math>=<span>1D7E7</span></span>
+  <span><math><mtext>&#x1D7E8;</mtext></math>=<span>1D7E8</span></span>
+  <span><math><mtext>&#x1D7E9;</mtext></math>=<span>1D7E9</span></span>
+  <span><math><mtext>&#x1D7EA;</mtext></math>=<span>1D7EA</span></span>
+  <span><math><mtext>&#x1D7EB;</mtext></math>=<span>1D7EB</span></span><br/>
+  <span><math><mtext>&#x1D5A0;</mtext></math>=<span>1D5A0</span></span>
+  <span><math><mtext>&#x1D5A1;</mtext></math>=<span>1D5A1</span></span>
+  <span><math><mtext>&#x1D5A2;</mtext></math>=<span>1D5A2</span></span>
+  <span><math><mtext>&#x1D5A3;</mtext></math>=<span>1D5A3</span></span>
+  <span><math><mtext>&#x1D5A4;</mtext></math>=<span>1D5A4</span></span>
+  <span><math><mtext>&#x1D5A5;</mtext></math>=<span>1D5A5</span></span>
+  <span><math><mtext>&#x1D5A6;</mtext></math>=<span>1D5A6</span></span>
+  <span><math><mtext>&#x1D5A7;</mtext></math>=<span>1D5A7</span></span>
+  <span><math><mtext>&#x1D5A8;</mtext></math>=<span>1D5A8</span></span>
+  <span><math><mtext>&#x1D5A9;</mtext></math>=<span>1D5A9</span></span><br/>
+  <span><math><mtext>&#x1D5AA;</mtext></math>=<span>1D5AA</span></span>
+  <span><math><mtext>&#x1D5AB;</mtext></math>=<span>1D5AB</span></span>
+  <span><math><mtext>&#x1D5AC;</mtext></math>=<span>1D5AC</span></span>
+  <span><math><mtext>&#x1D5AD;</mtext></math>=<span>1D5AD</span></span>
+  <span><math><mtext>&#x1D5AE;</mtext></math>=<span>1D5AE</span></span>
+  <span><math><mtext>&#x1D5AF;</mtext></math>=<span>1D5AF</span></span>
+  <span><math><mtext>&#x1D5B0;</mtext></math>=<span>1D5B0</span></span>
+  <span><math><mtext>&#x1D5B1;</mtext></math>=<span>1D5B1</span></span>
+  <span><math><mtext>&#x1D5B2;</mtext></math>=<span>1D5B2</span></span>
+  <span><math><mtext>&#x1D5B3;</mtext></math>=<span>1D5B3</span></span><br/>
+  <span><math><mtext>&#x1D5B4;</mtext></math>=<span>1D5B4</span></span>
+  <span><math><mtext>&#x1D5B5;</mtext></math>=<span>1D5B5</span></span>
+  <span><math><mtext>&#x1D5B6;</mtext></math>=<span>1D5B6</span></span>
+  <span><math><mtext>&#x1D5B7;</mtext></math>=<span>1D5B7</span></span>
+  <span><math><mtext>&#x1D5B8;</mtext></math>=<span>1D5B8</span></span>
+  <span><math><mtext>&#x1D5B9;</mtext></math>=<span>1D5B9</span></span>
+  <span><math><mtext>&#x1D5BA;</mtext></math>=<span>1D5BA</span></span>
+  <span><math><mtext>&#x1D5BB;</mtext></math>=<span>1D5BB</span></span>
+  <span><math><mtext>&#x1D5BC;</mtext></math>=<span>1D5BC</span></span>
+  <span><math><mtext>&#x1D5BD;</mtext></math>=<span>1D5BD</span></span><br/>
+  <span><math><mtext>&#x1D5BE;</mtext></math>=<span>1D5BE</span></span>
+  <span><math><mtext>&#x1D5BF;</mtext></math>=<span>1D5BF</span></span>
+  <span><math><mtext>&#x1D5C0;</mtext></math>=<span>1D5C0</span></span>
+  <span><math><mtext>&#x1D5C1;</mtext></math>=<span>1D5C1</span></span>
+  <span><math><mtext>&#x1D5C2;</mtext></math>=<span>1D5C2</span></span>
+  <span><math><mtext>&#x1D5C3;</mtext></math>=<span>1D5C3</span></span>
+  <span><math><mtext>&#x1D5C4;</mtext></math>=<span>1D5C4</span></span>
+  <span><math><mtext>&#x1D5C5;</mtext></math>=<span>1D5C5</span></span>
+  <span><math><mtext>&#x1D5C6;</mtext></math>=<span>1D5C6</span></span>
+  <span><math><mtext>&#x1D5C7;</mtext></math>=<span>1D5C7</span></span><br/>
+  <span><math><mtext>&#x1D5C8;</mtext></math>=<span>1D5C8</span></span>
+  <span><math><mtext>&#x1D5C9;</mtext></math>=<span>1D5C9</span></span>
+  <span><math><mtext>&#x1D5CA;</mtext></math>=<span>1D5CA</span></span>
+  <span><math><mtext>&#x1D5CB;</mtext></math>=<span>1D5CB</span></span>
+  <span><math><mtext>&#x1D5CC;</mtext></math>=<span>1D5CC</span></span>
+  <span><math><mtext>&#x1D5CD;</mtext></math>=<span>1D5CD</span></span>
+  <span><math><mtext>&#x1D5CE;</mtext></math>=<span>1D5CE</span></span>
+  <span><math><mtext>&#x1D5CF;</mtext></math>=<span>1D5CF</span></span>
+  <span><math><mtext>&#x1D5D0;</mtext></math>=<span>1D5D0</span></span>
+  <span><math><mtext>&#x1D5D1;</mtext></math>=<span>1D5D1</span></span><br/>
+  <span><math><mtext>&#x1D5D2;</mtext></math>=<span>1D5D2</span></span>
+  <span><math><mtext>&#x1D5D3;</mtext></math>=<span>1D5D3</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif.html
new file mode 100644
index 0000000..fc880b9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-sans-serif.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant sans-serif</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-sans-serif-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a sans-serif mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-sans-serif.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="sans-serif">&#x30;</mtext></math>=<span>1D7E2</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x31;</mtext></math>=<span>1D7E3</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x32;</mtext></math>=<span>1D7E4</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x33;</mtext></math>=<span>1D7E5</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x34;</mtext></math>=<span>1D7E6</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x35;</mtext></math>=<span>1D7E7</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x36;</mtext></math>=<span>1D7E8</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x37;</mtext></math>=<span>1D7E9</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x38;</mtext></math>=<span>1D7EA</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x39;</mtext></math>=<span>1D7EB</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif">&#x41;</mtext></math>=<span>1D5A0</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x42;</mtext></math>=<span>1D5A1</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x43;</mtext></math>=<span>1D5A2</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x44;</mtext></math>=<span>1D5A3</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x45;</mtext></math>=<span>1D5A4</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x46;</mtext></math>=<span>1D5A5</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x47;</mtext></math>=<span>1D5A6</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x48;</mtext></math>=<span>1D5A7</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x49;</mtext></math>=<span>1D5A8</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x4A;</mtext></math>=<span>1D5A9</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif">&#x4B;</mtext></math>=<span>1D5AA</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x4C;</mtext></math>=<span>1D5AB</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x4D;</mtext></math>=<span>1D5AC</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x4E;</mtext></math>=<span>1D5AD</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x4F;</mtext></math>=<span>1D5AE</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x50;</mtext></math>=<span>1D5AF</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x51;</mtext></math>=<span>1D5B0</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x52;</mtext></math>=<span>1D5B1</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x53;</mtext></math>=<span>1D5B2</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x54;</mtext></math>=<span>1D5B3</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif">&#x55;</mtext></math>=<span>1D5B4</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x56;</mtext></math>=<span>1D5B5</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x57;</mtext></math>=<span>1D5B6</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x58;</mtext></math>=<span>1D5B7</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x59;</mtext></math>=<span>1D5B8</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x5A;</mtext></math>=<span>1D5B9</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x61;</mtext></math>=<span>1D5BA</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x62;</mtext></math>=<span>1D5BB</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x63;</mtext></math>=<span>1D5BC</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x64;</mtext></math>=<span>1D5BD</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif">&#x65;</mtext></math>=<span>1D5BE</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x66;</mtext></math>=<span>1D5BF</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x67;</mtext></math>=<span>1D5C0</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x68;</mtext></math>=<span>1D5C1</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x69;</mtext></math>=<span>1D5C2</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x6A;</mtext></math>=<span>1D5C3</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x6B;</mtext></math>=<span>1D5C4</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x6C;</mtext></math>=<span>1D5C5</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x6D;</mtext></math>=<span>1D5C6</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x6E;</mtext></math>=<span>1D5C7</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif">&#x6F;</mtext></math>=<span>1D5C8</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x70;</mtext></math>=<span>1D5C9</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x71;</mtext></math>=<span>1D5CA</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x72;</mtext></math>=<span>1D5CB</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x73;</mtext></math>=<span>1D5CC</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x74;</mtext></math>=<span>1D5CD</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x75;</mtext></math>=<span>1D5CE</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x76;</mtext></math>=<span>1D5CF</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x77;</mtext></math>=<span>1D5D0</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x78;</mtext></math>=<span>1D5D1</span></span><br/>
+  <span><math><mtext mathvariant="sans-serif">&#x79;</mtext></math>=<span>1D5D2</span></span>
+  <span><math><mtext mathvariant="sans-serif">&#x7A;</mtext></math>=<span>1D5D3</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script-ref.html
new file mode 100644
index 0000000..725268a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script-ref.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant script (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-script.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1D49C;</mtext></math>=<span>1D49C</span></span>
+  <span><math><mtext>&#x212C;</mtext></math>=<span>0212C</span></span>
+  <span><math><mtext>&#x1D49E;</mtext></math>=<span>1D49E</span></span>
+  <span><math><mtext>&#x1D49F;</mtext></math>=<span>1D49F</span></span>
+  <span><math><mtext>&#x2130;</mtext></math>=<span>02130</span></span>
+  <span><math><mtext>&#x2131;</mtext></math>=<span>02131</span></span>
+  <span><math><mtext>&#x1D4A2;</mtext></math>=<span>1D4A2</span></span>
+  <span><math><mtext>&#x210B;</mtext></math>=<span>0210B</span></span>
+  <span><math><mtext>&#x2110;</mtext></math>=<span>02110</span></span>
+  <span><math><mtext>&#x1D4A5;</mtext></math>=<span>1D4A5</span></span><br/>
+  <span><math><mtext>&#x1D4A6;</mtext></math>=<span>1D4A6</span></span>
+  <span><math><mtext>&#x2112;</mtext></math>=<span>02112</span></span>
+  <span><math><mtext>&#x2133;</mtext></math>=<span>02133</span></span>
+  <span><math><mtext>&#x1D4A9;</mtext></math>=<span>1D4A9</span></span>
+  <span><math><mtext>&#x1D4AA;</mtext></math>=<span>1D4AA</span></span>
+  <span><math><mtext>&#x1D4AB;</mtext></math>=<span>1D4AB</span></span>
+  <span><math><mtext>&#x1D4AC;</mtext></math>=<span>1D4AC</span></span>
+  <span><math><mtext>&#x211B;</mtext></math>=<span>0211B</span></span>
+  <span><math><mtext>&#x1D4AE;</mtext></math>=<span>1D4AE</span></span>
+  <span><math><mtext>&#x1D4AF;</mtext></math>=<span>1D4AF</span></span><br/>
+  <span><math><mtext>&#x1D4B0;</mtext></math>=<span>1D4B0</span></span>
+  <span><math><mtext>&#x1D4B1;</mtext></math>=<span>1D4B1</span></span>
+  <span><math><mtext>&#x1D4B2;</mtext></math>=<span>1D4B2</span></span>
+  <span><math><mtext>&#x1D4B3;</mtext></math>=<span>1D4B3</span></span>
+  <span><math><mtext>&#x1D4B4;</mtext></math>=<span>1D4B4</span></span>
+  <span><math><mtext>&#x1D4B5;</mtext></math>=<span>1D4B5</span></span>
+  <span><math><mtext>&#x1D4B6;</mtext></math>=<span>1D4B6</span></span>
+  <span><math><mtext>&#x1D4B7;</mtext></math>=<span>1D4B7</span></span>
+  <span><math><mtext>&#x1D4B8;</mtext></math>=<span>1D4B8</span></span>
+  <span><math><mtext>&#x1D4B9;</mtext></math>=<span>1D4B9</span></span><br/>
+  <span><math><mtext>&#x212F;</mtext></math>=<span>0212F</span></span>
+  <span><math><mtext>&#x1D4BB;</mtext></math>=<span>1D4BB</span></span>
+  <span><math><mtext>&#x210A;</mtext></math>=<span>0210A</span></span>
+  <span><math><mtext>&#x1D4BD;</mtext></math>=<span>1D4BD</span></span>
+  <span><math><mtext>&#x1D4BE;</mtext></math>=<span>1D4BE</span></span>
+  <span><math><mtext>&#x1D4BF;</mtext></math>=<span>1D4BF</span></span>
+  <span><math><mtext>&#x1D4C0;</mtext></math>=<span>1D4C0</span></span>
+  <span><math><mtext>&#x1D4C1;</mtext></math>=<span>1D4C1</span></span>
+  <span><math><mtext>&#x1D4C2;</mtext></math>=<span>1D4C2</span></span>
+  <span><math><mtext>&#x1D4C3;</mtext></math>=<span>1D4C3</span></span><br/>
+  <span><math><mtext>&#x2134;</mtext></math>=<span>02134</span></span>
+  <span><math><mtext>&#x1D4C5;</mtext></math>=<span>1D4C5</span></span>
+  <span><math><mtext>&#x1D4C6;</mtext></math>=<span>1D4C6</span></span>
+  <span><math><mtext>&#x1D4C7;</mtext></math>=<span>1D4C7</span></span>
+  <span><math><mtext>&#x1D4C8;</mtext></math>=<span>1D4C8</span></span>
+  <span><math><mtext>&#x1D4C9;</mtext></math>=<span>1D4C9</span></span>
+  <span><math><mtext>&#x1D4CA;</mtext></math>=<span>1D4CA</span></span>
+  <span><math><mtext>&#x1D4CB;</mtext></math>=<span>1D4CB</span></span>
+  <span><math><mtext>&#x1D4CC;</mtext></math>=<span>1D4CC</span></span>
+  <span><math><mtext>&#x1D4CD;</mtext></math>=<span>1D4CD</span></span><br/>
+  <span><math><mtext>&#x1D4CE;</mtext></math>=<span>1D4CE</span></span>
+  <span><math><mtext>&#x1D4CF;</mtext></math>=<span>1D4CF</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script.html
new file mode 100644
index 0000000..5725ed5
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-script.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant script</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-script-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a script mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-script.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="script">&#x41;</mtext></math>=<span>1D49C</span></span>
+  <span><math><mtext mathvariant="script">&#x42;</mtext></math>=<span>0212C</span></span>
+  <span><math><mtext mathvariant="script">&#x43;</mtext></math>=<span>1D49E</span></span>
+  <span><math><mtext mathvariant="script">&#x44;</mtext></math>=<span>1D49F</span></span>
+  <span><math><mtext mathvariant="script">&#x45;</mtext></math>=<span>02130</span></span>
+  <span><math><mtext mathvariant="script">&#x46;</mtext></math>=<span>02131</span></span>
+  <span><math><mtext mathvariant="script">&#x47;</mtext></math>=<span>1D4A2</span></span>
+  <span><math><mtext mathvariant="script">&#x48;</mtext></math>=<span>0210B</span></span>
+  <span><math><mtext mathvariant="script">&#x49;</mtext></math>=<span>02110</span></span>
+  <span><math><mtext mathvariant="script">&#x4A;</mtext></math>=<span>1D4A5</span></span><br/>
+  <span><math><mtext mathvariant="script">&#x4B;</mtext></math>=<span>1D4A6</span></span>
+  <span><math><mtext mathvariant="script">&#x4C;</mtext></math>=<span>02112</span></span>
+  <span><math><mtext mathvariant="script">&#x4D;</mtext></math>=<span>02133</span></span>
+  <span><math><mtext mathvariant="script">&#x4E;</mtext></math>=<span>1D4A9</span></span>
+  <span><math><mtext mathvariant="script">&#x4F;</mtext></math>=<span>1D4AA</span></span>
+  <span><math><mtext mathvariant="script">&#x50;</mtext></math>=<span>1D4AB</span></span>
+  <span><math><mtext mathvariant="script">&#x51;</mtext></math>=<span>1D4AC</span></span>
+  <span><math><mtext mathvariant="script">&#x52;</mtext></math>=<span>0211B</span></span>
+  <span><math><mtext mathvariant="script">&#x53;</mtext></math>=<span>1D4AE</span></span>
+  <span><math><mtext mathvariant="script">&#x54;</mtext></math>=<span>1D4AF</span></span><br/>
+  <span><math><mtext mathvariant="script">&#x55;</mtext></math>=<span>1D4B0</span></span>
+  <span><math><mtext mathvariant="script">&#x56;</mtext></math>=<span>1D4B1</span></span>
+  <span><math><mtext mathvariant="script">&#x57;</mtext></math>=<span>1D4B2</span></span>
+  <span><math><mtext mathvariant="script">&#x58;</mtext></math>=<span>1D4B3</span></span>
+  <span><math><mtext mathvariant="script">&#x59;</mtext></math>=<span>1D4B4</span></span>
+  <span><math><mtext mathvariant="script">&#x5A;</mtext></math>=<span>1D4B5</span></span>
+  <span><math><mtext mathvariant="script">&#x61;</mtext></math>=<span>1D4B6</span></span>
+  <span><math><mtext mathvariant="script">&#x62;</mtext></math>=<span>1D4B7</span></span>
+  <span><math><mtext mathvariant="script">&#x63;</mtext></math>=<span>1D4B8</span></span>
+  <span><math><mtext mathvariant="script">&#x64;</mtext></math>=<span>1D4B9</span></span><br/>
+  <span><math><mtext mathvariant="script">&#x65;</mtext></math>=<span>0212F</span></span>
+  <span><math><mtext mathvariant="script">&#x66;</mtext></math>=<span>1D4BB</span></span>
+  <span><math><mtext mathvariant="script">&#x67;</mtext></math>=<span>0210A</span></span>
+  <span><math><mtext mathvariant="script">&#x68;</mtext></math>=<span>1D4BD</span></span>
+  <span><math><mtext mathvariant="script">&#x69;</mtext></math>=<span>1D4BE</span></span>
+  <span><math><mtext mathvariant="script">&#x6A;</mtext></math>=<span>1D4BF</span></span>
+  <span><math><mtext mathvariant="script">&#x6B;</mtext></math>=<span>1D4C0</span></span>
+  <span><math><mtext mathvariant="script">&#x6C;</mtext></math>=<span>1D4C1</span></span>
+  <span><math><mtext mathvariant="script">&#x6D;</mtext></math>=<span>1D4C2</span></span>
+  <span><math><mtext mathvariant="script">&#x6E;</mtext></math>=<span>1D4C3</span></span><br/>
+  <span><math><mtext mathvariant="script">&#x6F;</mtext></math>=<span>02134</span></span>
+  <span><math><mtext mathvariant="script">&#x70;</mtext></math>=<span>1D4C5</span></span>
+  <span><math><mtext mathvariant="script">&#x71;</mtext></math>=<span>1D4C6</span></span>
+  <span><math><mtext mathvariant="script">&#x72;</mtext></math>=<span>1D4C7</span></span>
+  <span><math><mtext mathvariant="script">&#x73;</mtext></math>=<span>1D4C8</span></span>
+  <span><math><mtext mathvariant="script">&#x74;</mtext></math>=<span>1D4C9</span></span>
+  <span><math><mtext mathvariant="script">&#x75;</mtext></math>=<span>1D4CA</span></span>
+  <span><math><mtext mathvariant="script">&#x76;</mtext></math>=<span>1D4CB</span></span>
+  <span><math><mtext mathvariant="script">&#x77;</mtext></math>=<span>1D4CC</span></span>
+  <span><math><mtext mathvariant="script">&#x78;</mtext></math>=<span>1D4CD</span></span><br/>
+  <span><math><mtext mathvariant="script">&#x79;</mtext></math>=<span>1D4CE</span></span>
+  <span><math><mtext mathvariant="script">&#x7A;</mtext></math>=<span>1D4CF</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched-ref.html
new file mode 100644
index 0000000..1450e19
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched-ref.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant stretched (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-stretched.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1EE7E;</mtext></math>=<span>1EE7E</span></span>
+  <span><math><mtext>&#x1EE61;</mtext></math>=<span>1EE61</span></span>
+  <span><math><mtext>&#x1EE75;</mtext></math>=<span>1EE75</span></span>
+  <span><math><mtext>&#x1EE76;</mtext></math>=<span>1EE76</span></span>
+  <span><math><mtext>&#x1EE62;</mtext></math>=<span>1EE62</span></span>
+  <span><math><mtext>&#x1EE67;</mtext></math>=<span>1EE67</span></span>
+  <span><math><mtext>&#x1EE77;</mtext></math>=<span>1EE77</span></span>
+  <span><math><mtext>&#x1EE6E;</mtext></math>=<span>1EE6E</span></span>
+  <span><math><mtext>&#x1EE74;</mtext></math>=<span>1EE74</span></span>
+  <span><math><mtext>&#x1EE71;</mtext></math>=<span>1EE71</span></span><br/>
+  <span><math><mtext>&#x1EE79;</mtext></math>=<span>1EE79</span></span>
+  <span><math><mtext>&#x1EE68;</mtext></math>=<span>1EE68</span></span>
+  <span><math><mtext>&#x1EE7A;</mtext></math>=<span>1EE7A</span></span>
+  <span><math><mtext>&#x1EE6F;</mtext></math>=<span>1EE6F</span></span>
+  <span><math><mtext>&#x1EE7B;</mtext></math>=<span>1EE7B</span></span>
+  <span><math><mtext>&#x1EE70;</mtext></math>=<span>1EE70</span></span>
+  <span><math><mtext>&#x1EE72;</mtext></math>=<span>1EE72</span></span>
+  <span><math><mtext>&#x1EE6A;</mtext></math>=<span>1EE6A</span></span>
+  <span><math><mtext>&#x1EE6C;</mtext></math>=<span>1EE6C</span></span>
+  <span><math><mtext>&#x1EE6D;</mtext></math>=<span>1EE6D</span></span><br/>
+  <span><math><mtext>&#x1EE64;</mtext></math>=<span>1EE64</span></span>
+  <span><math><mtext>&#x1EE69;</mtext></math>=<span>1EE69</span></span>
+  <span><math><mtext>&#x1EE7C;</mtext></math>=<span>1EE7C</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched.html
new file mode 100644
index 0000000..603cfe7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-stretched.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant stretched</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-stretched-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a stretched mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-stretched.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="stretched">&#x6A1;</mtext></math>=<span>1EE7E</span></span>
+  <span><math><mtext mathvariant="stretched">&#x628;</mtext></math>=<span>1EE61</span></span>
+  <span><math><mtext mathvariant="stretched">&#x62A;</mtext></math>=<span>1EE75</span></span>
+  <span><math><mtext mathvariant="stretched">&#x62B;</mtext></math>=<span>1EE76</span></span>
+  <span><math><mtext mathvariant="stretched">&#x62C;</mtext></math>=<span>1EE62</span></span>
+  <span><math><mtext mathvariant="stretched">&#x62D;</mtext></math>=<span>1EE67</span></span>
+  <span><math><mtext mathvariant="stretched">&#x62E;</mtext></math>=<span>1EE77</span></span>
+  <span><math><mtext mathvariant="stretched">&#x633;</mtext></math>=<span>1EE6E</span></span>
+  <span><math><mtext mathvariant="stretched">&#x634;</mtext></math>=<span>1EE74</span></span>
+  <span><math><mtext mathvariant="stretched">&#x635;</mtext></math>=<span>1EE71</span></span><br/>
+  <span><math><mtext mathvariant="stretched">&#x636;</mtext></math>=<span>1EE79</span></span>
+  <span><math><mtext mathvariant="stretched">&#x637;</mtext></math>=<span>1EE68</span></span>
+  <span><math><mtext mathvariant="stretched">&#x638;</mtext></math>=<span>1EE7A</span></span>
+  <span><math><mtext mathvariant="stretched">&#x639;</mtext></math>=<span>1EE6F</span></span>
+  <span><math><mtext mathvariant="stretched">&#x63A;</mtext></math>=<span>1EE7B</span></span>
+  <span><math><mtext mathvariant="stretched">&#x641;</mtext></math>=<span>1EE70</span></span>
+  <span><math><mtext mathvariant="stretched">&#x642;</mtext></math>=<span>1EE72</span></span>
+  <span><math><mtext mathvariant="stretched">&#x643;</mtext></math>=<span>1EE6A</span></span>
+  <span><math><mtext mathvariant="stretched">&#x645;</mtext></math>=<span>1EE6C</span></span>
+  <span><math><mtext mathvariant="stretched">&#x646;</mtext></math>=<span>1EE6D</span></span><br/>
+  <span><math><mtext mathvariant="stretched">&#x647;</mtext></math>=<span>1EE64</span></span>
+  <span><math><mtext mathvariant="stretched">&#x64A;</mtext></math>=<span>1EE69</span></span>
+  <span><math><mtext mathvariant="stretched">&#x66E;</mtext></math>=<span>1EE7C</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed-ref.html
new file mode 100644
index 0000000..b8e6160fe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant tailed (reference)</title>
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-tailed.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext>&#x1EE52;</mtext></math>=<span>1EE52</span></span>
+  <span><math><mtext>&#x1EE4B;</mtext></math>=<span>1EE4B</span></span>
+  <span><math><mtext>&#x1EE4D;</mtext></math>=<span>1EE4D</span></span>
+  <span><math><mtext>&#x1EE49;</mtext></math>=<span>1EE49</span></span>
+  <span><math><mtext>&#x1EE42;</mtext></math>=<span>1EE42</span></span>
+  <span><math><mtext>&#x1EE47;</mtext></math>=<span>1EE47</span></span>
+  <span><math><mtext>&#x1EE57;</mtext></math>=<span>1EE57</span></span>
+  <span><math><mtext>&#x1EE5F;</mtext></math>=<span>1EE5F</span></span>
+  <span><math><mtext>&#x1EE4E;</mtext></math>=<span>1EE4E</span></span>
+  <span><math><mtext>&#x1EE54;</mtext></math>=<span>1EE54</span></span><br/>
+  <span><math><mtext>&#x1EE51;</mtext></math>=<span>1EE51</span></span>
+  <span><math><mtext>&#x1EE59;</mtext></math>=<span>1EE59</span></span>
+  <span><math><mtext>&#x1EE4F;</mtext></math>=<span>1EE4F</span></span>
+  <span><math><mtext>&#x1EE5B;</mtext></math>=<span>1EE5B</span></span>
+  <span><math><mtext>&#x1EE5D;</mtext></math>=<span>1EE5D</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed.html
new file mode 100644
index 0000000..c00417d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/mathvariant-tailed.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>mathvariant tailed</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>
+<link rel="match" href="mathvariant-tailed-ref.html"/>
+<meta name="assert" content="Verify that a single-char <mtext> with a tailed mathvariant is equivalent to an <mtext> with the transformed unicode character.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/mathvariant-tailed.woff");
+  }
+  body > span {
+    padding: 10px;
+  }
+  span > span {
+    font-family: monospace;
+    font-size: 10px;
+  }
+  math {
+    font-family: TestFont;
+    font-size: 10px;
+  }
+</style>
+<body>
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->
+  <p>Test passes if all the equalities below are true.</p>
+  <span><math><mtext mathvariant="tailed">&#x642;</mtext></math>=<span>1EE52</span></span>
+  <span><math><mtext mathvariant="tailed">&#x644;</mtext></math>=<span>1EE4B</span></span>
+  <span><math><mtext mathvariant="tailed">&#x646;</mtext></math>=<span>1EE4D</span></span>
+  <span><math><mtext mathvariant="tailed">&#x64A;</mtext></math>=<span>1EE49</span></span>
+  <span><math><mtext mathvariant="tailed">&#x62C;</mtext></math>=<span>1EE42</span></span>
+  <span><math><mtext mathvariant="tailed">&#x62D;</mtext></math>=<span>1EE47</span></span>
+  <span><math><mtext mathvariant="tailed">&#x62E;</mtext></math>=<span>1EE57</span></span>
+  <span><math><mtext mathvariant="tailed">&#x66F;</mtext></math>=<span>1EE5F</span></span>
+  <span><math><mtext mathvariant="tailed">&#x633;</mtext></math>=<span>1EE4E</span></span>
+  <span><math><mtext mathvariant="tailed">&#x634;</mtext></math>=<span>1EE54</span></span><br/>
+  <span><math><mtext mathvariant="tailed">&#x635;</mtext></math>=<span>1EE51</span></span>
+  <span><math><mtext mathvariant="tailed">&#x636;</mtext></math>=<span>1EE59</span></span>
+  <span><math><mtext mathvariant="tailed">&#x639;</mtext></math>=<span>1EE4F</span></span>
+  <span><math><mtext mathvariant="tailed">&#x63A;</mtext></math>=<span>1EE5B</span></span>
+  <span><math><mtext mathvariant="tailed">&#x6BA;</mtext></math>=<span>1EE5D</span></span>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1-ref.html
new file mode 100644
index 0000000..fcaf5fe
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>visibility (reference)</title>
+</head>
+<body>
+  <p>Test passes if you see a green square.</p>
+  <div style="background: green; width: 200px; height: 200px;">
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1.html
new file mode 100644
index 0000000..be8da3b0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/css-styling/visibility-1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>visibility</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1">
+<link rel="match" href="visibility-1-ref.html"/>
+<meta name="assert" content="Verify that visibility=hidden is used for text and graphical elements.">
+</head>
+<body>
+  <p>Test passes if you see a green square.</p>
+  <div style="background: green; color: red; width: 200px; height: 200px;">
+    <math><mfrac style="visibility: hidden"><mn>1</mn><mn>2</mn></mfrac></math>
+    <math><msqrt style="visibility: hidden"><mn>3</mn></msqrt></math>
+    <math><mroot style="visibility: hidden"><mn>4</mn><mn>5</mn></mroot></math>
+    <math><menclose notation="left" style="visibility: hidden"><mn>6</mn></menclose></math>
+    <math><menclose notation="right" style="visibility: hidden"><mn>7</mn></menclose></math>
+    <math><menclose notation="top" style="visibility: hidden"><mn>8</mn></menclose></math>
+    <math><menclose notation="bottom" style="visibility: hidden"><mn>9</mn></menclose></math>
+    <math><menclose notation="box" style="visibility: hidden"><mn>10</mn></menclose></math>
+    <math><menclose notation="roundedbox" style="visibility: hidden"><mn>11</mn></menclose></math>
+    <math><menclose notation="actuarial" style="visibility: hidden"><mn>12</mn></menclose></math>
+    <math><menclose notation="madruwb" style="visibility: hidden"><mn>13</mn></menclose></math>
+    <math><menclose notation="horizontalstrike" style="visibility: hidden"><mn>14</mn></menclose></math>
+    <math><menclose notation="verticalstrike" style="visibility: hidden"><mn>15</mn></menclose></math>
+    <math><menclose notation="updiagonalstrike" style="visibility: hidden"><mn>16</mn></menclose></math>
+    <math><menclose notation="downdiagonalstrike" style="visibility: hidden"><mn>17</mn></menclose></math>
+    <math><menclose notation="longdiv" style="visibility: hidden"><mn>18</mn></menclose></math>
+    <math><menclose notation="circle" style="visibility: hidden"><mn>19</mn></menclose></math>
+    <math><mi style="visibility: hidden">20</mi></math>
+    <math><mn style="visibility: hidden">21</mn></math>
+    <math><mo style="visibility: hidden">22</mo></math>
+    <math><mtext style="visibility: hidden">23</mtext></math>
+    <math><ms style="visibility: hidden">24</ms></math>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1-ref.html
new file mode 100644
index 0000000..5afa59e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Class (reference)</title>
+</head>
+<body>
+
+  <p>Test passes if you see the text "PASS".</p>
+  <math>
+    <mtext style="background: green; color: white;">PASS</mtext>
+  </math>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1.html
new file mode 100644
index 0000000..65485232
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Class</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2"/>
+<link rel="match" href="class-1-ref.html"/>
+<meta name="assert" content="Verify that the class attribute affects CSS selectors.">
+<style>
+  mtext.fail { display: none; }
+  mtext.pass { background: green; }
+</style>
+</head>
+<body>
+
+  <p>Test passes if you see the text "PASS".</p>
+  <math>
+    <mtext class="fail" style="background: red; color: white;">FAIL</mtext>
+    <mtext class="pass" style="color: white;">PASS</mtext>
+  </math>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-2.html
new file mode 100644
index 0000000..9dbede8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/class-2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Class</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2">
+<meta name="assert" content="Verify whether the getElementsByClassName() works for MathML elements.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  setup({ explicit_done: true });
+  window.addEventListener("DOMContentLoaded", function() {
+    var mtext = document.getElementsByClassName("cl");
+    test(function() {
+      assert_equals(mtext.length, 3);
+      var mtext_ref = document.body.firstElementChild.firstElementChild;
+      mtext_ref = mtext_ref.nextElementSibling.nextElementSibling
+      assert_equals(mtext[0], mtext_ref);
+      mtext_ref = mtext_ref.nextElementSibling.nextElementSibling;
+      assert_equals(mtext[1], mtext_ref);
+      mtext_ref = mtext_ref.nextElementSibling.nextElementSibling;
+      assert_equals(mtext[2], mtext_ref);
+    }, "getElementsByClassName()");
+    done();
+  });
+</script>
+</head>
+<body>
+  <math>
+    <mtext class="cl_"></mtext>
+    <mtext class="c"></mtext>
+    <mtext class="cl"></mtext>
+    <mtext class="cl_"></mtext>
+    <mtext class="cl"></mtext>
+    <mtext class="c"></mtext>
+    <mtext class="cl"></mtext>
+    <mtext class="cl_"></mtext>
+  </math>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1-ref.html
new file mode 100644
index 0000000..71ee8ce
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Color Attributes (reference)</title>
+<style>
+  #content > div {
+    position: absolute;
+  }
+</style>
+</head>
+<body>
+
+  <p>Test passes if you see the text below is written in white on a green
+    background.</p>
+
+  <div id="content">
+    <div>
+      <math style="background: green;">
+        <mtext style="color: white;">Hello World!</mtext>
+      </math>
+    </div>
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1.html
new file mode 100644
index 0000000..b7bdf72
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/color-attributes-1.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Color Attributes</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2"/>
+<meta name="assert" content="Verify that the mathcolor and mathbackground attributes are supported on the math element.">
+<link rel="match" href="color-attributes-1-ref.html"/>
+<style>
+  #content {
+    color: red;
+  }
+  #content > div {
+    position: absolute;
+  }
+</style>
+</head>
+<body>
+
+  <p>Test passes if you see the text below is written in white on a green
+    background.</p>
+
+  <div id="content">
+    <div>
+      <math style="background: red;">
+        <mtext style="visibility: hidden;">Hello World!</mtext>
+      </math>
+    </div>
+    <div>
+      <math mathcolor="white" mathbackground="green">
+        <mtext>Hello World!</mtext>
+      </math>
+    </div>
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/display-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/display-1.html
new file mode 100644
index 0000000..4eb302c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/display-1.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML display attribute</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2">
+<meta name="assert" content="Verify that the display attribute on the math element is supported and impacts centering and line breaking with surrounding content.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  function getBox(aId) {
+    return document.getElementById(aId).getBoundingClientRect();
+  }
+  window.addEventListener("DOMContentLoaded", function() {
+    var content = getBox("content");
+
+    var before_block = getBox("before_block");
+    var mspace_block = getBox("mspace_block");
+    var after_block = getBox("after_block");
+    test(function() {
+      assert_approx_equals(before_block.left, content.left, 1,
+                           "content before must be left aligned");
+      assert_approx_equals((mspace_block.left + mspace_block.right) / 2,
+                           (content.left + content.right) / 2,
+                           1,
+                           "math must be centered.");
+      assert_approx_equals(after_block.left, content.left, 1,
+                           "content before must be left aligned");
+      assert_less_than_equal(before_block.bottom, mspace_block.top,
+                            "new line before math");
+      assert_less_than_equal(mspace_block.bottom, after_block.top,
+                            "new line after math");
+    }, "Test display math");
+
+    var before_inline = getBox("before_inline");
+    var mspace_inline = getBox("mspace_inline");
+    var after_inline = getBox("after_inline");
+    test(function() {
+      assert_approx_equals((before_inline.top + before_inline.bottom) / 2,
+                           (mspace_inline.top + mspace_inline.bottom) / 2,
+                           1,
+                           "content before must be horizontally aligned with math");
+      assert_approx_equals((after_inline.top + after_inline.bottom) / 2,
+                           (mspace_inline.top + mspace_inline.bottom) / 2,
+                           1,
+                           "content after must be horizontally aligned with math");
+      assert_less_than_equal(before_inline.right, mspace_inline.left,
+                            "content before must be on the left of math");
+      assert_less_than_equal(mspace_inline.right, after_inline.left,
+                            "content after must be on the right of math");
+    }, "Test inline math");
+
+    done();
+  });
+</script>
+<style>
+  #content {
+    width: 600px;
+    background: #ccc;
+  }
+  span.square {
+    display: inline-block;
+    width: 50px;
+    height: 50px;
+    background: black;
+  }
+  mspace {
+    background: black;
+  }
+</style>
+</head>
+<body>
+  <div id="content">
+    <span id="before_block" class="square"></span>
+    <math display="block"><mspace id="mspace_block" width="50px" height="50px"/></math>
+    <span id="after_block" class="square"></span>
+    <br/>
+    <span id="before_inline" class="square"></span>
+    <math display="inline"><mspace id="mspace_inline" width="50px" height="50px"/></math>
+    <span id="after_inline" class="square"></span>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1-ref.html
new file mode 100644
index 0000000..5fa90e9d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Dynamic MathML DOM (reference)</title>
+<style>
+  mtext.pass { background: green; color: white; }
+</style>
+</head>
+<body>
+  <p>Test passes if you see the text "PASS".</p>
+  <math>
+    <mtext class="pass">PASS</mtext>
+  </math>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1.html
new file mode 100644
index 0000000..ab22ad1
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/dynamic-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Dynamic MathML DOM</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS1"/>
+<link rel="match" href="dynamic-1-ref.html"/>
+<meta name="assert" content="Verify that the MathML DOM tree can be modified via javascript and that the rendering is correctly updated.">
+<style>
+  mtext.fail { background: red; color: white; }
+  mtext.pass { background: green; color: white; }
+</style>
+<script>
+  window.addEventListener("DOMContentLoaded", function() {
+    var kMathMLNamespace = "http://www.w3.org/1998/Math/MathML";
+    var mtext = document.createElementNS(kMathMLNamespace, "mtext");
+    mtext.setAttribute("class", "pass");
+    mtext.textContent = "PASS";
+    var math = document.getElementsByTagNameNS(kMathMLNamespace, "math")[0];
+    math.replaceChild(mtext, math.firstElementChild);
+  });
+</script>
+</head>
+<body>
+  <p>Test passes if you see the text "PASS".</p>
+  <math>
+    <mtext class="fail">FAIL</mtext>
+  </math>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1-ref.html
new file mode 100644
index 0000000..8695256
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>href click (reference)</title>
+</head>
+<body>
+
+  <p>This test passes if you see a green square.</p>
+
+  <div style="width: 150px; height: 150px; background: green">
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1.html
new file mode 100644
index 0000000..80e4c75
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="utf-8"/>
+<title>href click</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2">
+<link rel="match" href="href-click-1-ref.html"/>
+<meta name="assert" content="Verify that a click on a link moves to the target.">
+<script type="text/javascript">
+  function test()
+  {
+    var event = new MouseEvent('click', {bubbles: true, cancelable: true});
+    document.getElementById('link').dispatchEvent(event);
+    document.documentElement.className = "";
+  }
+</script>
+</head>
+<body onload="test()">
+
+  <p>This test passes if you see a green square.</p>
+
+  <div style="width: 150px; height: 150px; overflow: hidden">
+    <math>
+      <mrow id="link" href="#target">
+        <mspace id="space" width="150px" height="150px" mathbackground="red"/>
+      </mrow>
+    </math>
+    <div style="height: 500px;"></div>
+    <math id="target">
+      <mspace width="150px" height="150px" mathbackground="green"/>
+    </math>
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2-ref.html
new file mode 100644
index 0000000..8695256
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>href click (reference)</title>
+</head>
+<body>
+
+  <p>This test passes if you see a green square.</p>
+
+  <div style="width: 150px; height: 150px; background: green">
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2.html
new file mode 100644
index 0000000..1e41f77c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-click-2.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="utf-8"/>
+<title>href click</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2">
+<link rel="match" href="href-click-2-ref.html"/>
+<meta name="assert" content="Verify that a click on an element bubbles to an ancestor link.">
+<script type="text/javascript">
+  function test()
+  {
+    var event = new MouseEvent('click', {bubbles: true, cancelable: true});
+    document.getElementById('space').dispatchEvent(event);
+    document.documentElement.className = "";
+  }
+</script>
+</head>
+<body onload="test()">
+
+  <p>This test passes if you see a green square.</p>
+
+  <div style="width: 150px; height: 150px; overflow: hidden">
+    <math>
+      <mrow href="#target">
+        <mrow>
+          <mrow>
+            <mspace id="space" width="150px" height="150px" mathbackground="red"/>
+          </mrow>
+        </mrow>
+      </mrow>
+    </math>
+    <div style="height: 500px;"></div>
+    <math id="target">
+      <mspace width="150px" height="150px" mathbackground="green"/>
+    </math>
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-manual.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-manual.html
new file mode 100644
index 0000000..7f24f20f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/href-manual.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Manual click on a link</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({explicit_timeout: true})
+function handler() {
+document.body.insertAdjacentHTML("beforeend",
+  "<span style='background: green; color: white;'>PASS</span>");
+}
+</script>
+</head>
+<body>
+  <p>Click on the one asterisk which is a link. If a "PASS" result appears the
+    test passes, otherwise it fails.</p>
+  <p>
+    <math style="font-size: 3em;">
+      <mtext>*****</mtext>
+      <mtext href="javascript:handler()">*</mtext>
+      <mtext>*****</mtext>
+    </math>
+  </p>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1-ref.html
new file mode 100644
index 0000000..4987754
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML inside foreignObject (reference)</title>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+  <div>
+    <div style="position: absolute; width: 200px; height: 200px; background: green"></div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1.html
new file mode 100644
index 0000000..b3ab19c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML inside foreignObject</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS1"/>
+<link rel="match" href="integration-point-1-ref.html"/>
+<meta name="assert" content="Verify that MathML can be used inside a foreignObject element.">
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+  <div>
+    <div style="position: absolute; width: 200px; height: 200px; background: red"></div>
+    <div style="position: absolute;">
+      <svg width="200px" height="200px">
+        <rect width="200px" height="100px" fill="red"/>
+        <foreignObject width="200px" height="200px"
+                       requiredExtensions="http://www.w3.org/1998/Math/MathML">
+          <div style="width: 200px; height: 200px; background: green"></div>
+        </foreignObject>
+      </svg>
+    </div>
+  </div>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2-ref.html
new file mode 100644
index 0000000..33c4b7e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML as a phrasing content (reference)</title>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+
+  <div>
+    <div style="position: absolute; background: green; width: 300px; height: 300px;"></div>
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2.html
new file mode 100644
index 0000000..e970f9ec
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-2.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>MathML as a phrasing content</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS1"/>
+<link rel="match" href="integration-point-2-ref.html"/>
+<meta name="assert" content="Verify that MathML can be used at positions where phrasing content is accepted.">
+<style type="text/css">
+  span, math { font-family: monospace; font-size: 10px; }
+  div {
+    color: green;
+  }
+  span.redsquare {
+    display: inline-block;
+    width: 10px;
+    height: 10px;
+    background: red;
+  }
+  mspace {
+    background: green;
+  }
+</style>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+
+  <div>
+
+    <div style="position: absolute; background: green; width: 300px; height: 300px;">
+      <p><span class="redsquare"></span></p>
+      <h1><span class="redsquare"></span></h1>
+      <h2><span class="redsquare"></span></h2>
+      <ul>
+        <li><span class="redsquare"></span></li>
+      </ul>
+      <ol>
+        <li><span class="redsquare"></span></li>
+      </ol>
+      <table><tr><td><span class="redsquare"></span></td></tr></table>
+      <a href="#id"><span class="redsquare"></span></a>
+      <em><span class="redsquare"></span></em>
+      <strong><span class="redsquare"></span></strong>
+      <small><span class="redsquare"></span></small>
+      <span><span class="redsquare"></span></span>
+      <u><span class="redsquare"></span></u>
+      <q><span class="redsquare"></span></q>
+    </div>
+    <div style="position: absolute; width: 400px;">
+      <p><math><mspace width="10px" height="10px"/></math></p>
+      <h1><math><mspace width="10px" height="10px"/></math></h1>
+      <h2><math><mspace width="10px" height="10px"/></math></h2>
+      <ul>
+        <li><math><mspace width="10px" height="10px"/></math></li>
+      </ul>
+      <ol>
+        <li><math><mspace width="10px" height="10px"/></math></li>
+      </ol>
+      <table><tr><td><math><mspace width="10px" height="10px"/></math></td></tr></table>
+      <a href="#id"><math><mspace width="10px" height="10px"/></math></a>
+      <em><math><mspace width="10px" height="10px"/></math></em>
+      <strong><math><mspace width="10px" height="10px"/></math></strong>
+      <small><math><mspace width="10px" height="10px"/></math></small>
+      <span><math><mspace width="10px" height="10px"/></math></span>
+      <u><math><mspace width="10px" height="10px"/></math></u>
+      <q><math><mspace width="10px" height="10px"/></math></q>
+    </div>
+
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3-ref.html
new file mode 100644
index 0000000..8362ed28
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>phrasing content inside mtext (reference)</title>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+
+  <div>
+
+    <div style="position: absolute; background: green; width: 200px; height: 200px;"></div>
+
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3.html
new file mode 100644
index 0000000..8132acc6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/integration-point-3.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>phrasing content inside mtext</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS1"/>
+<link rel="match" href="integration-point-3-ref.html"/>
+<meta name="assert" content="Verify that <mtext> can contain phrasing content">
+<style type="text/css">
+  div {
+    color: green;
+  }
+  span, math, mtext { font-family: monospace; font-size: 10px; }
+  span.redsquare, span.greensquare {
+    display: inline-block;
+    width: 10px;
+    height: 10px;
+  }
+  span.redsquare {
+    background: red;
+  }
+  span.greensquare {
+    background: green;
+  }
+  mspace {
+    background: green;
+  }
+</style>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+
+  <div>
+
+    <div style="position: absolute; background: green; width: 200px; height: 200px;">
+      <span><span><span><span class="redsquare"></span></span></span></span>
+      <span><span><u><span class="redsquare"></span></u></span></span>
+      <span><span><a href="#id"><span class="redsquare"></span></a></span></span>
+      <span><span><del><span class="redsquare"></span></del></span></span>
+      <span><span><em><span class="redsquare"></span></em></span></span>
+      <span><span><strong><span class="redsquare"></span></strong></span></span>
+      <span><span><q><span class="redsquare"></span></q></span></span>
+      <span><span><small><span class="redsquare"></span></small></span></span>
+      <span><span><var><span class="redsquare"></span></var></span></span>
+      <span><span><samp><span class="redsquare"></span></samp></span></span>
+    </div>
+    <div style="position: absolute; width: 200px;">
+      <math><mtext><span><span class="greensquare"></span></span></mtext></math>
+      <math><mtext><u><span class="greensquare"></span></u></mtext></math>
+      <math><mtext><a href="#id"><span class="greensquare"></span></a></mtext></math>
+      <math><mtext><del><span class="greensquare"></span></del></mtext></math>
+      <math><mtext><em><span class="greensquare"></span></em></mtext></math>
+      <math><mtext><strong><span class="greensquare"></span></strong></mtext></math>
+      <math><mtext><q><span class="greensquare"></span></q></mtext></math>
+      <math><mtext><small><span class="greensquare"></span></small></mtext></math>
+      <math><mtext><var><span class="greensquare"></span></var></mtext></math>
+      <math><mtext><samp><span class="greensquare"></span></samp></mtext></math>
+    </div>
+
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-1.html
new file mode 100644
index 0000000..795f516
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>SVG requiredExtensions</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1">
+<meta name="assert" content="Verify whether the MathML namespace is recognized as a required extensions of SVG foreignObject when using the hasExtension javascript function.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  test(function() {
+  assert_true(document.createElementNS("http://www.w3.org/2000/svg", "foreignObject").hasExtension("http://www.w3.org/1998/Math/MathML"))
+  }, "Testing foreignObject.hasExtension('http://www.w3.org/1998/Math/MathML')");
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2-ref.html
new file mode 100644
index 0000000..dcc5b2b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>SVG requiredExtensions (reference)</title>
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+  <svg width="200px" height="200px">
+    <rect width="200px" height="200px" fill="green"/>
+  </svg>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2.html
new file mode 100644
index 0000000..b49bdda
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/required-extensions-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>SVG requiredExtensions</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS1"/>
+<link rel="match" href="required-extensions-2-ref.html"/>
+<meta name="assert" content="Verify that a foreignObject with MathML used as a requiredExtensions value is selected for display in a SVG switch element.">
+</head>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+  <svg width="200px" height="200px">
+    <rect width="200px" height="100px" fill="red"/>
+    <switch>
+      <foreignObject width="200px" height="200px"
+                     requiredExtensions="http://www.w3.org/1998/Math/MathML">
+        <div style="width: 200px; height: 200px; background: green"></div>
+      </foreignObject>
+      <rect y="100px" width="200px" height="100px" fill="red"/>
+    </switch>
+  </svg>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1-iframe-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1-iframe-1.html
new file mode 100644
index 0000000..6b3ab07
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1-iframe-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Unique Identifier (iframe)</title>
+</head>
+<body>
+
+  <div style="width: 100px; height: 500px;">
+    <math><mtext style="background: red; color: white;">FAIL</mtext></math>
+  </div>
+  <div style="width: 100px; height: 500px;">
+    <math><mtext style="background: green; color: white;"
+                 id="PASS">PASS</mtext></math>
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1-iframe-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1-iframe-2.html
new file mode 100644
index 0000000..ade0110
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1-iframe-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Unique Identifier (iframe reference)</title>
+</head>
+<body>
+
+  <div style="width: 100px; height: 500px;">
+    <math><mtext style="background: green; color: white;"
+                 id="PASS" class="pass">PASS</mtext></math>
+  </div>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1-ref.html
new file mode 100644
index 0000000..a219b2c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Unique identifier (reference)</title>
+</head>
+<body>
+
+  <p>Test passes if you see the text "PASS".</p>
+
+  <iframe width="100" height="100" frameborder="0" scrolling="no"
+          marginheight="0" marginwidth="0"
+          src="unique-identifier-1-iframe-2.html#PASS"></iframe>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1.html
new file mode 100644
index 0000000..39c49a6b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Unique identifier</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2"/>
+<link rel="match" href="unique-identifier-1-ref.html"/>
+<meta name="assert" content="Verify that the id on a MathML element can be used as a fragment identifier in order to force initial scrolling.">
+</head>
+<body>
+
+  <p>Test passes if you see the text "PASS".</p>
+
+  <iframe width="100" height="100" frameborder="0" scrolling="no"
+          marginheight="0" marginwidth="0"
+          src="unique-identifier-1-iframe-1.html#PASS"></iframe>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-2.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-2.html
new file mode 100644
index 0000000..b3226c29
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Unique Identifier</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2">
+<meta name="assert" content="Verify whether the getElementById() works for MathML elements.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+  setup({ explicit_done: true });
+  window.addEventListener("DOMContentLoaded", function() {
+    var mtext = document.getElementById("MTEXT");
+    test(function() {
+      assert_equals(mtext, document.body.firstElementChild.lastElementChild);
+    }, "getElementById()");
+    done();
+  });
+</script>
+</head>
+<body>
+  <math>
+    <mtext id="MTEXT_"></mtext>
+    <mtext id="MTEX"></mtext>
+    <mtext id="MTEXT"></mtext>
+  </math>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3-ref.html
new file mode 100644
index 0000000..ef056e00
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Unique identifier 3 (reference)</title>
+</head>
+<body>
+
+  <p>Test passes if you see the text "PASS".</p>
+  <math>
+    <mtext style="background: green; color: white;">PASS</mtext>
+  </math>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3.html b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3.html
new file mode 100644
index 0000000..5d66e13
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/html5-tree/unique-identifier-3.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<title>Unique Identifier</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS1.SSS2"/>
+<link rel="match" href="unique-identifier-3-ref.html"/>
+<meta name="assert" content="Verify that the id attribute affects CSS selectors.">
+<style>
+  #fail { display: none; }
+  #pass { background: green; }
+</style>
+</head>
+<body>
+
+  <p>Test passes if you see the text "PASS".</p>
+  <math>
+    <mtext id="fail" style="background: red; color: white;">FAIL</mtext>
+    <mtext id="pass" style="color: white;">PASS</mtext>
+  </math>
+
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1-ref.html b/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1-ref.html
new file mode 100644
index 0000000..3f7f7640
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<title>Open Font Format: USE_TYPO_METRICS (reference)</title>
+<style>
+  #green {
+     position: absolute;
+     background: green;
+     left: 10px;
+     width: 230px;
+     height: 230px;
+  }
+</style>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+
+  <div>
+    <div id="green"></div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1.html b/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1.html
new file mode 100644
index 0000000..081d66b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/relations/text-and-math/use-typo-metrics-1.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta charset="utf-8"/>
+<title>Open Font Format: USE_TYPO_METRICS</title>
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS2.SSS1"/>
+<link rel="match" href="use-typo-metrics-1-ref.html"/>
+<meta name="assert" content="Verify that the USE_TYPO_METRICS flag from the OS/2 table is taken into account to calculate line height.">
+<style>
+  @font-face {
+    font-family: TestFont;
+    src: url("/fonts/math/lineheight5000-typolineheight2300.woff");
+  }
+  .green {
+     position: absolute;
+     background: green;
+     width: 115px;
+  }
+  .red {
+     position: absolute;
+     background: red;
+     width: 115px;
+  }
+  .forceHeight {
+    height: 230px;
+  }
+  .leftSide {
+    left: 10px;
+  }
+  .rightSide {
+    left: 125px;
+  }
+  span {
+    /* em=1000, lineHeight=5000, typoLineHeight=2300 and font-size=100px
+       implies typoLineHeightPx = 230px < 500px = lineHeightPx */
+     font-family: TestFont;
+     font-size: 100px;
+     color: transparent;
+  }
+</style>
+<body>
+  <p>Test passes if there is a green square and no red.</p>
+
+  <div>
+    <!-- Left side verifies that typoLineHeightPx <= 230px -->
+    <div class="leftSide red"><span>O</span></div>
+    <div class="leftSide green forceHeight"></div>
+
+    <!-- Right side verifies that typoLineHeightPx => 230px -->
+    <div class="rightSide red forceHeight"></div>
+    <div class="rightSide green"><span>O</span></div>
+  </div>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/axisheight.py b/third_party/blink/web_tests/external/wpt/mathml/tools/axisheight.py
new file mode 100755
index 0000000..43827e7
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/axisheight.py
@@ -0,0 +1,23 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+verticalArrowCodePoint = 0x21A8
+v1 = 5 * mathfont.em
+v2 = 14 * mathfont.em
+f = mathfont.create("axisheight%d-verticalarrow%d" % (v1, v2))
+f.math.AxisHeight = v1
+mathfont.createSquareGlyph(f, verticalArrowCodePoint)
+g = f.createChar(-1, "size1")
+mathfont.drawRectangleGlyph(g, mathfont.em, v2 / 2, 0)
+g = f.createChar(-1, "size2")
+mathfont.drawRectangleGlyph(g, mathfont.em, v2, 0)
+g = f.createChar(-1, "bot")
+mathfont.drawRectangleGlyph(g, mathfont.em, v2 + v1, 0)
+g = f.createChar(-1, "ext")
+mathfont.drawRectangleGlyph(g, mathfont.em, mathfont.em, 0)
+f[verticalArrowCodePoint].verticalVariants = "uni21A8 size1 size2"
+f[verticalArrowCodePoint].verticalComponents = \
+  (("bot", False, 0, 0, mathfont.em), ("ext", True, 0, 0, mathfont.em));
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/fractions.py b/third_party/blink/web_tests/external/wpt/mathml/tools/fractions.py
new file mode 100755
index 0000000..8652806
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/fractions.py
@@ -0,0 +1,153 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+v1 = 7 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("fraction-axisheight%d-rulethickness%d" % (v1, v2))
+f.math.AxisHeight = v1
+f.math.FractionDenominatorDisplayStyleGapMin = 0
+f.math.FractionDenominatorDisplayStyleShiftDown = 0
+f.math.FractionDenominatorGapMin = 0
+f.math.FractionDenominatorShiftDown = 0
+f.math.FractionNumeratorDisplayStyleGapMin = 0
+f.math.FractionNumeratorDisplayStyleShiftUp = 0
+f.math.FractionNumeratorGapMin = 0
+f.math.FractionNumeratorShiftUp = 0
+f.math.FractionRuleThickness = v2
+mathfont.save(f)
+
+v1 = 5 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("fraction-denominatordisplaystylegapmin%d-rulethickness%d" % (v1, v2))
+f.math.AxisHeight = 0
+f.math.FractionDenominatorDisplayStyleGapMin = v1
+f.math.FractionDenominatorDisplayStyleShiftDown = 0
+f.math.FractionDenominatorGapMin = 0
+f.math.FractionDenominatorShiftDown = 0
+f.math.FractionNumeratorDisplayStyleGapMin = 0
+f.math.FractionNumeratorDisplayStyleShiftUp = 0
+f.math.FractionNumeratorGapMin = 0
+f.math.FractionNumeratorShiftUp = 0
+f.math.FractionRuleThickness = v2
+mathfont.save(f)
+
+v1 = 6 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("fraction-denominatordisplaystyleshiftdown%d-rulethickness%d" % (v1, v2))
+f.math.AxisHeight = 0
+f.math.FractionDenominatorDisplayStyleGapMin = 0
+f.math.FractionDenominatorDisplayStyleShiftDown = v1
+f.math.FractionDenominatorGapMin = 0
+f.math.FractionDenominatorShiftDown = 0
+f.math.FractionNumeratorDisplayStyleGapMin = 0
+f.math.FractionNumeratorDisplayStyleShiftUp = 0
+f.math.FractionNumeratorGapMin = 0
+f.math.FractionNumeratorShiftUp = 0
+f.math.FractionRuleThickness = v2
+mathfont.save(f)
+
+v1 = 4 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("fraction-denominatorgapmin%d-rulethickness%d" % (v1, v2))
+f.math.AxisHeight = 0
+f.math.FractionDenominatorDisplayStyleGapMin = 0
+f.math.FractionDenominatorDisplayStyleShiftDown = 0
+f.math.FractionDenominatorGapMin = v1
+f.math.FractionDenominatorShiftDown = 0
+f.math.FractionNumeratorDisplayStyleGapMin = 0
+f.math.FractionNumeratorDisplayStyleShiftUp = 0
+f.math.FractionNumeratorGapMin = 0
+f.math.FractionNumeratorShiftUp = 0
+f.math.FractionRuleThickness = v2
+mathfont.save(f)
+
+v1 = 3 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("fraction-denominatorshiftdown%d-rulethickness%d" % (v1, v2))
+f.math.AxisHeight = 0
+f.math.FractionDenominatorDisplayStyleGapMin = 0
+f.math.FractionDenominatorDisplayStyleShiftDown = 0
+f.math.FractionDenominatorGapMin = 0
+f.math.FractionDenominatorShiftDown = v1
+f.math.FractionNumeratorDisplayStyleGapMin = 0
+f.math.FractionNumeratorDisplayStyleShiftUp = 0
+f.math.FractionNumeratorGapMin = 0
+f.math.FractionNumeratorShiftUp = 0
+f.math.FractionRuleThickness = v2
+mathfont.save(f)
+
+v1 = 8 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("fraction-numeratordisplaystylegapmin%d-rulethickness%d" % (v1, v2))
+f.math.AxisHeight = 0
+f.math.FractionDenominatorDisplayStyleGapMin = 0
+f.math.FractionDenominatorDisplayStyleShiftDown = 0
+f.math.FractionDenominatorGapMin = 0
+f.math.FractionDenominatorShiftDown = 0
+f.math.FractionNumeratorDisplayStyleGapMin = v1
+f.math.FractionNumeratorDisplayStyleShiftUp = 0
+f.math.FractionNumeratorGapMin = 0
+f.math.FractionNumeratorShiftUp = 0
+f.math.FractionRuleThickness = v2
+mathfont.save(f)
+
+v1 = 2 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("fraction-numeratordisplaystyleshiftup%d-rulethickness%d" % (v1, v2))
+f.math.AxisHeight = 0
+f.math.FractionDenominatorDisplayStyleGapMin = 0
+f.math.FractionDenominatorDisplayStyleShiftDown = 0
+f.math.FractionDenominatorGapMin = 0
+f.math.FractionDenominatorShiftDown = 0
+f.math.FractionNumeratorDisplayStyleGapMin = 0
+f.math.FractionNumeratorDisplayStyleShiftUp = v1
+f.math.FractionNumeratorGapMin = 0
+f.math.FractionNumeratorShiftUp = 0
+f.math.FractionRuleThickness = v2
+mathfont.save(f)
+
+v1 = 9 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("fraction-numeratorgapmin%d-rulethickness%d" % (v1, v2))
+f.math.AxisHeight = 0
+f.math.FractionDenominatorDisplayStyleGapMin = 0
+f.math.FractionDenominatorDisplayStyleShiftDown = 0
+f.math.FractionDenominatorGapMin = 0
+f.math.FractionDenominatorShiftDown = 0
+f.math.FractionNumeratorDisplayStyleGapMin = 0
+f.math.FractionNumeratorDisplayStyleShiftUp = 0
+f.math.FractionNumeratorGapMin = v1
+f.math.FractionNumeratorShiftUp = 0
+f.math.FractionRuleThickness = v2
+mathfont.save(f)
+
+v1 = 11 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("fraction-numeratorshiftup%d-rulethickness%d" % (v1, v2))
+f.math.AxisHeight = 0
+f.math.FractionDenominatorDisplayStyleGapMin = 0
+f.math.FractionDenominatorDisplayStyleShiftDown = 0
+f.math.FractionDenominatorGapMin = 0
+f.math.FractionDenominatorShiftDown = 0
+f.math.FractionNumeratorDisplayStyleGapMin = 0
+f.math.FractionNumeratorDisplayStyleShiftUp = 0
+f.math.FractionNumeratorGapMin = 0
+f.math.FractionNumeratorShiftUp = v1
+f.math.FractionRuleThickness = v2
+mathfont.save(f)
+
+v1 = 10 * mathfont.em
+f = mathfont.create("fraction-rulethickness%d" % v1)
+f.math.AxisHeight = 0
+f.math.FractionDenominatorDisplayStyleGapMin = 0
+f.math.FractionDenominatorDisplayStyleShiftDown = 0
+f.math.FractionDenominatorGapMin = 0
+f.math.FractionDenominatorShiftDown = 0
+f.math.FractionNumeratorDisplayStyleGapMin = 0
+f.math.FractionNumeratorDisplayStyleShiftUp = 0
+f.math.FractionNumeratorGapMin = 0
+f.math.FractionNumeratorShiftUp = 0
+f.math.FractionRuleThickness = v1
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/largeop.py b/third_party/blink/web_tests/external/wpt/mathml/tools/largeop.py
new file mode 100755
index 0000000..73d967689
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/largeop.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+nAryWhiteVerticalBarCodePoint = 0x2AFF
+v1 = 5 * mathfont.em
+f = mathfont.create("largeop-displayoperatorminheight%d" % v1)
+f.math.DisplayOperatorMinHeight = v1
+mathfont.createSquareGlyph(f, nAryWhiteVerticalBarCodePoint)
+g = f.createChar(-1, "uni2AFF.display")
+mathfont.drawRectangleGlyph(g, mathfont.em, v1, 0)
+f[nAryWhiteVerticalBarCodePoint].verticalVariants = "uni2AFF uni2AFF.display"
+mathfont.save(f)
+
+v1 = 2 * mathfont.em
+v2 = 3 * mathfont.em
+f = mathfont.create("largeop-displayoperatorminheight%d-2AFF-italiccorrection%d" % (v1, v2))
+f.copyright = "Copyright (c) 2018 Igalia S.L."
+f.math.DisplayOperatorMinHeight = v1
+mathfont.createSquareGlyph(f, nAryWhiteVerticalBarCodePoint)
+g = f.createChar(-1, "uni2AFF.display")
+p = g.glyphPen()
+p.moveTo(0, 0)
+p.lineTo(v2, v1)
+p.lineTo(v2 + mathfont.em, v1)
+p.lineTo(mathfont.em, 0)
+p.closePath();
+g.width = mathfont.em + v2
+g.italicCorrection = v2
+f[nAryWhiteVerticalBarCodePoint].verticalVariants = "uni2AFF uni2AFF.display"
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/limits.py b/third_party/blink/web_tests/external/wpt/mathml/tools/limits.py
new file mode 100755
index 0000000..840a76ff
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/limits.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+nArySumCodePoint = 0x2211 # largeop operator
+
+v = 3 * mathfont.em
+f = mathfont.create("limits-lowerlimitbaselinedropmin%d" % v)
+mathfont.createSquareGlyph(f, nArySumCodePoint)
+f.math.LowerLimitBaselineDropMin = v
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
+
+v = 11 * mathfont.em
+f = mathfont.create("limits-lowerlimitgapmin%d" % v)
+mathfont.createSquareGlyph(f, nArySumCodePoint)
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = v
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
+
+v = 5 * mathfont.em
+f = mathfont.create("limits-upperlimitbaselinerisemin%d" % v)
+mathfont.createSquareGlyph(f, nArySumCodePoint)
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = v
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
+
+v = 7 * mathfont.em
+f = mathfont.create("limits-upperlimitgapmin%d" % v)
+mathfont.createSquareGlyph(f, nArySumCodePoint)
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = v
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/mathvariant-transforms.py b/third_party/blink/web_tests/external/wpt/mathml/tools/mathvariant-transforms.py
new file mode 100755
index 0000000..6094a744
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/mathvariant-transforms.py
@@ -0,0 +1,127 @@
+#!/usr/bin/python
+
+from __future__ import print_function
+from lxml import etree
+from utils.misc import downloadWithProgressBar
+from utils import mathfont
+import json
+
+# Retrieve the unicode.xml file if necessary.
+unicodeXML = downloadWithProgressBar("http://www.w3.org/2003/entities/2007xml/unicode.xml")
+
+# Extract the mathvariants transformation.
+xsltTransform = etree.XSLT(etree.XML('''\
+<xsl:stylesheet version="1.0"
+                       xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:strip-space elements="*"/>
+  <xsl:template match="charlist">
+    <root><xsl:apply-templates select="character"/></root>
+  </xsl:template>
+  <xsl:template match="character">
+    <xsl:if test="surrogate">
+      <entry>
+        <xsl:attribute name="mathvariant">
+            <xsl:value-of select="surrogate/@mathvariant"/>
+        </xsl:attribute>
+        <xsl:attribute name="baseChar">
+          <xsl:value-of select="surrogate/@ref"/>
+        </xsl:attribute>
+        <xsl:attribute name="transformedChar">
+          <xsl:choose>
+            <xsl:when test="bmp">
+              <xsl:value-of select="bmp/@ref"/>
+            </xsl:when>
+            <xsl:otherwise>
+               <xsl:value-of select="@id"/>
+            </xsl:otherwise>
+          </xsl:choose>
+        </xsl:attribute>
+      </entry>
+    </xsl:if>
+  </xsl:template>
+</xsl:stylesheet>'''))
+
+# Put the mathvariant transforms into a Python structure.
+mathvariantTransforms = {}
+root = xsltTransform(etree.parse(unicodeXML)).getroot()
+def parseCodePoint(aHexaString):
+    return int("0x%s" % aHexaString[1:], 16)
+for entry in root:
+    mathvariant = entry.get("mathvariant")
+    baseChar = parseCodePoint(entry.get("baseChar"))
+    transformedChar = parseCodePoint(entry.get("transformedChar"))
+    if mathvariant not in mathvariantTransforms:
+        mathvariantTransforms[mathvariant] = {}
+    mathvariantTransforms[mathvariant][baseChar] = transformedChar
+
+# There is no "isolated" mathvariant.
+del mathvariantTransforms["isolated"]
+
+# Create a WOFF font for each mathvariant.
+for mathvariant in mathvariantTransforms:
+    font = mathfont.create("mathvariant-%s" % mathvariant)
+    for baseChar in mathvariantTransforms[mathvariant]:
+        if baseChar not in font:
+            mathfont.createGlyphFromValue(font, baseChar)
+        transformedChar = mathvariantTransforms[mathvariant][baseChar]
+        mathfont.createGlyphFromValue(font, transformedChar)
+    mathfont.save(font)
+
+# Create a test font for each mathvariant.
+for mathvariant in mathvariantTransforms:
+    print("Generating test for %s..." % mathvariant, end="")
+    reftest = open("../relations/css-styling/mathvariant-%s.html" % mathvariant, "w")
+    reftestReference = open("../relations/css-styling/mathvariant-%s-ref.html" % mathvariant, "w")
+    source = '\
+<!DOCTYPE html>\n\
+<html>\n\
+<head>\n\
+<meta charset="utf-8"/>\n\
+<title>mathvariant %s</title>\n'
+    reftest.write(source % mathvariant)
+    reftestReference.write(source % ("%s (reference)" % mathvariant))
+    source ='\
+<link rel="help" href="http://www.mathml-association.org/MathMLinHTML5/S2.html#SS3.SSS1.tab2"/>\n\
+<link rel="match" href="mathvariant-%s-ref.html"/>\n\
+<meta name="assert" content="Verify that a single-char <mtext> with a %s mathvariant is equivalent to an <mtext> with the transformed unicode character.">\n'
+    reftest.write(source % (mathvariant, mathvariant))
+    source = '\
+<style>\n\
+  @font-face {\n\
+    font-family: TestFont;\n\
+    src: url("/fonts/math/mathvariant-%s.woff");\n\
+  }\n\
+  body > span {\n\
+    padding: 10px;\n\
+  }\n\
+  span > span {\n\
+    font-family: monospace;\n\
+    font-size: 10px;\n\
+  }\n\
+  math {\n\
+    font-family: TestFont;\n\
+    font-size: 10px;\n\
+  }\n\
+</style>\n\
+<body>\n\
+  <!-- Generated by mathml/tools/mathvariant.py; DO NOT EDIT. -->\n\
+  <p>Test passes if all the equalities below are true.</p>\n' % mathvariant
+    reftest.write(source)
+    reftestReference.write(source)
+    charIndex = 0
+    for baseChar in mathvariantTransforms[mathvariant]:
+        transformedChar = mathvariantTransforms[mathvariant][baseChar]
+        reftest.write('  <span><math><mtext mathvariant="%s">&#x%0X;</mtext></math>=<span>%05X</span></span>' % (mathvariant, baseChar, transformedChar))
+        reftestReference.write('  <span><math><mtext>&#x%0X;</mtext></math>=<span>%05X</span></span>' % (transformedChar, transformedChar))
+        charIndex += 1
+        if charIndex % 10 == 0:
+            reftest.write('<br/>')
+            reftestReference.write('<br/>')
+        reftest.write('\n')
+        reftestReference.write('\n')
+    source = '</body>\n</html>\n'
+    reftest.write(source)
+    reftestReference.write(source)
+    reftest.close()
+    reftestReference.close()
+    print(" done.")
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/radicals.py b/third_party/blink/web_tests/external/wpt/mathml/tools/radicals.py
new file mode 100755
index 0000000..90fe1d9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/radicals.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+def createStretchyRadical(aFont):
+    radicalCodePoint = 0x221a
+    mathfont.createSquareGlyph(aFont, radicalCodePoint)
+    g = aFont.createChar(-1, "size1")
+    mathfont.drawRectangleGlyph(g, mathfont.em, 2 * mathfont.em, 0)
+    g = aFont.createChar(-1, "size2")
+    mathfont.drawRectangleGlyph(g, mathfont.em, 3 * mathfont.em, 0)
+    g = aFont.createChar(-1, "size3")
+    mathfont.drawRectangleGlyph(g, mathfont.em, 4 * mathfont.em, 0)
+    overlap = mathfont.em / 2
+    aFont[radicalCodePoint].verticalVariants = "radical size1 size2 size3"
+    aFont[radicalCodePoint].verticalComponents = \
+        (("size2", False, 0, mathfont.em, 3 * mathfont.em), \
+         ("size1", True, mathfont.em, mathfont.em, 2 * mathfont.em))
+
+v1 = 25
+v2 = 1 * mathfont.em
+f = mathfont.create("radical-degreebottomraisepercent%d-rulethickness%d" % (v1, v2))
+createStretchyRadical(f)
+f.math.RadicalDegreeBottomRaisePercent = v1
+f.math.RadicalDisplayStyleVerticalGap = 0
+f.math.RadicalExtraAscender = 0
+f.math.RadicalKernAfterDegree = 0
+f.math.RadicalKernBeforeDegree = 0
+f.math.RadicalRuleThickness = v2
+f.math.RadicalVerticalGap = 0
+mathfont.save(f)
+
+v1 = 7 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("radical-displaystyleverticalgap%d-rulethickness%d" % (v1, v2))
+createStretchyRadical(f)
+f.math.RadicalDegreeBottomRaisePercent = 0
+f.math.RadicalDisplayStyleVerticalGap = v1
+f.math.RadicalExtraAscender = 0
+f.math.RadicalKernAfterDegree = 0
+f.math.RadicalKernBeforeDegree = 0
+f.math.RadicalRuleThickness = v2
+f.math.RadicalVerticalGap = 0
+mathfont.save(f)
+
+v1 = 3 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("radical-extraascender%d-rulethickness%d" % (v1, v2))
+createStretchyRadical(f)
+f.math.RadicalDegreeBottomRaisePercent = 0
+f.math.RadicalDisplayStyleVerticalGap = 0
+f.math.RadicalExtraAscender = v1
+f.math.RadicalKernAfterDegree = 0
+f.math.RadicalKernBeforeDegree = 0
+f.math.RadicalRuleThickness = v2
+f.math.RadicalVerticalGap = 0
+mathfont.save(f)
+
+v1 = 5 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("radical-kernafterdegreeminus%d-rulethickness%d" % (v1, v2))
+createStretchyRadical(f)
+f.math.RadicalDegreeBottomRaisePercent = 0
+f.math.RadicalDisplayStyleVerticalGap = 0
+f.math.RadicalExtraAscender = 0
+f.math.RadicalKernAfterDegree = -v1
+f.math.RadicalKernBeforeDegree = 0
+f.math.RadicalRuleThickness = v2
+f.math.RadicalVerticalGap = 0
+mathfont.save(f)
+
+v1 = 4 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("radical-kernbeforedegree%d-rulethickness%d" % (v1, v2))
+createStretchyRadical(f)
+f.math.RadicalDegreeBottomRaisePercent = 0
+f.math.RadicalDisplayStyleVerticalGap = 0
+f.math.RadicalExtraAscender = 0
+f.math.RadicalKernAfterDegree = 0
+f.math.RadicalKernBeforeDegree = v1
+f.math.RadicalRuleThickness = v2
+f.math.RadicalVerticalGap = 0
+mathfont.save(f)
+
+v = 8 * mathfont.em
+f = mathfont.create("radical-rulethickness%d" % v)
+createStretchyRadical(f)
+f.math.RadicalDegreeBottomRaisePercent = 0
+f.math.RadicalDisplayStyleVerticalGap = 0
+f.math.RadicalExtraAscender = 0
+f.math.RadicalKernAfterDegree = 0
+f.math.RadicalKernBeforeDegree = 0
+f.math.RadicalRuleThickness = v
+f.math.RadicalVerticalGap = 0
+mathfont.save(f)
+
+v1 = 6 * mathfont.em
+v2 = 1 * mathfont.em
+f = mathfont.create("radical-verticalgap%d-rulethickness%d" % (v1, v2))
+createStretchyRadical(f)
+f.math.RadicalDegreeBottomRaisePercent = 0
+f.math.RadicalDisplayStyleVerticalGap = 0
+f.math.RadicalExtraAscender = 0
+f.math.RadicalKernAfterDegree = 0
+f.math.RadicalKernBeforeDegree = 0
+f.math.RadicalRuleThickness = v2
+f.math.RadicalVerticalGap = v1
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/scripts.py b/third_party/blink/web_tests/external/wpt/mathml/tools/scripts.py
new file mode 100755
index 0000000..add1637
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/scripts.py
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+v = 3 * mathfont.em
+f = mathfont.create("scripts-spaceafterscript%d" % v)
+f.math.SpaceAfterScript = v
+f.math.SubSuperscriptGapMin = 0
+f.math.SubscriptBaselineDropMin = 0
+f.math.SubscriptShiftDown = 0
+f.math.SubscriptTopMax = 0
+f.math.SuperscriptBaselineDropMax = 0
+f.math.SuperscriptBottomMaxWithSubscript = 0
+f.math.SuperscriptBottomMin = 0
+f.math.SuperscriptShiftUp = 0
+f.math.SuperscriptShiftUpCramped = 0
+mathfont.save(f)
+
+v = 7 * mathfont.em
+f = mathfont.create("scripts-superscriptshiftup%d" % v)
+f.math.SpaceAfterScript = 0
+f.math.SubSuperscriptGapMin = 0
+f.math.SubscriptBaselineDropMin = 0
+f.math.SubscriptShiftDown = 0
+f.math.SubscriptTopMax = 0
+f.math.SuperscriptBaselineDropMax = 0
+f.math.SuperscriptBottomMaxWithSubscript = 0
+f.math.SuperscriptBottomMin = 0
+f.math.SuperscriptShiftUp = v
+f.math.SuperscriptShiftUpCramped = 0
+mathfont.save(f)
+
+v = 5 * mathfont.em
+f = mathfont.create("scripts-superscriptshiftupcramped%d" % v)
+f.math.SpaceAfterScript = 0
+f.math.SubSuperscriptGapMin = 0
+f.math.SubscriptBaselineDropMin = 0
+f.math.SubscriptShiftDown = 0
+f.math.SubscriptTopMax = 0
+f.math.SuperscriptBaselineDropMax = 0
+f.math.SuperscriptBottomMaxWithSubscript = 0
+f.math.SuperscriptBottomMin = 0
+f.math.SuperscriptShiftUp = 0
+f.math.SuperscriptShiftUpCramped = v
+mathfont.save(f)
+
+v = 6 * mathfont.em
+f = mathfont.create("scripts-subscriptshiftdown%d" % v)
+f.math.SpaceAfterScript = 0
+f.math.SubSuperscriptGapMin = 0
+f.math.SubscriptBaselineDropMin = 0
+f.math.SubscriptShiftDown = v
+f.math.SubscriptTopMax = 0
+f.math.SuperscriptBaselineDropMax = 0
+f.math.SuperscriptBottomMaxWithSubscript = 0
+f.math.SuperscriptBottomMin = 0
+f.math.SuperscriptShiftUp = 0
+f.math.SuperscriptShiftUpCramped = 0
+mathfont.save(f)
+
+v = 11 * mathfont.em
+f = mathfont.create("scripts-subsuperscriptgapmin%d" % v)
+f.math.SpaceAfterScript = 0
+f.math.SubSuperscriptGapMin = v
+f.math.SubscriptBaselineDropMin = 0
+f.math.SubscriptShiftDown = 0
+f.math.SubscriptTopMax = 0
+f.math.SuperscriptBaselineDropMax = 0
+f.math.SuperscriptBottomMaxWithSubscript = 0
+f.math.SuperscriptBottomMin = 0
+f.math.SuperscriptShiftUp = 0
+f.math.SuperscriptShiftUpCramped = 0
+mathfont.save(f)
+
+v1 = 11 * mathfont.em
+v2 = 3 * mathfont.em
+f = mathfont.create("scripts-subsuperscriptgapmin%d-superscriptbottommaxwithsubscript%d" % (v1, v2))
+f.math.SpaceAfterScript = 0
+f.math.SubSuperscriptGapMin = v1
+f.math.SubscriptBaselineDropMin = 0
+f.math.SubscriptShiftDown = 0
+f.math.SubscriptTopMax = 0
+f.math.SuperscriptBaselineDropMax = 0
+f.math.SuperscriptBottomMaxWithSubscript = v2
+f.math.SuperscriptBottomMin = 0
+f.math.SuperscriptShiftUp = 0
+f.math.SuperscriptShiftUpCramped = 0
+mathfont.save(f)
+
+v = 4 * mathfont.em
+f = mathfont.create("scripts-subscripttopmax%d" % v)
+f.math.SpaceAfterScript = 0
+f.math.SubSuperscriptGapMin = 0
+f.math.SubscriptBaselineDropMin = 0
+f.math.SubscriptShiftDown = 0
+f.math.SubscriptTopMax = v
+f.math.SuperscriptBaselineDropMax = 0
+f.math.SuperscriptBottomMaxWithSubscript = 0
+f.math.SuperscriptBottomMin = 0
+f.math.SuperscriptShiftUp = 0
+f.math.SuperscriptShiftUpCramped = 0
+mathfont.save(f)
+
+v = 8 * mathfont.em
+f = mathfont.create("scripts-superscriptbottommin%d" % v)
+f.math.SpaceAfterScript = 0
+f.math.SubSuperscriptGapMin = 0
+f.math.SubscriptBaselineDropMin = 0
+f.math.SubscriptShiftDown = 0
+f.math.SubscriptTopMax = 0
+f.math.SuperscriptBaselineDropMax = 0
+f.math.SuperscriptBottomMaxWithSubscript = 0
+f.math.SuperscriptBottomMin = v
+f.math.SuperscriptShiftUp = 0
+f.math.SuperscriptShiftUpCramped = 0
+mathfont.save(f)
+
+v = 9 * mathfont.em
+f = mathfont.create("scripts-subscriptbaselinedropmin%d" % v)
+f.math.SpaceAfterScript = 0
+f.math.SubSuperscriptGapMin = 0
+f.math.SubscriptBaselineDropMin = v
+f.math.SubscriptShiftDown = 0
+f.math.SubscriptTopMax = 0
+f.math.SuperscriptBaselineDropMax = 0
+f.math.SuperscriptBottomMaxWithSubscript = 0
+f.math.SuperscriptBottomMin = 0
+f.math.SuperscriptShiftUp = 0
+f.math.SuperscriptShiftUpCramped = 0
+mathfont.save(f)
+
+v = 10 * mathfont.em
+f = mathfont.create("scripts-superscriptbaselinedropmax%d" % v)
+f.math.SpaceAfterScript = 0
+f.math.SubSuperscriptGapMin = 0
+f.math.SubscriptBaselineDropMin = 0
+f.math.SubscriptShiftDown = 0
+f.math.SubscriptTopMax = 0
+f.math.SuperscriptBaselineDropMax = v
+f.math.SuperscriptBottomMaxWithSubscript = 0
+f.math.SuperscriptBottomMin = 0
+f.math.SuperscriptShiftUp = 0
+f.math.SuperscriptShiftUpCramped = 0
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/stacks.py b/third_party/blink/web_tests/external/wpt/mathml/tools/stacks.py
new file mode 100755
index 0000000..81f79be
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/stacks.py
@@ -0,0 +1,81 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+v = 7 * mathfont.em
+f = mathfont.create("stack-axisheight%d" % v)
+f.math.AxisHeight = v
+f.math.StackBottomDisplayStyleShiftDown = 0
+f.math.StackBottomShiftDown = 0
+f.math.StackDisplayStyleGapMin = 0
+f.math.StackGapMin = 0
+f.math.StackTopDisplayStyleShiftUp = 0
+f.math.StackTopShiftUp = 0
+mathfont.save(f)
+
+v = 5 * mathfont.em
+f = mathfont.create("stack-bottomdisplaystyleshiftdown%d" % v)
+f.math.AxisHeight = 0
+f.math.StackBottomDisplayStyleShiftDown = v
+f.math.StackBottomShiftDown = 0
+f.math.StackDisplayStyleGapMin = 0
+f.math.StackGapMin = 0
+f.math.StackTopDisplayStyleShiftUp = 0
+f.math.StackTopShiftUp = 0
+mathfont.save(f)
+
+v = 6 * mathfont.em
+f = mathfont.create("stack-bottomshiftdown%d" % v)
+f.math.AxisHeight = 0
+f.math.StackBottomDisplayStyleShiftDown = 0
+f.math.StackBottomShiftDown = v
+f.math.StackDisplayStyleGapMin = 0
+f.math.StackGapMin = 0
+f.math.StackTopDisplayStyleShiftUp = 0
+f.math.StackTopShiftUp = 0
+mathfont.save(f)
+
+v = 4 * mathfont.em
+f = mathfont.create("stack-displaystylegapmin%d" % v)
+f.math.AxisHeight = 0
+f.math.StackBottomDisplayStyleShiftDown = 0
+f.math.StackBottomShiftDown = 0
+f.math.StackDisplayStyleGapMin = v
+f.math.StackGapMin = 0
+f.math.StackTopDisplayStyleShiftUp = 0
+f.math.StackTopShiftUp = 0
+mathfont.save(f)
+
+v = 8 * mathfont.em
+f = mathfont.create("stack-gapmin%d" % v)
+f.math.AxisHeight = 0
+f.math.StackBottomDisplayStyleShiftDown = 0
+f.math.StackBottomShiftDown = 0
+f.math.StackDisplayStyleGapMin = 0
+f.math.StackGapMin = v
+f.math.StackTopDisplayStyleShiftUp = 0
+f.math.StackTopShiftUp = 0
+mathfont.save(f)
+
+v = 3 * mathfont.em
+f = mathfont.create("stack-topdisplaystyleshiftup%d" % v)
+f.math.AxisHeight = 0
+f.math.StackBottomDisplayStyleShiftDown = 0
+f.math.StackBottomShiftDown = 0
+f.math.StackDisplayStyleGapMin = 0
+f.math.StackGapMin = 0
+f.math.StackTopDisplayStyleShiftUp = v
+f.math.StackTopShiftUp = 0
+mathfont.save(f)
+
+v = 9 * mathfont.em
+f = mathfont.create("stack-topshiftup%d" % v)
+f.math.AxisHeight = 0
+f.math.StackBottomDisplayStyleShiftDown = 0
+f.math.StackBottomShiftDown = 0
+f.math.StackDisplayStyleGapMin = 0
+f.math.StackGapMin = 0
+f.math.StackTopDisplayStyleShiftUp = 0
+f.math.StackTopShiftUp = v
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/stretchstacks.py b/third_party/blink/web_tests/external/wpt/mathml/tools/stretchstacks.py
new file mode 100755
index 0000000..7b888f4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/stretchstacks.py
@@ -0,0 +1,74 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+arrowCodePoint = 0x2192 # horizontal stretch operator
+
+v = 3 * mathfont.em
+f = mathfont.create("stretchstack-bottomshiftdown%d" % v)
+mathfont.createSquareGlyph(f, arrowCodePoint)
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = v
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
+
+v = 11 * mathfont.em
+f = mathfont.create("stretchstack-gapbelowmin%d" % v)
+mathfont.createSquareGlyph(f, arrowCodePoint)
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = v
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
+
+v = 5 * mathfont.em
+f = mathfont.create("stretchstack-topshiftup%d" % v)
+mathfont.createSquareGlyph(f, arrowCodePoint)
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = v
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
+
+v = 7 * mathfont.em
+f = mathfont.create("stretchstack-gapabovemin%d" % v)
+mathfont.createSquareGlyph(f, arrowCodePoint)
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = v
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/underover.py b/third_party/blink/web_tests/external/wpt/mathml/tools/underover.py
new file mode 100755
index 0000000..df34e79
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/underover.py
@@ -0,0 +1,84 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+breveCodePoint = 0x2D8 # accent operator
+degreeCodePoint = 0xB0 # nonaccent operator
+accentBaseHeight = 4 * mathfont.em
+
+v = 3 * mathfont.em
+f = mathfont.create("underover-accentbaseheight%d-overbarextraascender%d" % (accentBaseHeight, v))
+mathfont.createSquareGlyph(f, breveCodePoint)
+mathfont.createSquareGlyph(f, degreeCodePoint)
+f.math.AccentBaseHeight = accentBaseHeight
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = v
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
+
+v = 11 * mathfont.em
+f = mathfont.create("underover-accentbaseheight%d-overbarverticalgap%d" % (accentBaseHeight, v))
+mathfont.createSquareGlyph(f, breveCodePoint)
+mathfont.createSquareGlyph(f, degreeCodePoint)
+f.math.AccentBaseHeight = accentBaseHeight
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = v
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
+
+v = 5 * mathfont.em
+f = mathfont.create("underover-accentbaseheight%d-underbarextradescender%d" % (accentBaseHeight, v))
+mathfont.createSquareGlyph(f, breveCodePoint)
+mathfont.createSquareGlyph(f, degreeCodePoint)
+f.math.AccentBaseHeight = accentBaseHeight
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = v
+f.math.UnderbarVerticalGap = 0
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
+
+v = 7 * mathfont.em
+f = mathfont.create("underover-accentbaseheight%d-underbarverticalgap%d" % (accentBaseHeight, v))
+mathfont.createSquareGlyph(f, breveCodePoint)
+mathfont.createSquareGlyph(f, degreeCodePoint)
+f.math.AccentBaseHeight = accentBaseHeight
+f.math.LowerLimitBaselineDropMin = 0
+f.math.LowerLimitGapMin = 0
+f.math.OverbarExtraAscender = 0
+f.math.OverbarVerticalGap = 0
+f.math.StretchStackBottomShiftDown = 0
+f.math.StretchStackGapAboveMin = 0
+f.math.StretchStackGapBelowMin = 0
+f.math.StretchStackTopShiftUp = 0
+f.math.UnderbarExtraDescender = 0
+f.math.UnderbarVerticalGap = v
+f.math.UpperLimitBaselineRiseMin = 0
+f.math.UpperLimitGapMin = 0
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/use-typo-lineheight.py b/third_party/blink/web_tests/external/wpt/mathml/tools/use-typo-lineheight.py
new file mode 100755
index 0000000..9768979f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/use-typo-lineheight.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+
+from __future__ import print_function
+from utils.misc import MathMLAssociationCopyright
+import fontforge
+
+font = fontforge.font()
+font.em = 1000
+typoLineHeight = 2300
+winHeight = 5000
+name = "font-lineheight%d-typolineheight%d" % (winHeight, typoLineHeight)
+font.fontname = name
+font.familyname = name
+font.fullname = name
+font.copyright = MathMLAssociationCopyright
+
+glyph = font.createChar(ord(" "), "space")
+glyph.width = 1000
+glyph = font.createChar(ord("O"))
+pen = glyph.glyphPen()
+pen.moveTo(0, -200)
+pen.lineTo(0, 800)
+pen.lineTo(1000, 800)
+pen.lineTo(1000, -200)
+pen.closePath();
+
+font.os2_typoascent_add = False
+font.os2_typoascent = 800
+font.os2_typodescent_add = False
+font.os2_typodescent = -200
+font.os2_typolinegap = typoLineHeight - \
+                       (font.os2_typoascent - font.os2_typodescent)
+
+font.hhea_ascent = winHeight / 2
+font.hhea_ascent_add = False
+font.hhea_descent = -winHeight / 2
+font.hhea_descent_add = False
+font.hhea_linegap = 0
+
+font.os2_winascent = winHeight / 2
+font.os2_winascent_add = False
+font.os2_windescent = winHeight / 2
+font.os2_windescent_add = False
+
+font.os2_use_typo_metrics = True
+
+path = "../../fonts/math/lineheight%d-typolineheight%d.woff" % (winHeight, typoLineHeight)
+print("Generating %s..." % path, end="")
+font.generate(path)
+if font.validate() == 0:
+    print(" done.")
+else:
+    print(" validation error!")
+    exit(1)
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-gainnode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/mathml/tools/utils/__init__.py
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-gainnode-interface/.gitkeep
rename to third_party/blink/web_tests/external/wpt/mathml/tools/utils/__init__.py
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/utils/mathfont.py b/third_party/blink/web_tests/external/wpt/mathml/tools/utils/mathfont.py
new file mode 100644
index 0000000..7664c6d
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/utils/mathfont.py
@@ -0,0 +1,190 @@
+from __future__ import print_function
+import fontforge
+from misc import MathMLAssociationCopyright
+
+em = 1000
+
+def create(aName):
+    print("Generating %s.woff..." % aName, end="")
+    mathFont = fontforge.font()
+    mathFont.fontname = aName
+    mathFont.familyname = aName
+    mathFont.fullname = aName
+    mathFont.copyright = MathMLAssociationCopyright
+    mathFont.encoding = "UnicodeFull"
+
+    # Create a space character. Also force the creation of some MATH subtables
+    # so that OTS will not reject the MATH table.
+    g = mathFont.createChar(ord(" "), "space")
+    g.width = em
+    g.italicCorrection = 0
+    g.topaccent = 0
+    g.mathKern.bottomLeft = tuple([(0,0)])
+    g.mathKern.bottomRight = tuple([(0,0)])
+    g.mathKern.topLeft = tuple([(0,0)])
+    g.mathKern.topRight = tuple([(0,0)])
+    mathFont[ord(" ")].horizontalVariants = "space"
+    mathFont[ord(" ")].verticalVariants = "space"
+    return mathFont
+
+def drawRectangleGlyph(aGlyph, aWidth, aAscent, aDescent):
+    p = aGlyph.glyphPen()
+    p.moveTo(0, -aDescent)
+    p.lineTo(0, aAscent)
+    p.lineTo(aWidth, aAscent)
+    p.lineTo(aWidth, -aDescent)
+    p.closePath();
+    aGlyph.width = aWidth
+
+def createSquareGlyph(aFont, aCodePoint):
+    g = aFont.createChar(aCodePoint)
+    drawRectangleGlyph(g, em, em, 0)
+
+def drawHexaDigit(aGlyph, aX, aValue):
+    t = em / 10
+    p = aGlyph.glyphPen(replace = False)
+    if aValue == 0:
+        p.moveTo(aX + t, t)
+        p.lineTo(aX + t, em - t)
+        p.lineTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.closePath()
+    elif aValue == 1:
+        p.moveTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.endPath()
+    elif aValue == 2:
+        p.moveTo(aX + t, em - t)
+        p.lineTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + em / 2 - t, em / 2)
+        p.lineTo(aX + t, em / 2)
+        p.lineTo(aX + t, t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.endPath()
+    elif aValue == 3:
+        p.moveTo(aX + t, em - t)
+        p.lineTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.lineTo(aX + t, t)
+        p.endPath()
+        p.moveTo(aX + t, em / 2)
+        p.lineTo(aX + em / 2 - 2.5 * t, em / 2)
+        p.endPath()
+    elif aValue == 4:
+        p.moveTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.endPath()
+        p.moveTo(aX + t, em - t)
+        p.lineTo(aX + t, em / 2)
+        p.lineTo(aX + em / 2 - 2.5 * t, em / 2)
+        p.endPath()
+    elif aValue == 5:
+        p.moveTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + t, em - t)
+        p.lineTo(aX + t, em / 2)
+        p.lineTo(aX + em / 2 - t, em / 2)
+        p.lineTo(aX + em / 2 - t, t)
+        p.lineTo(aX + t, t)
+        p.endPath()
+    elif aValue == 6:
+        p.moveTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + t, em - t)
+        p.lineTo(aX + t, t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.lineTo(aX + em / 2 - t, em / 2)
+        p.lineTo(aX + 2.5 * t, em / 2)
+        p.endPath()
+    elif aValue == 7:
+        p.moveTo(aX + t, em - t)
+        p.lineTo(aX + em / 2  - t, em - t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.endPath()
+    elif aValue == 8:
+        p.moveTo(aX + t, t)
+        p.lineTo(aX + t, em - t)
+        p.lineTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.closePath()
+        p.moveTo(aX + 2.5 * t, em / 2)
+        p.lineTo(aX + em / 2 - 2.5 * t, em / 2)
+        p.endPath()
+    elif aValue == 9:
+        p.moveTo(aX + t, t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.lineTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + t, em - t)
+        p.lineTo(aX + t, em / 2)
+        p.lineTo(aX + em / 2 - 2.5 * t, em / 2)
+        p.endPath()
+    elif aValue == 10: # A
+        p.moveTo(aX + t, t)
+        p.lineTo(aX + t, em - t)
+        p.lineTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.endPath()
+        p.moveTo(aX + 2.5 * t, em / 2)
+        p.lineTo(aX + em / 2 - 2.5 * t, em / 2)
+        p.endPath()
+    elif aValue == 11: # b
+        p.moveTo(aX + t, em - t)
+        p.lineTo(aX + t, t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.lineTo(aX + em / 2 - t, em / 2)
+        p.lineTo(aX + 2.5 * t, em / 2)
+        p.endPath()
+    elif aValue == 12: # C
+        p.moveTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + t, em - t)
+        p.lineTo(aX + t, t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.endPath()
+    elif aValue == 13: # d
+        p.moveTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.lineTo(aX + t, t)
+        p.lineTo(aX + t, em / 2)
+        p.lineTo(aX + em / 2 - 2.5 * t, em / 2)
+        p.endPath()
+    elif aValue == 14: # E
+        p.moveTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + t, em - t)
+        p.lineTo(aX + t, t)
+        p.lineTo(aX + em / 2 - t, t)
+        p.endPath()
+        p.moveTo(aX + em / 2 - t, em / 2)
+        p.lineTo(aX + 2.5 * t, em / 2)
+        p.endPath()
+    elif aValue == 15: # F
+        p.moveTo(aX + em / 2 - t, em - t)
+        p.lineTo(aX + t, em - t)
+        p.lineTo(aX + t, t)
+        p.endPath()
+        p.moveTo(aX + em / 2 - t, em / 2)
+        p.lineTo(aX + 2.5 * t, em / 2)
+        p.endPath()
+
+def createGlyphFromValue(aFont, aCodePoint):
+    g = aFont.createChar(aCodePoint)
+    value = aCodePoint
+    for i in range(0, 5):
+        drawHexaDigit(g, (5 - (i + 1)) * em / 2, value % 16)
+        value /= 16
+    g.width = 5 * em / 2
+    g.stroke("circular", em / 10, "square", "miter", "cleanup")
+
+def save(aFont):
+    aFont.em = em
+    aFont.ascent = aFont.hhea_ascent = aFont.os2_typoascent = em
+    aFont.descent = aFont.hhea_descent = aFont.os2_typodescent = 0
+    # aFont.os2_winascent, aFont.os2_windescent should be the maximum of
+    # ascent/descent for all glyphs. Does fontforge compute them automatically?
+    aFont.hhea_ascent_add = aFont.hhea_descent_add = 0
+    aFont.os2_typoascent_add = aFont.os2_typodescent_add = 0
+    aFont.os2_winascent_add = aFont.os2_windescent_add = 0
+    aFont.os2_use_typo_metrics = True
+    aFont.generate("../../fonts/math/%s.woff" % aFont.fontname)
+    if aFont.validate() == 0:
+        print(" done.")
+    else:
+        print(" validation error!")
+        exit(1)
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/utils/misc.py b/third_party/blink/web_tests/external/wpt/mathml/tools/utils/misc.py
new file mode 100644
index 0000000..e4d21d6
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/utils/misc.py
@@ -0,0 +1,36 @@
+from __future__ import print_function
+import os
+import progressbar
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib2 import urlopen
+
+MathMLAssociationCopyright = "Copyright (c) 2016 MathML Association"
+
+def downloadWithProgressBar(url, outputDirectory="./", forceDownload=False):
+
+    baseName = os.path.basename(url)
+    fileName = os.path.join(outputDirectory, baseName)
+
+    if not forceDownload and os.path.exists(fileName):
+        return fileName
+
+    request = urlopen(url)
+    totalSize = int(request.info().getheader('Content-Length').strip())
+    bar = progressbar.ProgressBar(maxval=totalSize).start()
+
+    chunkSize = 16 * 1024
+    downloaded = 0
+    print("Downloading %s" % url)
+    os.umask(0o002)
+    with open(fileName, 'wb') as fp:
+        while True:
+            chunk = request.read(chunkSize)
+            downloaded += len(chunk)
+            bar.update(downloaded)
+            if not chunk: break
+            fp.write(chunk)
+        bar.finish()
+
+    return fileName
diff --git a/third_party/blink/web_tests/external/wpt/mathml/tools/xHeight.py b/third_party/blink/web_tests/external/wpt/mathml/tools/xHeight.py
new file mode 100755
index 0000000..724352b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/mathml/tools/xHeight.py
@@ -0,0 +1,11 @@
+#!/usr/bin/python
+
+from utils import mathfont
+import fontforge
+
+v = mathfont.em / 2
+f = mathfont.create("xheight%d" % v)
+g = f.createChar(ord('x'))
+mathfont.drawRectangleGlyph(g, mathfont.em, v, 0)
+assert f.xHeight == v, "Bad x-height value!"
+mathfont.save(f)
diff --git a/third_party/blink/web_tests/external/wpt/mixed-content/imageset.https.sub.html b/third_party/blink/web_tests/external/wpt/mixed-content/imageset.https.sub.html
index dd37156..1f3d047 100644
--- a/third_party/blink/web_tests/external/wpt/mixed-content/imageset.https.sub.html
+++ b/third_party/blink/web_tests/external/wpt/mixed-content/imageset.https.sub.html
@@ -21,9 +21,9 @@
   <img srcset="http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?img_srcset">
   <script>
     window.addEventListener("load", t.step_func(function() {
-        verifyNumberOfDownloads("http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?img_src", 1);
-        verifyNumberOfDownloads("http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?picture", 0);
-        verifyNumberOfDownloads("http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?img_srcset", 0);
+        verifyNumberOfResourceTimingEntries("http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?img_src", 1);
+        verifyNumberOfResourceTimingEntries("http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?picture", 0);
+        verifyNumberOfResourceTimingEntries("http://{{domains[]}}:{{ports[http][0]}}/images/smiley.png?img_srcset", 0);
         t.done();
     }));
   </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-preload.html b/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-preload.html
index 77838c3..a1b19c8 100644
--- a/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-preload.html
+++ b/third_party/blink/web_tests/external/wpt/preload/avoid-delaying-onload-link-preload.html
@@ -9,7 +9,7 @@
 <script>
     window.addEventListener("load", t.step_func(function() {
         verifyPreloadAndRTSupport();
-        verifyNumberOfDownloads("/resources/dummy.js?pipe=trickle(d5)", 0);
+        verifyNumberOfResourceTimingEntries("/resources/dummy.js?pipe=trickle(d5)", 0);
         t.done();
     }));
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/delaying-onload-link-preload-after-discovery.html b/third_party/blink/web_tests/external/wpt/preload/delaying-onload-link-preload-after-discovery.html
index 095d89ad..1c856d16 100644
--- a/third_party/blink/web_tests/external/wpt/preload/delaying-onload-link-preload-after-discovery.html
+++ b/third_party/blink/web_tests/external/wpt/preload/delaying-onload-link-preload-after-discovery.html
@@ -11,8 +11,8 @@
 <script>
     window.addEventListener("load", t.step_func(function() {
         verifyPreloadAndRTSupport();
-        verifyNumberOfDownloads("resources/dummy.js?pipe=trickle(d5)", 1);
-        verifyNumberOfDownloads("resources/square.png?pipe=trickle(d5)", 1);
+        verifyLoadedAndNoDoubleDownload("resources/dummy.js?pipe=trickle(d5)");
+        verifyLoadedAndNoDoubleDownload("resources/square.png?pipe=trickle(d5)");
         t.done();
     }));
     var script = document.createElement("script");
diff --git a/third_party/blink/web_tests/external/wpt/preload/download-resources.html b/third_party/blink/web_tests/external/wpt/preload/download-resources.html
index dc2b4693..510ebb4 100644
--- a/third_party/blink/web_tests/external/wpt/preload/download-resources.html
+++ b/third_party/blink/web_tests/external/wpt/preload/download-resources.html
@@ -16,20 +16,21 @@
 <link rel=preload href="resources/dummy.xml?novalue">
 <link rel=preload href="resources/dummy.xml" as="fetch">
 <body>
-<script src="resources/dummy.js?pipe=trickle(d5)&download-resources"></script>
 <script>
     window.addEventListener("load", t.step_func(function() {
-        verifyPreloadAndRTSupport()
-        verifyNumberOfDownloads("resources/dummy.js", 1);
-        verifyNumberOfDownloads("resources/dummy.css", 1);
-        verifyNumberOfDownloads("/fonts/CanvasTest.ttf", 1);
-        verifyNumberOfDownloads("resources/white.mp4", 1);
-        verifyNumberOfDownloads("resources/sound_5.oga", 1);
-        verifyNumberOfDownloads("resources/foo.vtt", 1);
-        verifyNumberOfDownloads("resources/dummy.xml?foo=bar", 0);
-        verifyNumberOfDownloads("resources/dummy.xml?novalue", 0);
-        verifyNumberOfDownloads("resources/dummy.xml", 1);
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport()
+            verifyNumberOfResourceTimingEntries("resources/dummy.js", 1);
+            verifyNumberOfResourceTimingEntries("resources/dummy.css", 1);
+            verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 1);
+            verifyNumberOfResourceTimingEntries("resources/white.mp4", 1);
+            verifyNumberOfResourceTimingEntries("resources/sound_5.oga", 1);
+            verifyNumberOfResourceTimingEntries("resources/foo.vtt", 1);
+            verifyNumberOfResourceTimingEntries("resources/dummy.xml?foo=bar", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.xml?novalue", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.xml", 1);
+            t.done();
+        }, 5000);
     }));
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-imagesrcset.tentative.html b/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-imagesrcset.tentative.html
index be8f0af..e1b8431 100644
--- a/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-imagesrcset.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-imagesrcset.tentative.html
@@ -17,15 +17,14 @@
         link.imageSizes = "400px";
         link.onload = t.step_func(function() {
             t.step_timeout(function() {
-                verifyNumberOfDownloads("resources/square.png?default", 0);
-                verifyNumberOfDownloads("resources/square.png?200", 0);
-                verifyNumberOfDownloads("resources/square.png?400", 1);
-                verifyNumberOfDownloads("resources/square.png?800", 0);
+                verifyNumberOfResourceTimingEntries("resources/square.png?default", 0);
+                verifyNumberOfResourceTimingEntries("resources/square.png?200", 0);
+                verifyNumberOfResourceTimingEntries("resources/square.png?400", 1);
+                verifyNumberOfResourceTimingEntries("resources/square.png?800", 0);
                 t.done();
             }, 0);
         });
         document.body.appendChild(link);
     });
 </script>
-<script src="resources/dummy.js?pipe=trickle(d5)&dynamic-adding-preload"></script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-nonce.html b/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-nonce.html
index 10dae6b9..19e09472e 100644
--- a/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-nonce.html
+++ b/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload-nonce.html
@@ -14,7 +14,7 @@
     link.nonce = "abc";
     link.onload = link.onerror = t.step_func(function() {
         t.step_timeout(function() {
-            verifyNumberOfDownloads("resources/dummy.js?with-nonce", 1);
+            verifyNumberOfResourceTimingEntries("resources/dummy.js?with-nonce", 1);
             t.done();
         }, 0);
     });
@@ -29,7 +29,7 @@
     link.href = "resources/dummy.js?without-nonce";
     link.onload = link.onerror = t.step_func(function() {
         t.step_timeout(function() {
-            verifyNumberOfDownloads("resources/dummy.js?without-nonce", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.js?without-nonce", 0);
             t.done();
         }, 0);
     });
diff --git a/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload.html b/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload.html
index 2a299bd..0cecc198 100644
--- a/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload.html
+++ b/third_party/blink/web_tests/external/wpt/preload/dynamic-adding-preload.html
@@ -15,12 +15,11 @@
         link.href = "resources/dummy.js?dynamic-adding-preload";
         link.onload = t.step_func(function() {
             t.step_timeout(function() {
-                verifyNumberOfDownloads("resources/dummy.js?dynamic-adding-preload", 1);
+                verifyNumberOfResourceTimingEntries("resources/dummy.js?dynamic-adding-preload", 1);
                 t.done();
             }, 0);
         });
         document.body.appendChild(link);
     });
 </script>
-<script src="resources/dummy.js?pipe=trickle(d5)&dynamic-adding-preload"></script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-on-subresource.html b/third_party/blink/web_tests/external/wpt/preload/link-header-on-subresource.html
index a02bc7c..087a342 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-on-subresource.html
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-on-subresource.html
@@ -6,12 +6,13 @@
     var t = async_test('Makes sure that Link headers on subresources preload resources');
 </script>
 <link rel=stylesheet href="resources/dummy-preloads-subresource.css?link-header-on-subresource">
-<script src="resources/dummy.js?pipe=trickle(d5)&link-header-on-subresources"></script>
 <script>
     window.addEventListener("load", t.step_func(function() {
-        verifyPreloadAndRTSupport();
-        verifyNumberOfDownloads("/fonts/CanvasTest.ttf?link-header-on-subresource", 1);
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport();
+            verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf?link-header-on-subresource", 1);
+            t.done();
+        }, 5000);
     }));
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-delay-onload.html b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-delay-onload.html
index 7f38f8c..a445d80 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-delay-onload.html
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-delay-onload.html
@@ -31,10 +31,10 @@
             }
         }
         assert_true(found_background_first);
-        verifyNumberOfDownloads("resources/square.png?link-header-preload-delay-onload", 1);
-        verifyNumberOfDownloads("resources/square.png?background", 1);
-        verifyNumberOfDownloads("resources/dummy.js?link-header-preload-delay-onload", 1);
-        verifyNumberOfDownloads("resources/dummy.css?link-header-preload-delay-onload", 1);
+        verifyLoadedAndNoDoubleDownload("resources/square.png?link-header-preload-delay-onload");
+        verifyLoadedAndNoDoubleDownload("resources/square.png?background");
+        verifyLoadedAndNoDoubleDownload("resources/dummy.js?link-header-preload-delay-onload");
+        verifyLoadedAndNoDoubleDownload("resources/dummy.css?link-header-preload-delay-onload");
         t.done();
     }));
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html
index 240d6f1..dc1ec10 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-nonce.html
@@ -5,12 +5,13 @@
 <script nonce="abc">
     var t = async_test('Makes sure that Link headers preload resources with CSP nonce');
 </script>
-<script nonce="abc" src="resources/dummy.js?pipe=trickle(d5)&link-header-preload-nonce"></script>
 <script nonce="abc">
     window.addEventListener('load', t.step_func(function() {
-        verifyPreloadAndRTSupport();
-        verifyNumberOfResourceTimingEntries("resources/dummy.js?from-header&without-nonce", 0);
-        verifyNumberOfResourceTimingEntries("resources/dummy.js?from-header&with-nonce", 1);
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport();
+            verifyNumberOfResourceTimingEntries("resources/dummy.js?from-header&without-nonce", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.js?from-header&with-nonce", 1);
+            t.done();
+        }, 5000);
     }));
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html
index 024da96..8d057549 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-preload-srcset.tentative.html
@@ -7,21 +7,22 @@
     var t = async_test('Makes sure that Link headers preload images with (experimental) imagesrcset/imagesizes attributes.');
 </script>
 <body>
-<script src="resources/dummy.js?pipe=trickle(d3)&link-header-preload-srcset"></script>
 <script>
     window.addEventListener("load", t.step_func(function() {
-        verifyPreloadAndRTSupport();
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&1x', 1);
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&2x', 0);
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&3x', 0);
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&base', 0);
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&200', 0);
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&400', 1);
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&800', 0);
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&150', 0);
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&300', 1);
-        verifyNumberOfResourceTimingEntries('resources/square.png?from-header&600', 0);
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport();
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&1x', 1);
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&2x', 0);
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&3x', 0);
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&base', 0);
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&200', 0);
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&400', 1);
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&800', 0);
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&150', 0);
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&300', 1);
+            verifyNumberOfResourceTimingEntries('resources/square.png?from-header&600', 0);
+            t.done();
+        }, 3000);
     }));
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/link-header-preload.html b/third_party/blink/web_tests/external/wpt/preload/link-header-preload.html
index 94a731b..0ca364b 100644
--- a/third_party/blink/web_tests/external/wpt/preload/link-header-preload.html
+++ b/third_party/blink/web_tests/external/wpt/preload/link-header-preload.html
@@ -6,14 +6,15 @@
     var t = async_test('Makes sure that Link headers preload resources');
 </script>
 <body>
-<script src="resources/dummy.js?pipe=trickle(d5)&link-header-preload"></script>
 <script>
     window.addEventListener("load", t.step_func(function() {
-        verifyPreloadAndRTSupport();
-        verifyNumberOfDownloads("resources/square.png?link-header-preload", 1);
-        verifyNumberOfDownloads("resources/dummy.js?link-header-preload", 1);
-        verifyNumberOfDownloads("resources/dummy.css?link-header-preload", 1);
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport();
+            verifyNumberOfResourceTimingEntries("resources/square.png?link-header-preload", 1);
+            verifyNumberOfResourceTimingEntries("resources/dummy.js?link-header-preload", 1);
+            verifyNumberOfResourceTimingEntries("resources/dummy.css?link-header-preload", 1);
+            t.done();
+        }, 5000);
     }));
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/onerror-event.html b/third_party/blink/web_tests/external/wpt/preload/onerror-event.html
index 5fae70d..8190be87 100644
--- a/third_party/blink/web_tests/external/wpt/preload/onerror-event.html
+++ b/third_party/blink/web_tests/external/wpt/preload/onerror-event.html
@@ -28,21 +28,22 @@
 <link rel=preload href="non-existent/dummy.xml?foo" as=foobarxmlthing onerror="gibberishFailed = true;">
 <link rel=preload href="non-existent/dummy.xml?fetch" as=fetch onerror="fetchFailed = true;">
 <link rel=preload href="non-existent/dummy.xml?empty" onerror="emptyFailed = true;">
-<script src="resources/dummy.js?pipe=trickle(d5)&onerror-event"></script>
 <script>
     window.onload = t.step_func(function() {
-        verifyPreloadAndRTSupport();
-        assert_true(styleFailed, "style triggered error event");
-        assert_true(scriptFailed, "script triggered error event");
-        assert_true(imageFailed, "image triggered error event");
-        assert_true(fontFailed, "font triggered error event");
-        assert_true(videoFailed, "video triggered error event");
-        assert_true(audioFailed, "audio triggered error event");
-        assert_true(trackFailed, "track triggered error event");
-        assert_false(gibberishFailed, "gibberish as value did not trigger error event");
-        assert_true(fetchFailed, "fetch as triggered error event");
-        assert_false(emptyFailed, "empty as triggered error event");
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport();
+            assert_true(styleFailed, "style triggered error event");
+            assert_true(scriptFailed, "script triggered error event");
+            assert_true(imageFailed, "image triggered error event");
+            assert_true(fontFailed, "font triggered error event");
+            assert_true(videoFailed, "video triggered error event");
+            assert_true(audioFailed, "audio triggered error event");
+            assert_true(trackFailed, "track triggered error event");
+            assert_false(gibberishFailed, "gibberish as value did not trigger error event");
+            assert_true(fetchFailed, "fetch as triggered error event");
+            assert_false(emptyFailed, "empty as triggered error event");
+            t.done();
+        }, 5000);
     });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/onload-event.html b/third_party/blink/web_tests/external/wpt/preload/onload-event.html
index 6af2d64a..f9348b8c 100644
--- a/third_party/blink/web_tests/external/wpt/preload/onload-event.html
+++ b/third_party/blink/web_tests/external/wpt/preload/onload-event.html
@@ -27,22 +27,23 @@
 <link rel=preload href="resources/dummy.xml?fetch" as=fetch onload="fetchLoaded = true;">
 <link rel=preload href="resources/dummy.xml" onload="noTypeLoaded = true;">
 <body>
-<script src="resources/dummy.js?pipe=trickle(d5)&onload-event"></script>
 <script>
     window.onload = t.step_func(function() {
-        verifyPreloadAndRTSupport();
-        assert_true(styleLoaded, "style triggered load event");
-        assert_true(scriptLoaded, "script triggered load event");
-        assert_true(imageLoaded, "image triggered load event");
-        assert_true(fontLoaded, "font triggered load event");
-        assert_true(videoLoaded, "video triggered load event");
-        assert_true(audioLoaded, "audio triggered load event");
-        assert_true(trackLoaded, "track triggered load event");
-        assert_false(gibberishLoaded, "gibberish as value triggered load event");
-        assert_false(gibberishErrored, "gibberish as value triggered error event");
-        assert_true(fetchLoaded, "fetch as value triggered load event");
-        assert_false(noTypeLoaded, "empty as triggered load event");
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport();
+            assert_true(styleLoaded, "style triggered load event");
+            assert_true(scriptLoaded, "script triggered load event");
+            assert_true(imageLoaded, "image triggered load event");
+            assert_true(fontLoaded, "font triggered load event");
+            assert_true(videoLoaded, "video triggered load event");
+            assert_true(audioLoaded, "audio triggered load event");
+            assert_true(trackLoaded, "track triggered load event");
+            assert_false(gibberishLoaded, "gibberish as value triggered load event");
+            assert_false(gibberishErrored, "gibberish as value triggered error event");
+            assert_true(fetchLoaded, "fetch as value triggered load event");
+            assert_false(noTypeLoaded, "empty as triggered load event");
+            t.done();
+        }, 5000);
     });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html b/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html
index 8e5e45b..7fe06eb0 100644
--- a/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-csp.sub.html
@@ -16,19 +16,20 @@
 <link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing>
 <link rel=preload href="resources/dummy.xml">
 <body>
-<script src="resources/dummy.js?pipe=trickle(d5)&preload-csp"></script>
 <script>
     window.onload = t.step_func(function() {
-        verifyPreloadAndRTSupport();
-        verifyNumberOfDownloads("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
-        verifyNumberOfDownloads("resources/dummy.css", 0);
-        verifyNumberOfDownloads("resources/square.png", 0);
-        verifyNumberOfDownloads("/fonts/CanvasTest.ttf", 0);
-        verifyNumberOfDownloads("resources/white.mp4", 0);
-        verifyNumberOfDownloads("resources/sound_5.oga", 0);
-        verifyNumberOfDownloads("resources/foo.vtt", 0);
-        verifyNumberOfDownloads("resources/dummy.xml", 0);
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport();
+            verifyNumberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.css", 0);
+            verifyNumberOfResourceTimingEntries("resources/square.png", 0);
+            verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 0);
+            verifyNumberOfResourceTimingEntries("resources/white.mp4", 0);
+            verifyNumberOfResourceTimingEntries("resources/sound_5.oga", 0);
+            verifyNumberOfResourceTimingEntries("resources/foo.vtt", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.xml", 0);
+            t.done();
+        }, 5000);
     });
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html b/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html
index cb080e6..7813e36 100644
--- a/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-default-csp.sub.html
@@ -16,19 +16,20 @@
 <link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing>
 <link rel=preload href="resources/dummy.xml">
 <body>
-<script src="resources/dummy.js?pipe=trickle(d5)&preload-default-csp"></script>
 <script>
     window.onload = t.step_func(function() {
-        verifyPreloadAndRTSupport();
-        verifyNumberOfDownloads("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
-        verifyNumberOfDownloads("resources/dummy.css", 0);
-        verifyNumberOfDownloads("resources/square.png", 0);
-        verifyNumberOfDownloads("/fonts/CanvasTest.ttf", 0);
-        verifyNumberOfDownloads("resources/white.mp4", 0);
-        verifyNumberOfDownloads("resources/sound_5.oga", 0);
-        verifyNumberOfDownloads("resources/foo.vtt", 0);
-        verifyNumberOfDownloads("resources/dummy.xml", 0);
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport();
+            verifyNumberOfResourceTimingEntries("{{host}}:{{ports[http][1]}}/preload/resources/dummy.js", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.css", 0);
+            verifyNumberOfResourceTimingEntries("resources/square.png", 0);
+            verifyNumberOfResourceTimingEntries("/fonts/CanvasTest.ttf", 0);
+            verifyNumberOfResourceTimingEntries("resources/white.mp4", 0);
+            verifyNumberOfResourceTimingEntries("resources/sound_5.oga", 0);
+            verifyNumberOfResourceTimingEntries("resources/foo.vtt", 0);
+            verifyNumberOfResourceTimingEntries("resources/dummy.xml", 0);
+            t.done();
+        }, 5000);
     });
 </script>
 
diff --git a/third_party/blink/web_tests/external/wpt/preload/preload-with-type.html b/third_party/blink/web_tests/external/wpt/preload/preload-with-type.html
index 8578143..83eafc5 100644
--- a/third_party/blink/web_tests/external/wpt/preload/preload-with-type.html
+++ b/third_party/blink/web_tests/external/wpt/preload/preload-with-type.html
@@ -47,19 +47,20 @@
 </script>
 <link rel=preload href="resources/foo.vtt" as=track type="text/foobar" onload="gibberishLoaded++;">
 <body>
-<script src="resources/dummy.js?pipe=trickle(d5)&preload-with-type"></script>
 <script>
     window.onload = t.step_func(function() {
-        verifyPreloadAndRTSupport();
-        assert_true(styleLoaded, "style triggered load event");
-        assert_true(scriptLoaded, "script triggered load event");
-        assert_true(imageLoaded, "image triggered load event");
-        assert_true(fontLoaded, "font triggered load event");
-        assert_true(videoLoaded, "video triggered load event");
-        assert_true(audioLoaded, "audio triggered load event");
-        assert_true(trackLoaded, "track triggered load event");
-        assert_equals(gibberishLoaded, 0, "resources with gibberish type should not be loaded");
-        t.done();
+        t.step_timeout(function() {
+            verifyPreloadAndRTSupport();
+            assert_true(styleLoaded, "style triggered load event");
+            assert_true(scriptLoaded, "script triggered load event");
+            assert_true(imageLoaded, "image triggered load event");
+            assert_true(fontLoaded, "font triggered load event");
+            assert_true(videoLoaded, "video triggered load event");
+            assert_true(audioLoaded, "audio triggered load event");
+            assert_true(trackLoaded, "track triggered load event");
+            assert_equals(gibberishLoaded, 0, "resources with gibberish type should not be loaded");
+            t.done();
+        }, 5000);
     });
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/preload/resources/preload_helper.js b/third_party/blink/web_tests/external/wpt/preload/resources/preload_helper.js
index b2cf832..f464908 100644
--- a/third_party/blink/web_tests/external/wpt/preload/resources/preload_helper.js
+++ b/third_party/blink/web_tests/external/wpt/preload/resources/preload_helper.js
@@ -10,19 +10,32 @@
     return new URL(url, location.href).href;
 }
 
-function verifyNumberOfDownloads(url, number)
-{
-    var numDownloads = 0;
-    performance.getEntriesByName(getAbsoluteURL(url)).forEach(entry => {
-        if (entry.transferSize > 0) {
-            numDownloads++;
-        }
-    });
-    assert_equals(numDownloads, number, url);
-}
-
 function verifyNumberOfResourceTimingEntries(url, number)
 {
     var numEntries = performance.getEntriesByName(getAbsoluteURL(url)).length;
     assert_equals(numEntries, number, url);
 }
+
+// Verifies that the resource is loaded, but not downloaded from network
+// more than once. This can be used to verify that a preloaded resource is
+// not downloaded again when used.
+function verifyLoadedAndNoDoubleDownload(url) {
+    var entries = performance.getEntriesByName(getAbsoluteURL(url));
+    // UA may create separate RT entries for preload and normal load,
+    // so we just check (entries.length > 0).
+    assert_greater_than(entries.length, 0, url + ' should be loaded');
+
+    var numDownloads = 0;
+    entries.forEach(entry => {
+        // transferSize is zero if the resource is loaded from cache.
+        if (entry.transferSize > 0) {
+            numDownloads++;
+        }
+    });
+    // numDownloads can be zero if the resource was already cached before running
+    // the test (for example, when the test is running repeatedly without
+    // clearing cache between runs).
+    assert_less_than_equal(
+        numDownloads, 1,
+        url + ' should be downloaded from network at most once');
+}
diff --git a/third_party/blink/web_tests/external/wpt/preload/single-download-late-used-preload.html b/third_party/blink/web_tests/external/wpt/preload/single-download-late-used-preload.html
index 5549cb8..51533ba 100644
--- a/third_party/blink/web_tests/external/wpt/preload/single-download-late-used-preload.html
+++ b/third_party/blink/web_tests/external/wpt/preload/single-download-late-used-preload.html
@@ -9,11 +9,11 @@
     assert_equals(link.as, "image");
     link.addEventListener("load", () => {
         verifyPreloadAndRTSupport();
-        verifyNumberOfDownloads("resources/square.png?pipe=trickle(d1)", 1);
+        verifyNumberOfResourceTimingEntries("resources/square.png?pipe=trickle(d1)", 1);
         var img = document.createElement("img");
         img.src = "resources/square.png?pipe=trickle(d1)";
         img.onload = () => {
-            verifyNumberOfDownloads("resources/square.png?pipe=trickle(d1)", 1);
+            verifyLoadedAndNoDoubleDownload("resources/square.png?pipe=trickle(d1)");
             done();
         };
         document.body.appendChild(img);
diff --git a/third_party/blink/web_tests/external/wpt/preload/single-download-preload.html b/third_party/blink/web_tests/external/wpt/preload/single-download-preload.html
index e8f2617..16d893c 100644
--- a/third_party/blink/web_tests/external/wpt/preload/single-download-preload.html
+++ b/third_party/blink/web_tests/external/wpt/preload/single-download-preload.html
@@ -16,7 +16,6 @@
 <link rel=preload href="resources/dummy.xml?foo=bar" as=foobarxmlthing>
 <link rel=preload href="resources/dummy.xml?single-download-preload">
 <body>
-<script src="resources/dummy.js?pipe=trickle(d3)&single-download-preload"></script>
 <style>
     #background {
         width: 200px;
@@ -45,17 +44,17 @@
     window.addEventListener("load", t.step_func(function() {
         verifyPreloadAndRTSupport();
         setTimeout(t.step_func(function() {
-            verifyNumberOfDownloads("resources/dummy.js?single-download-preload", 1);
-            verifyNumberOfDownloads("resources/dummy.css?single-download-preload", 1);
-            verifyNumberOfDownloads("resources/square.png?single-download-preload", 1);
-            verifyNumberOfDownloads("resources/square.png?background&single-download-preload", 1);
-            verifyNumberOfDownloads("/fonts/CanvasTest.ttf?single-download-preload", 1);
-            verifyNumberOfDownloads("resources/dummy.xml?foobar", 0);
-            verifyNumberOfDownloads("resources/foo.vtt?single-download-preload", 1);
-            verifyNumberOfDownloads("resources/dummy.xml?single-download-preload", 1);
+            verifyLoadedAndNoDoubleDownload("resources/dummy.js?single-download-preload");
+            verifyLoadedAndNoDoubleDownload("resources/dummy.css?single-download-preload");
+            verifyLoadedAndNoDoubleDownload("resources/square.png?single-download-preload");
+            verifyLoadedAndNoDoubleDownload("resources/square.png?background&single-download-preload");
+            verifyLoadedAndNoDoubleDownload("/fonts/CanvasTest.ttf?single-download-preload");
+            verifyNumberOfResourceTimingEntries("resources/dummy.xml?foobar", 0);
+            verifyLoadedAndNoDoubleDownload("resources/foo.vtt?single-download-preload");
+            verifyLoadedAndNoDoubleDownload("resources/dummy.xml?single-download-preload");
             // FIXME: We should verify for video and audio as well, but they seem to (flakily?) trigger multiple partial requests.
             t.done();
-        }), 0);
+        }), 3000);
     }));
 </script>
 <span>PASS - this text is here just so that the browser will download the font.</span>
diff --git a/third_party/blink/web_tests/external/wpt/resources/testharness.js b/third_party/blink/web_tests/external/wpt/resources/testharness.js
index fb86c580..d40817c 100644
--- a/third_party/blink/web_tests/external/wpt/resources/testharness.js
+++ b/third_party/blink/web_tests/external/wpt/resources/testharness.js
@@ -635,7 +635,7 @@
      * which can make it a lot easier to test a very specific series of events,
      * including ensuring that unexpected events are not fired at any point.
      */
-    function EventWatcher(test, watchedNode, eventTypes)
+    function EventWatcher(test, watchedNode, eventTypes, timeoutPromise)
     {
         if (typeof eventTypes == 'string') {
             eventTypes = [eventTypes];
@@ -712,6 +712,27 @@
                 recordedEvents = [];
             }
             return new Promise(function(resolve, reject) {
+                var timeout = test.step_func(function() {
+                    // If the timeout fires after the events have been received
+                    // or during a subsequent call to wait_for, ignore it.
+                    if (!waitingFor || waitingFor.resolve !== resolve)
+                        return;
+
+                    // This should always fail, otherwise we should have
+                    // resolved the promise.
+                    assert_true(waitingFor.types.length == 0,
+                                'Timed out waiting for ' + waitingFor.types.join(', '));
+                    var result = recordedEvents;
+                    recordedEvents = null;
+                    var resolveFunc = waitingFor.resolve;
+                    waitingFor = null;
+                    resolveFunc(result);
+                });
+
+                if (timeoutPromise) {
+                    timeoutPromise().then(timeout);
+                }
+
                 waitingFor = {
                     types: types,
                     resolve: resolve,
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any-expected.txt b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any-expected.txt
deleted file mode 100644
index f2ded61..0000000
--- a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any-expected.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-This is a testharness.js-based test.
-Found 55 tests; 54 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Aborting a WritableStream before it starts should cause the writer's unsettled ready promise to reject
-PASS Aborting a WritableStream should cause the writer's fulfilled ready promise to reset to a rejected one
-PASS abort() on a released writer rejects
-PASS Aborting a WritableStream immediately prevents future writes
-PASS Aborting a WritableStream prevents further writes after any that are in progress
-PASS Fulfillment value of ws.abort() call must be undefined even if the underlying sink returns a non-undefined value
-PASS WritableStream if sink's abort throws, the promise returned by writer.abort() rejects
-PASS WritableStream if sink's abort throws, the promise returned by multiple writer.abort()s is the same and rejects
-PASS WritableStream if sink's abort throws, the promise returned by ws.abort() rejects
-PASS WritableStream if sink's abort throws, for an abort performed during a write, the promise returned by ws.abort() rejects
-PASS Aborting a WritableStream passes through the given reason
-PASS Aborting a WritableStream puts it in an errored state with the error passed to abort()
-PASS Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied
-PASS Closing but then immediately aborting a WritableStream causes the stream to error
-PASS Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt
-PASS Aborting a WritableStream after it is closed is a no-op
-PASS WritableStream should NOT call underlying sink's close if no abort is supplied (historical)
-PASS returning a thenable from abort() should work
-PASS .closed should not resolve before fulfilled write()
-PASS .closed should not resolve before rejected write(); write() error should not overwrite abort() error
-PASS writes should be satisfied in order when aborting
-PASS writes should be satisfied in order after rejected write when aborting
-PASS close() should reject with abort reason why abort() is first error
-PASS underlying abort() should not be called until underlying write() completes
-PASS underlying abort() should not be called if underlying close() has started
-PASS if underlying close() has started and then rejects, the abort() and close() promises should reject with the underlying close rejection reason
-PASS an abort() that happens during a write() should trigger the underlying abort() even with a close() queued
-PASS if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error
-PASS writer close() promise should resolve before abort() promise
-PASS writer.ready should reject on controller error without waiting for underlying write
-PASS writer.abort() while there is an in-flight write, and then finish the write with rejection
-PASS writer.abort(), controller.error() while there is an in-flight write, and then finish the write
-PASS writer.abort(), controller.error() while there is an in-flight close, and then finish the close
-PASS controller.error(), writer.abort() while there is an in-flight write, and then finish the write
-PASS controller.error(), writer.abort() while there is an in-flight close, and then finish the close
-PASS releaseLock() while aborting should reject the original closed promise
-PASS releaseLock() during delayed async abort() should reject the writer.closed promise
-PASS sink abort() should not be called until sink start() is done
-PASS if start attempts to error the controller after abort() has been called, then it should lose
-PASS stream abort() promise should still resolve if sink start() rejects
-PASS writer abort() during sink start() should replace the writer.ready promise synchronously
-PASS promises returned from other writer methods should be rejected when writer abort() happens during sink start()
-PASS abort() should succeed despite rejection from write
-PASS abort() should be rejected with the rejection returned from close()
-PASS a rejecting sink.write() should not prevent sink.abort() from being called
-PASS when start errors after stream abort(), underlying sink abort() should be called anyway
-PASS when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined
-PASS when calling abort() twice on the same stream, but sequentially so so there's no pending abort the second time, both should fulfill with undefined
-PASS calling abort() on an errored stream should fulfill with undefined
-PASS sink abort() should not be called if stream was erroring due to controller.error() before abort() was called
-PASS sink abort() should not be called if stream was erroring due to bad strategy before abort() was called
-PASS abort with no arguments should set the stored error to undefined
-PASS abort with an undefined argument should set the stored error to undefined
-PASS abort with a string argument should set the stored error to that argument
-FAIL abort on a locked stream should reject promise_test: Unhandled rejection with value: undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.serviceworker-expected.txt b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.serviceworker-expected.txt
deleted file mode 100644
index f2ded61..0000000
--- a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.serviceworker-expected.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-This is a testharness.js-based test.
-Found 55 tests; 54 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Aborting a WritableStream before it starts should cause the writer's unsettled ready promise to reject
-PASS Aborting a WritableStream should cause the writer's fulfilled ready promise to reset to a rejected one
-PASS abort() on a released writer rejects
-PASS Aborting a WritableStream immediately prevents future writes
-PASS Aborting a WritableStream prevents further writes after any that are in progress
-PASS Fulfillment value of ws.abort() call must be undefined even if the underlying sink returns a non-undefined value
-PASS WritableStream if sink's abort throws, the promise returned by writer.abort() rejects
-PASS WritableStream if sink's abort throws, the promise returned by multiple writer.abort()s is the same and rejects
-PASS WritableStream if sink's abort throws, the promise returned by ws.abort() rejects
-PASS WritableStream if sink's abort throws, for an abort performed during a write, the promise returned by ws.abort() rejects
-PASS Aborting a WritableStream passes through the given reason
-PASS Aborting a WritableStream puts it in an errored state with the error passed to abort()
-PASS Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied
-PASS Closing but then immediately aborting a WritableStream causes the stream to error
-PASS Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt
-PASS Aborting a WritableStream after it is closed is a no-op
-PASS WritableStream should NOT call underlying sink's close if no abort is supplied (historical)
-PASS returning a thenable from abort() should work
-PASS .closed should not resolve before fulfilled write()
-PASS .closed should not resolve before rejected write(); write() error should not overwrite abort() error
-PASS writes should be satisfied in order when aborting
-PASS writes should be satisfied in order after rejected write when aborting
-PASS close() should reject with abort reason why abort() is first error
-PASS underlying abort() should not be called until underlying write() completes
-PASS underlying abort() should not be called if underlying close() has started
-PASS if underlying close() has started and then rejects, the abort() and close() promises should reject with the underlying close rejection reason
-PASS an abort() that happens during a write() should trigger the underlying abort() even with a close() queued
-PASS if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error
-PASS writer close() promise should resolve before abort() promise
-PASS writer.ready should reject on controller error without waiting for underlying write
-PASS writer.abort() while there is an in-flight write, and then finish the write with rejection
-PASS writer.abort(), controller.error() while there is an in-flight write, and then finish the write
-PASS writer.abort(), controller.error() while there is an in-flight close, and then finish the close
-PASS controller.error(), writer.abort() while there is an in-flight write, and then finish the write
-PASS controller.error(), writer.abort() while there is an in-flight close, and then finish the close
-PASS releaseLock() while aborting should reject the original closed promise
-PASS releaseLock() during delayed async abort() should reject the writer.closed promise
-PASS sink abort() should not be called until sink start() is done
-PASS if start attempts to error the controller after abort() has been called, then it should lose
-PASS stream abort() promise should still resolve if sink start() rejects
-PASS writer abort() during sink start() should replace the writer.ready promise synchronously
-PASS promises returned from other writer methods should be rejected when writer abort() happens during sink start()
-PASS abort() should succeed despite rejection from write
-PASS abort() should be rejected with the rejection returned from close()
-PASS a rejecting sink.write() should not prevent sink.abort() from being called
-PASS when start errors after stream abort(), underlying sink abort() should be called anyway
-PASS when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined
-PASS when calling abort() twice on the same stream, but sequentially so so there's no pending abort the second time, both should fulfill with undefined
-PASS calling abort() on an errored stream should fulfill with undefined
-PASS sink abort() should not be called if stream was erroring due to controller.error() before abort() was called
-PASS sink abort() should not be called if stream was erroring due to bad strategy before abort() was called
-PASS abort with no arguments should set the stored error to undefined
-PASS abort with an undefined argument should set the stored error to undefined
-PASS abort with a string argument should set the stored error to that argument
-FAIL abort on a locked stream should reject promise_test: Unhandled rejection with value: undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.sharedworker-expected.txt b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.sharedworker-expected.txt
deleted file mode 100644
index f2ded61..0000000
--- a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.sharedworker-expected.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-This is a testharness.js-based test.
-Found 55 tests; 54 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Aborting a WritableStream before it starts should cause the writer's unsettled ready promise to reject
-PASS Aborting a WritableStream should cause the writer's fulfilled ready promise to reset to a rejected one
-PASS abort() on a released writer rejects
-PASS Aborting a WritableStream immediately prevents future writes
-PASS Aborting a WritableStream prevents further writes after any that are in progress
-PASS Fulfillment value of ws.abort() call must be undefined even if the underlying sink returns a non-undefined value
-PASS WritableStream if sink's abort throws, the promise returned by writer.abort() rejects
-PASS WritableStream if sink's abort throws, the promise returned by multiple writer.abort()s is the same and rejects
-PASS WritableStream if sink's abort throws, the promise returned by ws.abort() rejects
-PASS WritableStream if sink's abort throws, for an abort performed during a write, the promise returned by ws.abort() rejects
-PASS Aborting a WritableStream passes through the given reason
-PASS Aborting a WritableStream puts it in an errored state with the error passed to abort()
-PASS Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied
-PASS Closing but then immediately aborting a WritableStream causes the stream to error
-PASS Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt
-PASS Aborting a WritableStream after it is closed is a no-op
-PASS WritableStream should NOT call underlying sink's close if no abort is supplied (historical)
-PASS returning a thenable from abort() should work
-PASS .closed should not resolve before fulfilled write()
-PASS .closed should not resolve before rejected write(); write() error should not overwrite abort() error
-PASS writes should be satisfied in order when aborting
-PASS writes should be satisfied in order after rejected write when aborting
-PASS close() should reject with abort reason why abort() is first error
-PASS underlying abort() should not be called until underlying write() completes
-PASS underlying abort() should not be called if underlying close() has started
-PASS if underlying close() has started and then rejects, the abort() and close() promises should reject with the underlying close rejection reason
-PASS an abort() that happens during a write() should trigger the underlying abort() even with a close() queued
-PASS if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error
-PASS writer close() promise should resolve before abort() promise
-PASS writer.ready should reject on controller error without waiting for underlying write
-PASS writer.abort() while there is an in-flight write, and then finish the write with rejection
-PASS writer.abort(), controller.error() while there is an in-flight write, and then finish the write
-PASS writer.abort(), controller.error() while there is an in-flight close, and then finish the close
-PASS controller.error(), writer.abort() while there is an in-flight write, and then finish the write
-PASS controller.error(), writer.abort() while there is an in-flight close, and then finish the close
-PASS releaseLock() while aborting should reject the original closed promise
-PASS releaseLock() during delayed async abort() should reject the writer.closed promise
-PASS sink abort() should not be called until sink start() is done
-PASS if start attempts to error the controller after abort() has been called, then it should lose
-PASS stream abort() promise should still resolve if sink start() rejects
-PASS writer abort() during sink start() should replace the writer.ready promise synchronously
-PASS promises returned from other writer methods should be rejected when writer abort() happens during sink start()
-PASS abort() should succeed despite rejection from write
-PASS abort() should be rejected with the rejection returned from close()
-PASS a rejecting sink.write() should not prevent sink.abort() from being called
-PASS when start errors after stream abort(), underlying sink abort() should be called anyway
-PASS when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined
-PASS when calling abort() twice on the same stream, but sequentially so so there's no pending abort the second time, both should fulfill with undefined
-PASS calling abort() on an errored stream should fulfill with undefined
-PASS sink abort() should not be called if stream was erroring due to controller.error() before abort() was called
-PASS sink abort() should not be called if stream was erroring due to bad strategy before abort() was called
-PASS abort with no arguments should set the stored error to undefined
-PASS abort with an undefined argument should set the stored error to undefined
-PASS abort with a string argument should set the stored error to that argument
-FAIL abort on a locked stream should reject promise_test: Unhandled rejection with value: undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.worker-expected.txt b/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.worker-expected.txt
deleted file mode 100644
index f2ded61..0000000
--- a/third_party/blink/web_tests/external/wpt/streams/writable-streams/aborting.any.worker-expected.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-This is a testharness.js-based test.
-Found 55 tests; 54 PASS, 1 FAIL, 0 TIMEOUT, 0 NOTRUN.
-PASS Aborting a WritableStream before it starts should cause the writer's unsettled ready promise to reject
-PASS Aborting a WritableStream should cause the writer's fulfilled ready promise to reset to a rejected one
-PASS abort() on a released writer rejects
-PASS Aborting a WritableStream immediately prevents future writes
-PASS Aborting a WritableStream prevents further writes after any that are in progress
-PASS Fulfillment value of ws.abort() call must be undefined even if the underlying sink returns a non-undefined value
-PASS WritableStream if sink's abort throws, the promise returned by writer.abort() rejects
-PASS WritableStream if sink's abort throws, the promise returned by multiple writer.abort()s is the same and rejects
-PASS WritableStream if sink's abort throws, the promise returned by ws.abort() rejects
-PASS WritableStream if sink's abort throws, for an abort performed during a write, the promise returned by ws.abort() rejects
-PASS Aborting a WritableStream passes through the given reason
-PASS Aborting a WritableStream puts it in an errored state with the error passed to abort()
-PASS Aborting a WritableStream causes any outstanding write() promises to be rejected with the reason supplied
-PASS Closing but then immediately aborting a WritableStream causes the stream to error
-PASS Closing a WritableStream and aborting it while it closes causes the stream to ignore the abort attempt
-PASS Aborting a WritableStream after it is closed is a no-op
-PASS WritableStream should NOT call underlying sink's close if no abort is supplied (historical)
-PASS returning a thenable from abort() should work
-PASS .closed should not resolve before fulfilled write()
-PASS .closed should not resolve before rejected write(); write() error should not overwrite abort() error
-PASS writes should be satisfied in order when aborting
-PASS writes should be satisfied in order after rejected write when aborting
-PASS close() should reject with abort reason why abort() is first error
-PASS underlying abort() should not be called until underlying write() completes
-PASS underlying abort() should not be called if underlying close() has started
-PASS if underlying close() has started and then rejects, the abort() and close() promises should reject with the underlying close rejection reason
-PASS an abort() that happens during a write() should trigger the underlying abort() even with a close() queued
-PASS if a writer is created for a stream with a pending abort, its ready should be rejected with the abort error
-PASS writer close() promise should resolve before abort() promise
-PASS writer.ready should reject on controller error without waiting for underlying write
-PASS writer.abort() while there is an in-flight write, and then finish the write with rejection
-PASS writer.abort(), controller.error() while there is an in-flight write, and then finish the write
-PASS writer.abort(), controller.error() while there is an in-flight close, and then finish the close
-PASS controller.error(), writer.abort() while there is an in-flight write, and then finish the write
-PASS controller.error(), writer.abort() while there is an in-flight close, and then finish the close
-PASS releaseLock() while aborting should reject the original closed promise
-PASS releaseLock() during delayed async abort() should reject the writer.closed promise
-PASS sink abort() should not be called until sink start() is done
-PASS if start attempts to error the controller after abort() has been called, then it should lose
-PASS stream abort() promise should still resolve if sink start() rejects
-PASS writer abort() during sink start() should replace the writer.ready promise synchronously
-PASS promises returned from other writer methods should be rejected when writer abort() happens during sink start()
-PASS abort() should succeed despite rejection from write
-PASS abort() should be rejected with the rejection returned from close()
-PASS a rejecting sink.write() should not prevent sink.abort() from being called
-PASS when start errors after stream abort(), underlying sink abort() should be called anyway
-PASS when calling abort() twice on the same stream, both should give the same promise that fulfills with undefined
-PASS when calling abort() twice on the same stream, but sequentially so so there's no pending abort the second time, both should fulfill with undefined
-PASS calling abort() on an errored stream should fulfill with undefined
-PASS sink abort() should not be called if stream was erroring due to controller.error() before abort() was called
-PASS sink abort() should not be called if stream was erroring due to bad strategy before abort() was called
-PASS abort with no arguments should set the stored error to undefined
-PASS abort with an undefined argument should set the stored error to undefined
-PASS abort with a string argument should set the stored error to that argument
-FAIL abort on a locked stream should reject promise_test: Unhandled rejection with value: undefined
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/external/wpt/streams/writable-streams/write.any.js b/third_party/blink/web_tests/external/wpt/streams/writable-streams/write.any.js
index 85c7f8c..28fcf65 100644
--- a/third_party/blink/web_tests/external/wpt/streams/writable-streams/write.any.js
+++ b/third_party/blink/web_tests/external/wpt/streams/writable-streams/write.any.js
@@ -251,3 +251,27 @@
   });
   return ws.getWriter().write('a').then(() => assert_true(thenCalled, 'thenCalled should be true'));
 }, 'returning a thenable from write() should work');
+
+promise_test(() => {
+  const stream = new WritableStream();
+  const writer = stream.getWriter();
+  const WritableStreamDefaultWriter = writer.constructor;
+  assert_throws(new TypeError(), () => new WritableStreamDefaultWriter(stream),
+                'should not be able to construct on locked stream');
+  // If stream.[[writer]] no longer points to |writer| then the closed Promise
+  // won't work properly.
+  return Promise.all([writer.close(), writer.closed]);
+}, 'failing DefaultWriter constructor should not release an existing writer');
+
+promise_test(t => {
+  const ws = new WritableStream({
+    start() {
+      return Promise.reject(error1);
+    }
+  }, { highWaterMark: 0 });
+  const writer = ws.getWriter();
+  return Promise.all([
+    promise_rejects(t, error1, writer.ready, 'ready should be rejected'),
+    promise_rejects(t, error1, writer.write(), 'write() should be rejected')
+  ]);
+}, 'write() on a stream with HWM 0 should not cause the ready Promise to resolve');
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js b/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js
index 5dbf6fd7..e6dad7c 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js
+++ b/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js
@@ -191,13 +191,13 @@
 function waitForNextFrame() {
   const timeAtStart = document.timeline.currentTime;
   return new Promise(resolve => {
-    window.requestAnimationFrame(() => {
-      if (timeAtStart === document.timeline.currentTime) {
-        window.requestAnimationFrame(resolve);
-      } else {
-        resolve();
-      }
-    });
+   (function handleFrame() {
+    if (timeAtStart === document.timeline.currentTime) {
+      window.requestAnimationFrame(handleFrame);
+    } else {
+      resolve();
+    }
+  }());
   });
 }
 
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-analysernode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffer-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiobuffersourcenode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiocontext-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiodestinationnode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiodestinationnode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiodestinationnode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiolistener-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiolistener-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audiolistener-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audionode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audionode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audionode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioparam-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioprocessingevent-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioprocessingevent-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-audioprocessingevent-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-biquadfilternode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelmergernode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelsplitternode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelsplitternode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-channelsplitternode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-convolvernode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-delaynode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-delaynode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-delaynode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-dynamicscompressornode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-dynamicscompressornode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-dynamicscompressornode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediaelementaudiosourcenode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiodestinationnode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiodestinationnode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiodestinationnode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-mediastreamaudiosourcenode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-offlineaudiocontext-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-offlineaudiocontext-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-offlineaudiocontext-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-oscillatornode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-pannernode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-periodicwave-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-periodicwave-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-periodicwave-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-scriptprocessornode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-scriptprocessornode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-scriptprocessornode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/.gitkeep b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/.gitkeep
deleted file mode 100644
index e69de29..0000000
--- a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-waveshapernode-interface/.gitkeep
+++ /dev/null
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/RTCError.html b/third_party/blink/web_tests/external/wpt/webrtc/RTCError.html
new file mode 100644
index 0000000..e83dba2
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webrtc/RTCError.html
@@ -0,0 +1,84 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>RTCError and RTCErrorInit</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="RTCPeerConnection-helper.js"></script>
+<script>
+'use strict';
+
+test(() => {
+  const error = new RTCError('message', {errorDetail:'data-channel-failure'});
+  assert_equals(error.message, 'message');
+  assert_equals(error.errorDetail, 'data-channel-failure');
+}, 'RTCError constructor with message and errorDetail');
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new RTCError('message');
+  });
+  assert_throws(new TypeError(), () => {
+    new RTCError();
+  });
+}, 'RTCError constructor throws TypeError if any argument is missing');
+
+test(() => {
+  assert_throws(new TypeError(), () => {
+    new RTCError('message', {errorDetail:'invalid-error-detail'});
+  });
+}, 'RTCError constructor throws TypeError if the errorDetail is invalid');
+
+test(() => {
+  const error = new RTCError('message', {errorDetail:'data-channel-failure'});
+  assert_equals(error.name, 'RTCError');
+}, 'RTCError.name is \'RTCError\'');
+
+test(() => {
+  const error = new RTCError('message', {errorDetail:'data-channel-failure'});
+  assert_equals(error.code, 0);
+}, 'RTCError.code is 0');
+
+test(() => {
+  const error = new RTCError('message', {errorDetail:'data-channel-failure'});
+  assert_throws(new TypeError(), () => {
+    error.errorDetail = 'dtls-failure';
+  });
+}, 'RTCError.errorDetail is readonly.');
+
+test(() => {
+  // Infers what are valid RTCErrorInit objects by passing them to the RTCError
+  // constructor.
+  assert_throws(new TypeError(), () => {
+    new RTCError('message', {});
+  });
+  new RTCError('message', {errorDetail:'data-channel-failure'});
+}, 'RTCErrorInit.errorDetail is the only required attribute');
+
+// All of these are number types (long or unsigned long).
+const nullableAttributes = ['sdpLineNumber',
+                            'httpRequestStatusCode',
+                            'sctpCauseCode',
+                            'receivedAlert',
+                            'sentAlert'];
+
+nullableAttributes.forEach(attribute => {
+  test(() => {
+    const error = new RTCError('message', {errorDetail:'data-channel-failure'});
+    assert_equals(error[attribute], null);
+  }, 'RTCError.' + attribute + ' is null by default');
+
+  test(() => {
+    const error = new RTCError('message', {errorDetail:'data-channel-failure',
+                                           [attribute]: 0});
+    assert_equals(error[attribute], 0);
+  }, 'RTCError.' + attribute + ' is settable by constructor');
+
+  test(() => {
+    const error = new RTCError('message', {errorDetail:'data-channel-failure'});
+    assert_throws(new TypeError(), () => {
+      error[attribute] = 42;
+    });
+  }, 'RTCError.' + attribute + ' is readonly');
+});
+
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
index e8123d1..8191ea2 100644
--- a/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 494 tests; 372 PASS, 122 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 494 tests; 378 PASS, 116 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Test driver for asyncInitCertificate
 FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: ReferenceError: RTCSctpTransport is not defined Reached unreachable code
@@ -492,15 +492,15 @@
 FAIL RTCStatsEvent interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "RTCStatsEvent" expected property "RTCStatsEvent" missing
 FAIL RTCStatsEvent interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "RTCStatsEvent" expected property "RTCStatsEvent" missing
 FAIL RTCStatsEvent interface: attribute report assert_own_property: self does not have own property "RTCStatsEvent" expected property "RTCStatsEvent" missing
-FAIL RTCErrorEvent interface: existence and properties of interface object assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface object length assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface object name assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface: existence and properties of interface prototype object assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface: attribute error assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent must be primary interface of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: RTCErrorEvent is not defined"
-FAIL Stringification of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: RTCErrorEvent is not defined"
-FAIL RTCErrorEvent interface: new RTCErrorEvent('error') must inherit property "error" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: RTCErrorEvent is not defined"
+PASS RTCErrorEvent interface: existence and properties of interface object
+FAIL RTCErrorEvent interface object length assert_equals: wrong value for RTCErrorEvent.length expected 1 but got 2
+PASS RTCErrorEvent interface object name
+PASS RTCErrorEvent interface: existence and properties of interface prototype object
+PASS RTCErrorEvent interface: existence and properties of interface prototype object's "constructor" property
+PASS RTCErrorEvent interface: existence and properties of interface prototype object's @@unscopables property
+PASS RTCErrorEvent interface: attribute error
+FAIL RTCErrorEvent must be primary interface of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'RTCErrorEvent': 2 arguments required, but only 1 present."
+FAIL Stringification of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'RTCErrorEvent': 2 arguments required, but only 1 present."
+FAIL RTCErrorEvent interface: new RTCErrorEvent('error') must inherit property "error" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'RTCErrorEvent': 2 arguments required, but only 1 present."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/external/wpt/webstorage/symbol-props.window-expected.txt b/third_party/blink/web_tests/external/wpt/webstorage/symbol-props.window-expected.txt
new file mode 100644
index 0000000..df6dc939
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webstorage/symbol-props.window-expected.txt
@@ -0,0 +1,17 @@
+This is a testharness.js-based test.
+PASS localStorage: plain set + get (loose)
+PASS localStorage: plain set + get (strict)
+PASS localStorage: defineProperty + get
+FAIL localStorage: defineProperty not configurable assert_true: configurable expected true got false
+PASS localStorage: get with symbol on prototype
+PASS localStorage: delete existing property
+PASS localStorage: delete non-existent property
+PASS sessionStorage: plain set + get (loose)
+PASS sessionStorage: plain set + get (strict)
+PASS sessionStorage: defineProperty + get
+FAIL sessionStorage: defineProperty not configurable assert_true: configurable expected true got false
+PASS sessionStorage: get with symbol on prototype
+PASS sessionStorage: delete existing property
+PASS sessionStorage: delete non-existent property
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/external/wpt/webstorage/symbol-props.window.js b/third_party/blink/web_tests/external/wpt/webstorage/symbol-props.window.js
new file mode 100644
index 0000000..61dd8f8
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webstorage/symbol-props.window.js
@@ -0,0 +1,81 @@
+["localStorage", "sessionStorage"].forEach(function(name) {
+    test(function() {
+        var key = Symbol();
+
+        var storage = window[name];
+        storage.clear();
+
+        storage[key] = "test";
+        assert_equals(storage[key], "test");
+    }, name + ": plain set + get (loose)");
+
+    test(function() {
+        "use strict";
+        var key = Symbol();
+
+        var storage = window[name];
+        storage.clear();
+
+        storage[key] = "test";
+        assert_equals(storage[key], "test");
+    }, name + ": plain set + get (strict)");
+
+    test(function() {
+        var key = Symbol();
+
+        var storage = window[name];
+        storage.clear();
+
+        Object.defineProperty(storage, key, { "value": "test" });
+        assert_equals(storage[key], "test");
+    }, name + ": defineProperty + get");
+
+    test(function() {
+        var key = Symbol();
+
+        var storage = window[name];
+        storage.clear();
+
+        Object.defineProperty(storage, key, { "value": "test", "configurable": false });
+        assert_equals(storage[key], "test");
+        var desc = Object.getOwnPropertyDescriptor(storage, key);
+        assert_true(desc.configurable, "configurable");
+
+        assert_true(delete storage[key]);
+        assert_equals(storage[key], undefined);
+    }, name + ": defineProperty not configurable");
+
+    test(function() {
+        var key = Symbol();
+        Storage.prototype[key] = "test";
+        this.add_cleanup(function() { delete Storage.prototype[key]; });
+
+        var storage = window[name];
+        storage.clear();
+
+        assert_equals(storage[key], "test");
+        var desc = Object.getOwnPropertyDescriptor(storage, key);
+        assert_equals(desc, undefined);
+    }, name + ": get with symbol on prototype");
+
+    test(function() {
+        var key = Symbol();
+
+        var storage = window[name];
+        storage.clear();
+
+        storage[key] = "test";
+        assert_true(delete storage[key]);
+        assert_equals(storage[key], undefined);
+    }, name + ": delete existing property");
+
+    test(function() {
+        var key = Symbol();
+
+        var storage = window[name];
+        storage.clear();
+
+        assert_true(delete storage[key]);
+        assert_equals(storage[key], undefined);
+    }, name + ": delete non-existent property");
+});
diff --git a/third_party/blink/web_tests/fast/dom/document-contentType-data-uri-expected.txt b/third_party/blink/web_tests/fast/dom/document-contentType-data-uri-expected.txt
index f59ec981..0348098c 100644
--- a/third_party/blink/web_tests/fast/dom/document-contentType-data-uri-expected.txt
+++ b/third_party/blink/web_tests/fast/dom/document-contentType-data-uri-expected.txt
@@ -1,7 +1,7 @@
-PASS "text/xml" is "text/xml"
 PASS "application/xml" is "application/xml"
 PASS "image/svg+xml" is "image/svg+xml"
 PASS "text/html" is "text/html"
+PASS "text/xml" is "text/xml"
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/blink/web_tests/fast/dom/document-contentType-data-uri.html b/third_party/blink/web_tests/fast/dom/document-contentType-data-uri.html
index 2f260e73c2..dd9bf83 100644
--- a/third_party/blink/web_tests/fast/dom/document-contentType-data-uri.html
+++ b/third_party/blink/web_tests/fast/dom/document-contentType-data-uri.html
@@ -12,14 +12,31 @@
 <script>
 window.jsTestIsAsync = true;
 
+var events = [];
+
 window.onmessage = function(e) {
+    // For consistent ordering, we capture the events, sort them and then test them.
     if (e.data)
-        shouldBe('"' + e.data.obtained + '"', '"' + e.data.expected + '"');
+        events.push(e);
     else
         testFailed("Null message payload");
 
-    if (--expectedMessagesCount == 0)
+    if (--expectedMessagesCount == 0) {
+        events.sort((e1, e2) => {
+            if (e1.data.obtained < e2.data.obtained) {
+                return -1;
+            } if (e1.data.obtained > e2.data.obtained) {
+                return 1;
+            } else {
+                return 0;
+            }
+        });
+        for (var i in events) {
+            var e = events[i];
+            shouldBe('"' + e.data.obtained + '"', '"' + e.data.expected + '"');
+        }
         finishJSTest();
+    }
 };
 
 var documentContents = '<script xmlns="http://www.w3.org/1999/xhtml">' +
diff --git a/third_party/blink/web_tests/fast/text/color-emoji.html b/third_party/blink/web_tests/fast/text/color-emoji.html
index fbf00b1..d2357ca 100644
--- a/third_party/blink/web_tests/fast/text/color-emoji.html
+++ b/third_party/blink/web_tests/fast/text/color-emoji.html
@@ -9,8 +9,7 @@
 <div style="-webkit-transform: rotate(-45deg); width: 200px; height: 200px; left: 500px; top: 200px; position:absolute;">
 <p style="font-size: 24px;">rotated emoji: 🙉😍<br><span style="color: green;">green text: 😻🍼</span><br><span style="background-color: purple;">purple bg: 🐑🍲</span></p>
 </div>
-<p>For the following, each emoji should appear in two different colors.<br>
-Color swatches should not be visible.
-<p style="font-size: 24px;">zero-width-joiners: ✌&zwj;&#x1f3ff;✌&zwj;&#x1f3fc;👯&zwj;&#x1f3fb;👯&zwj;&#x1f3ff;👲&zwj;&#x1f3fd;👲&zwj;&#x1f3fb;</p>
+<p>For the following, each emoji should appear in two different skin tones.<br>
+<p style="font-size: 24px; font-family: Noto Color Emoji;">zero-width-joiners: ✌&#x1f3ff;✌&#x1f3fc;👋&#x1f3ff;👋&#x1f3fc;🤘&#x1f3ff;🤘&#x1f3fc;</p>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/fast/text/selection/find-hidden-text.html b/third_party/blink/web_tests/fast/text/selection/find-hidden-text.html
index d8b66839..d196572 100644
--- a/third_party/blink/web_tests/fast/text/selection/find-hidden-text.html
+++ b/third_party/blink/web_tests/fast/text/selection/find-hidden-text.html
@@ -55,33 +55,33 @@
 
             testNonHiddenTextStyle("height:0");
 
-            testHiddenTextStyle("height:0; overflow:hidden");
-            testHiddenTextStyle("height:0; overflow:scroll");
-            testHiddenTextStyle("height:0; overflow:auto");
+            testNonHiddenTextStyle("height:0; overflow:hidden");
+            testNonHiddenTextStyle("height:0; overflow:scroll");
+            testNonHiddenTextStyle("height:0; overflow:auto");
 
-            testHiddenTextStyle("width:0; overflow:hidden");
-            testHiddenTextStyle("width:0; overflow:scroll");
-            testHiddenTextStyle("width:0; overflow:auto");
+            testNonHiddenTextStyle("width:0; overflow:hidden");
+            testNonHiddenTextStyle("width:0; overflow:scroll");
+            testNonHiddenTextStyle("width:0; overflow:auto");
 
-            testHiddenTextStyle("height:0; overflow-x:hidden");
-            testHiddenTextStyle("height:0; overflow-x:scroll");
-            testHiddenTextStyle("height:0; overflow-x:auto");
+            testNonHiddenTextStyle("height:0; overflow-x:hidden");
+            testNonHiddenTextStyle("height:0; overflow-x:scroll");
+            testNonHiddenTextStyle("height:0; overflow-x:auto");
 
-            testHiddenTextStyle("width:0; overflow-x:hidden");
-            testHiddenTextStyle("width:0; overflow-x:scroll");
-            testHiddenTextStyle("width:0; overflow-x:auto");
+            testNonHiddenTextStyle("width:0; overflow-x:hidden");
+            testNonHiddenTextStyle("width:0; overflow-x:scroll");
+            testNonHiddenTextStyle("width:0; overflow-x:auto");
 
-            testHiddenTextStyle("height:0; overflow-y:hidden");
-            testHiddenTextStyle("height:0; overflow-y:scroll");
-            testHiddenTextStyle("height:0; overflow-y:auto");
+            testNonHiddenTextStyle("height:0; overflow-y:hidden");
+            testNonHiddenTextStyle("height:0; overflow-y:scroll");
+            testNonHiddenTextStyle("height:0; overflow-y:auto");
 
-            testHiddenTextStyle("width:0; overflow-y:hidden");
-            testHiddenTextStyle("width:0; overflow-y:scroll");
-            testHiddenTextStyle("width:0; overflow-y:auto");
+            testNonHiddenTextStyle("width:0; overflow-y:hidden");
+            testNonHiddenTextStyle("width:0; overflow-y:scroll");
+            testNonHiddenTextStyle("width:0; overflow-y:auto");
 
-            testHiddenTextStyle("position: relative", "height:0; overflow:hidden");
-            testHiddenTextStyle("position: relative", "height:0; overflow:scroll");
-            testHiddenTextStyle("position: relative", "height:0; overflow:auto");
+            testNonHiddenTextStyle("position: relative", "height:0; overflow:hidden");
+            testNonHiddenTextStyle("position: relative", "height:0; overflow:scroll");
+            testNonHiddenTextStyle("position: relative", "height:0; overflow:auto");
 
             testNonHiddenTextStyle("position: absolute", "height:0; overflow:hidden");
             testNonHiddenTextStyle("position: absolute", "height:0; overflow:scroll");
diff --git a/third_party/blink/web_tests/fast/xpath/xpath-iterator-result-should-mark-its-nodeset-expected.txt b/third_party/blink/web_tests/fast/xpath/xpath-iterator-result-should-mark-its-nodeset-expected.txt
index 1498fba5..1be5545 100644
--- a/third_party/blink/web_tests/fast/xpath/xpath-iterator-result-should-mark-its-nodeset-expected.txt
+++ b/third_party/blink/web_tests/fast/xpath/xpath-iterator-result-should-mark-its-nodeset-expected.txt
@@ -2,6 +2,6 @@
 For this test to PASS you should see 2 PASS below.
 
 
-undefined
-undefined
+PASS
+PASS
 
diff --git a/third_party/blink/web_tests/fast/xpath/xpath-other-nodeset-result-should-mark-its-nodeset-expected.txt b/third_party/blink/web_tests/fast/xpath/xpath-other-nodeset-result-should-mark-its-nodeset-expected.txt
index 55e8936a..33d3e3e 100644
--- a/third_party/blink/web_tests/fast/xpath/xpath-other-nodeset-result-should-mark-its-nodeset-expected.txt
+++ b/third_party/blink/web_tests/fast/xpath/xpath-other-nodeset-result-should-mark-its-nodeset-expected.txt
@@ -2,6 +2,6 @@
 For this test to PASS you should see 2 PASS below.
 
 
-undefined
-undefined
+PASS
+PASS
 
diff --git a/third_party/blink/web_tests/fast/xpath/xpath-snapshot-result-should-mark-its-nodeset-expected.txt b/third_party/blink/web_tests/fast/xpath/xpath-snapshot-result-should-mark-its-nodeset-expected.txt
index 55e8936a..33d3e3e 100644
--- a/third_party/blink/web_tests/fast/xpath/xpath-snapshot-result-should-mark-its-nodeset-expected.txt
+++ b/third_party/blink/web_tests/fast/xpath/xpath-snapshot-result-should-mark-its-nodeset-expected.txt
@@ -2,6 +2,6 @@
 For this test to PASS you should see 2 PASS below.
 
 
-undefined
-undefined
+PASS
+PASS
 
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/text/unicode-fallback-font-expected.png b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/text/unicode-fallback-font-expected.png
index df1db80..e390109 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/text/unicode-fallback-font-expected.png
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/fast/text/unicode-fallback-font-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-expected.txt
deleted file mode 100644
index de70025..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-expected.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
-          "rect": [50, 50, 75, 75],
-          "reason": "chunk disappeared"
-        }
-      ]
-    },
-    {
-      "name": "LayoutNGBlockFlow (positioned) DIV id='container'",
-      "position": [200, 100],
-      "bounds": [125, 125],
-      "backgroundColor": "#0000FF",
-      "paintInvalidations": [
-        {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'",
-          "rect": [0, 0, 100, 100],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
-          "rect": [50, 50, 75, 75],
-          "reason": "chunk appeared"
-        }
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-individual-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-individual-expected.txt
deleted file mode 100644
index de70025..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-added-individual-expected.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
-          "rect": [50, 50, 75, 75],
-          "reason": "chunk disappeared"
-        }
-      ]
-    },
-    {
-      "name": "LayoutNGBlockFlow (positioned) DIV id='container'",
-      "position": [200, 100],
-      "bounds": [125, 125],
-      "backgroundColor": "#0000FF",
-      "paintInvalidations": [
-        {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'",
-          "rect": [0, 0, 100, 100],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
-          "rect": [50, 50, 75, 75],
-          "reason": "chunk appeared"
-        }
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-expected.txt
deleted file mode 100644
index f4c9521..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-expected.txt
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
-          "rect": [50, 50, 75, 75],
-          "reason": "chunk appeared"
-        }
-      ]
-    },
-    {
-      "name": "LayoutNGBlockFlow (positioned) DIV id='container'",
-      "position": [200, 100],
-      "bounds": [100, 100],
-      "contentsOpaque": true,
-      "backgroundColor": "#0000FF",
-      "paintInvalidations": [
-        {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'",
-          "rect": [0, 0, 100, 100],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
-          "rect": [50, 50, 75, 75],
-          "reason": "chunk disappeared"
-        }
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-individual-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-individual-expected.txt
deleted file mode 100644
index 47f8e1a..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/containing-block-removed-individual-expected.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-CONSOLE MESSAGE: line 30: debug
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
-          "rect": [50, 50, 75, 75],
-          "reason": "chunk appeared"
-        }
-      ]
-    },
-    {
-      "name": "LayoutNGBlockFlow (positioned) DIV id='container'",
-      "position": [200, 100],
-      "bounds": [100, 100],
-      "contentsOpaque": true,
-      "backgroundColor": "#0000FF",
-      "paintInvalidations": [
-        {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV id='container'",
-          "rect": [0, 0, 100, 100],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV class='fixed'",
-          "rect": [50, 50, 75, 75],
-          "reason": "chunk disappeared"
-        }
-      ]
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/repaint-via-layout-offset-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/repaint-via-layout-offset-expected.txt
deleted file mode 100644
index c54f072..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/compositing/repaint-via-layout-offset-expected.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [800, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [800, 600],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Squashing Containment Layer",
-      "drawsContent": false
-    },
-    {
-      "name": "LayoutNGBlockFlow DIV",
-      "bounds": [200, 200],
-      "contentsOpaque": true,
-      "backgroundColor": "#ADD8E6",
-      "transform": 1
-    },
-    {
-      "name": "Squashing Layer (first squashed layer: LayoutNGBlockFlow (positioned) SPAN class='child')",
-      "position": [50, 50],
-      "bounds": [50, 50],
-      "paintInvalidations": [
-        {
-          "object": "LayoutNGBlockFlow (positioned) SPAN class='child embiggen'",
-          "rect": [0, 0, 50, 50],
-          "reason": "background"
-        },
-        {
-          "object": "LayoutNGBlockFlow (positioned) SPAN class='child'",
-          "rect": [0, 0, 40, 40],
-          "reason": "geometry"
-        }
-      ]
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [8, 8, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/gradients-em-stops-repaint-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/gradients-em-stops-repaint-expected.txt
index 1b269e5d..7d808380 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/gradients-em-stops-repaint-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/gradients-em-stops-repaint-expected.txt
@@ -23,11 +23,6 @@
           "reason": "style change"
         },
         {
-          "object": "LayoutNGBlockFlow DIV id='box3' class='box'",
-          "rect": [18, 18, 302, 122],
-          "reason": "geometry"
-        },
-        {
           "object": "LayoutNGBlockFlow DIV class='indicator'",
           "rect": [345, 19, 240, 20],
           "reason": "style change"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-1-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-1-expected.txt
index c6735ee..85ec1c50 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-1-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-1-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow P",
-          "rect": [8, 74, 418, 526],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'a ridge or furrow in the way wherever she wanted to send the'",
           "rect": [14, 240, 407, 139],
           "reason": "geometry"
@@ -178,16 +173,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutNGBlockFlow (floating) SPAN id='greenFloat'",
-          "rect": [372, 403, 48, 81],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutNGBlockFlow (floating) SPAN id='blueFloat'",
-          "rect": [14, 363, 48, 65],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'would'",
           "rect": [238, 180, 41, 19],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-10-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-10-expected.txt
index 8013ca3..825d1e06 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-10-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-10-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow P",
-          "rect": [8, 74, 418, 526],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'a ridge or furrow in the way wherever she wanted to send the'",
           "rect": [14, 240, 407, 139],
           "reason": "geometry"
@@ -188,11 +183,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutNGBlockFlow (floating) SPAN id='greenFloat'",
-          "rect": [372, 403, 48, 81],
-          "reason": "geometry"
-        },
-        {
           "object": "LayoutNGBlockFlow (floating) SPAN id='blueFloat'",
           "rect": [14, 363, 48, 65],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-3-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-3-expected.txt
index e566ac87..9bf99dd9 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-3-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-3-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow P",
-          "rect": [8, 74, 418, 526],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'a ridge or furrow in the way wherever she wanted to send the'",
           "rect": [14, 240, 407, 139],
           "reason": "geometry"
@@ -213,11 +208,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutNGBlockFlow (floating) SPAN id='blueFloat'",
-          "rect": [14, 363, 48, 65],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'would'",
           "rect": [238, 180, 41, 19],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-4-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-4-expected.txt
index 5b5858d..87facb32 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-4-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-4-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow P",
-          "rect": [8, 74, 418, 526],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'a ridge or furrow in the way wherever she wanted to send the'",
           "rect": [14, 240, 407, 139],
           "reason": "geometry"
@@ -178,11 +173,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutNGBlockFlow (floating) SPAN id='blueFloat'",
-          "rect": [14, 363, 48, 65],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'would'",
           "rect": [238, 180, 41, 19],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-5-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-5-expected.txt
index cfbb924..b9797b4b 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-5-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-5-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow P",
-          "rect": [8, 74, 418, 526],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'a ridge or furrow in the way wherever she wanted to send the'",
           "rect": [14, 240, 407, 139],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-6-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-6-expected.txt
index 859491d3..1ea9998 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-6-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-6-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow P",
-          "rect": [8, 74, 418, 526],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'a ridge or furrow in the way wherever she wanted to send the'",
           "rect": [14, 240, 407, 139],
           "reason": "geometry"
@@ -173,11 +168,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutNGBlockFlow (floating) SPAN id='greenFloat'",
-          "rect": [372, 403, 48, 81],
-          "reason": "geometry"
-        },
-        {
           "object": "LayoutNGBlockFlow (floating) SPAN id='blueFloat'",
           "rect": [14, 363, 48, 65],
           "reason": "disappeared"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-7-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-7-expected.txt
index c416b2e8..41b364f 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-7-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-7-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow P",
-          "rect": [8, 74, 418, 526],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'a ridge or furrow in the way wherever she wanted to send the'",
           "rect": [14, 240, 407, 139],
           "reason": "geometry"
@@ -173,16 +168,6 @@
           "reason": "style change"
         },
         {
-          "object": "LayoutNGBlockFlow (floating) SPAN id='greenFloat'",
-          "rect": [372, 403, 48, 81],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutNGBlockFlow (floating) SPAN id='blueFloat'",
-          "rect": [14, 363, 48, 65],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'would'",
           "rect": [238, 180, 41, 19],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-8-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-8-expected.txt
index b6d80681..6ec57b0f 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-8-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-8-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow P",
-          "rect": [8, 74, 418, 526],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'a ridge or furrow in the way wherever she wanted to send the'",
           "rect": [14, 240, 407, 139],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-9-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-9-expected.txt
index ee3ef0c..e0c764c 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-9-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/line-flow-with-floats-9-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow P",
-          "rect": [8, 74, 418, 526],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'and was in the act of crawling away: besides all this,'",
           "rect": [14, 240, 407, 139],
           "reason": "geometry"
@@ -193,11 +188,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutNGBlockFlow (floating) SPAN id='greenFloat'",
-          "rect": [372, 403, 48, 81],
-          "reason": "geometry"
-        },
-        {
           "object": "LayoutNGBlockFlow (floating) SPAN id='blueFloat'",
           "rect": [14, 383, 48, 65],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/focus-ring-on-child-move-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
index 7f6a014b..cccefe52 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/focus-ring-on-child-move-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow (positioned) DIV",
-          "rect": [99, 99, 302, 22],
-          "reason": "geometry"
-        },
-        {
           "object": "LayoutNGBlockFlow (positioned) DIV id='child'",
           "rect": [300, 50, 20, 300],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/outline-containing-image-in-non-standard-mode-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/outline-containing-image-in-non-standard-mode-expected.txt
index d7e9ae78..d90142ea 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/outline-containing-image-in-non-standard-mode-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/outline/outline-containing-image-in-non-standard-mode-expected.txt
@@ -21,11 +21,6 @@
           "object": "NGPhysicalBoxFragment LayoutInline SPAN id='target'",
           "rect": [6, 6, 204, 58],
           "reason": "style change"
-        },
-        {
-          "object": "LayoutImage IMG",
-          "rect": [8, 8, 200, 50],
-          "reason": "geometry"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/inline-relative-positioned-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/inline-relative-positioned-expected.txt
index ddcaec33..d9edbc4 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/inline-relative-positioned-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/position/inline-relative-positioned-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV id='target'",
-          "rect": [8, 88, 100, 100],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'a'",
           "rect": [8, 88, 100, 100],
           "reason": "full"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/reflection/reflection-with-rotation-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/reflection/reflection-with-rotation-expected.txt
index 19bc47f..646fe84 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/reflection/reflection-with-rotation-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/reflection/reflection-with-rotation-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV id='target'",
-          "rect": [22, 50, 226, 167],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'PASS'",
           "rect": [23, 51, 72, 110],
           "reason": "full"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/remove-inline-after-layout-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/remove-inline-after-layout-expected.txt
index bd38fc4..2ef26cf 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/remove-inline-after-layout-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/remove-inline-after-layout-expected.txt
@@ -33,11 +33,6 @@
           "reason": "geometry"
         },
         {
-          "object": "LayoutNGBlockFlow DIV",
-          "rect": [8, 108, 100, 100],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment ' '",
           "rect": [108, 193, 4, 19],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-of-transformed-move-after-scroll-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-of-transformed-move-after-scroll-expected.txt
deleted file mode 100644
index 2212567d..0000000
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/scroll/fixed-child-of-transformed-move-after-scroll-expected.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-{
-  "layers": [
-    {
-      "name": "LayoutView #document",
-      "bounds": [800, 600],
-      "drawsContent": false,
-      "backgroundColor": "#FFFFFF"
-    },
-    {
-      "name": "Scrolling Layer",
-      "bounds": [785, 600],
-      "drawsContent": false
-    },
-    {
-      "name": "Scrolling Contents Layer",
-      "bounds": [785, 3016],
-      "contentsOpaque": true,
-      "backgroundColor": "#FFFFFF",
-      "paintInvalidations": [
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV id='toMove'",
-          "rect": [158, 278, 100, 100],
-          "reason": "background"
-        },
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV",
-          "rect": [18, 278, 100, 100],
-          "reason": "geometry"
-        },
-        {
-          "object": "LayoutNGBlockFlow (positioned) DIV id='toMove'",
-          "rect": [18, 278, 100, 100],
-          "reason": "background"
-        }
-      ],
-      "transform": 1
-    }
-  ],
-  "transforms": [
-    {
-      "id": 1,
-      "transform": [
-        [1, 0, 0, 0],
-        [0, 1, 0, 0],
-        [0, 0, 1, 0],
-        [0, -200, 0, 1]
-      ],
-      "flattenInheritedTransform": false
-    }
-  ]
-}
-
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/subtree-root-skipped-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/subtree-root-skipped-expected.txt
index 46fdac2..ef40fd8 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/subtree-root-skipped-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/subtree-root-skipped-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "LayoutTextControl INPUT id='input'",
-          "rect": [8, 8, 181, 22],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'Selection is here'",
           "rect": [8, 30, 103, 19],
           "reason": "geometry"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/svg/svg-image-change-content-size-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/svg/svg-image-change-content-size-expected.txt
index e4e5de2c..8001297 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/svg/svg-image-change-content-size-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/svg/svg-image-change-content-size-expected.txt
@@ -21,11 +21,6 @@
           "object": "NGPhysicalBoxFragment LayoutNGBlockFlow div id='contentBox'",
           "rect": [8, 52, 602, 422],
           "reason": "geometry"
-        },
-        {
-          "object": "LayoutImage img",
-          "rect": [9, 53, 420, 420],
-          "reason": "geometry"
         }
       ]
     }
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/transform/transform-layout-repaint-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/transform/transform-layout-repaint-expected.txt
index b286fef..d08119d 100644
--- a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/transform/transform-layout-repaint-expected.txt
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/transform/transform-layout-repaint-expected.txt
@@ -18,11 +18,6 @@
       "backgroundColor": "#FFFFFF",
       "paintInvalidations": [
         {
-          "object": "NGPhysicalBoxFragment LayoutNGBlockFlow DIV id='target'",
-          "rect": [40, 50, 208, 118],
-          "reason": "geometry"
-        },
-        {
           "object": "NGPhysicalTextFragment 'PASS'",
           "rect": [52, 51, 43, 32],
           "reason": "full"
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align-length1-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align-length1-expected.txt
new file mode 100644
index 0000000..e9adeec0
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align-length1-expected.txt
@@ -0,0 +1,44 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutNGBlockFlow DIV class='other'",
+          "rect": [120, 130, 20, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='other'",
+          "rect": [120, 100, 20, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='other'",
+          "rect": [0, 130, 20, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='other'",
+          "rect": [0, 100, 20, 20],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align-length2-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align-length2-expected.txt
new file mode 100644
index 0000000..136f5e71
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align-length2-expected.txt
@@ -0,0 +1,34 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutNGBlockFlow DIV id='target'",
+          "rect": [200, 80, 100, 100],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV id='target'",
+          "rect": [200, 50, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align1-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align1-expected.txt
new file mode 100644
index 0000000..81838f43c
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align1-expected.txt
@@ -0,0 +1,44 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutNGBlockFlow DIV class='other'",
+          "rect": [120, 33, 20, 21],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='other'",
+          "rect": [0, 33, 20, 21],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='other'",
+          "rect": [120, 80, 20, 20],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV class='other'",
+          "rect": [0, 80, 20, 20],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align2-expected.txt b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align2-expected.txt
new file mode 100644
index 0000000..f1590ee2
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/enable-blink-features=LayoutNG/paint/invalidation/vertical-align2-expected.txt
@@ -0,0 +1,34 @@
+{
+  "layers": [
+    {
+      "name": "LayoutView #document",
+      "bounds": [800, 600],
+      "drawsContent": false,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling Layer",
+      "bounds": [800, 600],
+      "drawsContent": false
+    },
+    {
+      "name": "Scrolling Contents Layer",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF",
+      "paintInvalidations": [
+        {
+          "object": "LayoutNGBlockFlow DIV id='target'",
+          "rect": [200, 146, 100, 101],
+          "reason": "geometry"
+        },
+        {
+          "object": "LayoutNGBlockFlow DIV id='target'",
+          "rect": [200, 100, 100, 100],
+          "reason": "geometry"
+        }
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/sxg/sxg-transfer-size-expected.txt b/third_party/blink/web_tests/http/tests/devtools/sxg/sxg-transfer-size-expected.txt
new file mode 100644
index 0000000..75360224
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/sxg/sxg-transfer-size-expected.txt
@@ -0,0 +1,20 @@
+Tests the transfer size of signed exchange is set correctly.
+
+* http://127.0.0.1:8000/loading/sxg/resources/sxg-larger-than-10k.sxg
+  failed: false
+  statusCode: 200
+  resourceType: signed-exchange
+  SignedExchangeInfo
+    Request URL: https://127.0.0.1:8443/loading/sxg/resources/inner-url.html
+    Certificate URL: https://127.0.0.1:8443/loading/sxg/resources/127.0.0.1.sxg.pem.cbor
+    Certificate Subject: 127.0.0.1
+    Certificate Issuer: web-platform-tests
+* https://127.0.0.1:8443/loading/sxg/resources/127.0.0.1.sxg.pem.cbor
+  failed: false
+  statusCode: 200
+  resourceType: other
+* https://127.0.0.1:8443/loading/sxg/resources/inner-url.html
+  failed: false
+  statusCode: 200
+  resourceType: document
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/sxg/sxg-transfer-size.js b/third_party/blink/web_tests/http/tests/devtools/sxg/sxg-transfer-size.js
new file mode 100644
index 0000000..86c08acf
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/sxg/sxg-transfer-size.js
@@ -0,0 +1,17 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+(async function() {
+  TestRunner.addResult('Tests the transfer size of signed exchange is set correctly.\n');
+  await TestRunner.loadModule('network_test_runner');
+  await TestRunner.loadModule('console_test_runner');
+  await TestRunner.showPanel('network');
+  SDK.networkLog.reset();
+  await TestRunner.addIframe('/loading/sxg/resources/sxg-larger-than-10k.sxg');
+  ConsoleTestRunner.dumpConsoleMessages();
+  NetworkTestRunner.dumpNetworkRequestsWithSignedExchangeInfo();
+  var requests = NetworkTestRunner.findRequestsByURLPattern(/sxg-larger-than-10k.sxg/);
+  TestRunner.assertTrue(requests.length === 1);
+  TestRunner.assertTrue(requests[0].transferSize > 10000);
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations-expected.txt b/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations-expected.txt
deleted file mode 100644
index 4097578e..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations-expected.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-Tests invalidations produced by scrolling a page with position: fixed elements.
-
-Scroll invalidations[
-    {
-        cause : {reason: Scroll with viewport-constrained element, stackTrace: undefined}
-        changedAttribute : undefined
-        changedClass : undefined
-        changedId : undefined
-        changedPseudo : undefined
-        extraData : undefined
-        nodeName : "DIV"
-        selectorPart : undefined
-        type : "ScrollInvalidationTracking"
-    }
-]
-
diff --git a/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations.js b/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations.js
deleted file mode 100644
index d4a5c50..0000000
--- a/third_party/blink/web_tests/http/tests/devtools/tracing/scroll-invalidations.js
+++ /dev/null
@@ -1,29 +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.
-
-(async function() {
-  TestRunner.addResult(`Tests invalidations produced by scrolling a page with position: fixed elements.\n`);
-  await TestRunner.loadModule('performance_test_runner');
-  await TestRunner.showPanel('timeline');
-  await TestRunner.loadHTML(`
-    <script src="../../resources/run-after-layout-and-paint.js"></script>
-    <div style="width: 400px; height: 2000px; background-color: grey"></div>
-    <div style="position: fixed; left: 50px; top: 100px; width: 50px; height: 50px; background-color: rgba(255, 100, 100, 0.6)"></div>
-  `);
-  await TestRunner.evaluateInPagePromise(`
-    function scrollAndDisplay() {
-      scrollTo(0, 200);
-      return new Promise(fulfill => runAfterLayoutAndPaint(fulfill));
-    }
-  `);
-
-  Runtime.experiments.enableForTest('timelineInvalidationTracking');
-  await PerformanceTestRunner.invokeAsyncWithTimeline('scrollAndDisplay');
-
-  const event = PerformanceTestRunner.findTimelineEvent(TimelineModel.TimelineModel.RecordType.Paint);
-  TestRunner.addArray(
-      TimelineModel.InvalidationTracker.invalidationEventsFor(event), PerformanceTestRunner.InvalidationFormatters,
-      '', 'Scroll invalidations');
-  TestRunner.completeTest();
-})();
diff --git a/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view-expected.txt b/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view-expected.txt
index 34fadd5f..2db716d 100644
--- a/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view-expected.txt
+++ b/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view-expected.txt
@@ -4,9 +4,6 @@
 c2VuZGluZyB0aGlzIHV0Zi04IHN0cmluZyBhcyBhIGJpbmFyeSBtZXNzYWdlLi4u
 
 HexView:
-73656e64696e672074686973207574662d3820737472696e6720617320612062696e617279206d6573736167652e2e2e
-
-HexViewerView:
 00000000: 7365 6e64 696e 6720 7468 6973 2075 7466  sending this utf
 00000001: 2d38 2073 7472 696e 6720 6173 2061 2062  -8 string as a b
 00000002: 696e 6172 7920 6d65 7373 6167 652e 2e2e  inary message...
diff --git a/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view.js b/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view.js
index 3ea89256..a9fe484 100644
--- a/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view.js
+++ b/third_party/blink/web_tests/http/tests/devtools/unit/binary-resource-view.js
@@ -17,10 +17,6 @@
   TestRunner.addResult(await (factory.createHexView()._lazyContent()));
   TestRunner.addResult('');
 
-  TestRunner.addResult('HexViewerView:');
-  TestRunner.addResult(await (factory.createHexViewerView()._lazyContent()));
-  TestRunner.addResult('');
-
   TestRunner.addResult('Utf8View:');
   TestRunner.addResult(await (factory.createUtf8View()._lazyContent()));
   TestRunner.addResult('');
diff --git a/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js b/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js
index 8d90680..0c11f70 100644
--- a/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js
+++ b/third_party/blink/web_tests/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test.js
@@ -11,20 +11,18 @@
   // scripts in the cache.
   SDK.multitargetNetworkManager.clearBrowserCache();
 
-  function runTests() {
-    // Loads a WASM module that is smaller than the threshold twice. It should
-    // compile and not be cached.
+  async function runTests() {
+    // Loads a WASM module that is smaller than the threshold. It should compile
+    // but not be cached.
     function loadSmallWasmModule(iframe_window) {
       const url = 'http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors'
-      return iframe_window.instantiateModule(url)
-        .then(() => iframe_window.instantiateModule(url));
+      return iframe_window.instantiateModule(url);
     }
-    // Loads a WASM module that is larger than the caching threshold. It
-    // should be cached the first run and bypass compilation the second.
+    // Loads a WASM module that is larger than the caching threshold. It should
+    // compile and be cached.
     function loadLargeWasmModule(iframe_window) {
       const url = 'http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=large.wasm&cors'
-      return iframe_window.instantiateModule(url)
-        .then(() => iframe_window.instantiateModule(url));
+      return iframe_window.instantiateModule(url);
     }
     // Load the same large WASM module with a different URL. It should miss
     // the cache, compile and be cached.
@@ -41,10 +39,17 @@
     const frameId = 'frame_id';
     const iframe_window = document.getElementById(frameId).contentWindow;
 
-    // These functions must be called in this order.
-    return loadSmallWasmModule(iframe_window)
-      .then(() => loadLargeWasmModule(iframe_window))
-      .then(() => loadOtherLargeWasmModule(iframe_window));
+    await loadSmallWasmModule(iframe_window);
+    await loadLargeWasmModule(iframe_window);
+
+    // Second loads. The small module should compile again and not be cached.
+    await loadSmallWasmModule(iframe_window);
+    // The large module should hit the cache.
+    await loadLargeWasmModule(iframe_window);
+
+    // Loading the large module from a different URL should miss the cache,
+    // compile, and be cached.
+    await loadOtherLargeWasmModule(iframe_window);
   }
 
   await TestRunner.evaluateInPagePromise(runTests.toString());
@@ -55,8 +60,10 @@
   // Create a same origin iframe.
   const scope = 'http://127.0.0.1:8000/wasm/resources/wasm-cache-iframe.html';
   await TestRunner.addIframe(scope, {id: 'frame_id'});
+  await PerformanceTestRunner.startTimeline();
 
-  await PerformanceTestRunner.invokeAsyncWithTimeline('runTests');
+  await TestRunner.callFunctionInPageAsync('runTests');
+  await PerformanceTestRunner.stopTimeline();
 
   const events = new Set([
     TimelineModel.TimelineModel.RecordType.WasmStreamFromResponseCallback,
@@ -72,11 +79,14 @@
   // Second navigation
   TestRunner.addResult(
       '\n--- Second navigation - from a different origin ------\n');
+  await PerformanceTestRunner.startTimeline();
 
+  // Create a cross origin iframe.
   const other_scope = 'http://localhost:8000/wasm/resources/wasm-cache-iframe.html';
   await TestRunner.addIframe(other_scope, {id: 'frame_id'});
 
-  await PerformanceTestRunner.invokeAsyncWithTimeline('runTests');
+  await TestRunner.callFunctionInPageAsync('runTests');
+  await PerformanceTestRunner.stopTimeline();
 
   tracingModel.sortedProcesses().forEach(p => p.sortedThreads().forEach(t =>
       t.events().filter(event => events.has(event.name)).forEach(PerformanceTestRunner.printTraceEventProperties)));
diff --git a/third_party/blink/web_tests/http/tests/loading/sxg/resources/generate-test-sxgs.sh b/third_party/blink/web_tests/http/tests/loading/sxg/resources/generate-test-sxgs.sh
index 90af026..1767904 100755
--- a/third_party/blink/web_tests/http/tests/loading/sxg/resources/generate-test-sxgs.sh
+++ b/third_party/blink/web_tests/http/tests/loading/sxg/resources/generate-test-sxgs.sh
@@ -89,4 +89,19 @@
   -miRecordSize 100 \
   -ignoreErrors true
 
+# Generate the signed exchange file larger than 10KB.
+gen-signedexchange \
+  -version $sxg_version \
+  -uri https://127.0.0.1:8443/loading/sxg/resources/inner-url.html \
+  -status 200 \
+  -content sxg-larger-than-10k.html \
+  -certificate $certs_dir/127.0.0.1.sxg.pem \
+  -certUrl https://127.0.0.1:8443/loading/sxg/resources/127.0.0.1.sxg.pem.cbor \
+  -validityUrl https://127.0.0.1:8443/loading/sxg/resources/resource.validity.msg \
+  -privateKey $certs_dir/127.0.0.1.sxg.key \
+  -date 2018-04-01T00:00:00Z \
+  -expire 168h \
+  -o sxg-larger-than-10k.sxg \
+  -miRecordSize 100
+
 rm -fr $tmpdir
diff --git a/third_party/blink/web_tests/http/tests/loading/sxg/resources/sxg-larger-than-10k.html b/third_party/blink/web_tests/http/tests/loading/sxg/resources/sxg-larger-than-10k.html
new file mode 100644
index 0000000..a343242
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/loading/sxg/resources/sxg-larger-than-10k.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<title>Content of SignedHTTPExchange</title>
+<script>
+window.addEventListener('message', (event) => {
+  event.data.port.postMessage({location: document.location.href, is_fallback: false});
+}, false);
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+/******************************************************************************/
+</script>
diff --git a/third_party/blink/web_tests/http/tests/loading/sxg/resources/sxg-larger-than-10k.sxg b/third_party/blink/web_tests/http/tests/loading/sxg/resources/sxg-larger-than-10k.sxg
new file mode 100644
index 0000000..626c4c6
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/loading/sxg/resources/sxg-larger-than-10k.sxg
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/origin_trials/webexposed/isinputpending-origin-trial-interfaces.html b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/isinputpending-origin-trial-interfaces.html
new file mode 100644
index 0000000..f89bfa0
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/origin_trials/webexposed/isinputpending-origin-trial-interfaces.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<!-- Generate this token with the command:
+generate_token.py http://127.0.0.1:8000 ExperimentalIsInputPending --expire-timestamp=2000000000
+-- -->
+
+<meta http-equiv="origin-trial" content="AsUM4BM9DEuyd0xo0RDUBJCWHHGZxqU38AUzXCUyh1ZXp8aKlo2iNgykQCeZUmEI5Yjt0Wf4R5Crdis+Xu4xjwYAAABieyJvcmlnaW4iOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAwIiwgImZlYXR1cmUiOiAiRXhwZXJpbWVudGFsSXNJbnB1dFBlbmRpbmciLCAiZXhwaXJ5IjogMjAwMDAwMDAwMH0=" />
+
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/origin-trials-helper.js"></script>
+
+<script>
+test(t => {
+  OriginTrialsHelper.check_properties(this,
+      {
+        'Navigator': ['scheduling'],
+        'Scheduling': ['isInputPending'],
+      },
+  );
+}, "isInputPending related interfaces in Origin-Trial enabled document.");
+</script>
diff --git a/third_party/blink/web_tests/media/controls/controls-layout-in-different-size.html b/third_party/blink/web_tests/media/controls/controls-layout-in-different-size.html
index 7d0b0898..1f4a9ae 100644
--- a/third_party/blink/web_tests/media/controls/controls-layout-in-different-size.html
+++ b/third_party/blink/web_tests/media/controls/controls-layout-in-different-size.html
@@ -32,13 +32,35 @@
 
         let nextIndex = index + 1;
         if (nextIndex === testCases.length) {
-          t.done();
+          testLayoutWhilePlaying();
           return;
         }
         runTestCase(nextIndex);
       }));
     }
 
+    function testLayoutWhilePlaying() {
+      video.width = 400;
+
+      // Start playing video and wait for controls to hide
+      video.addEventListener('playing', t.step_func(() => {
+        runAfterHideMediaControlsTimerFired(t.step_func(() => {
+          assert_false(isControlsPanelVisible(video), 'controls should not be shown');
+
+          // Change width to 80 and check layout
+          video.width = 80;
+          runAfterLayoutAndPaint(t.step_func(() => {
+
+            // Hover to show the controls
+            hoverOverControl(video, t.step_func_done(() => {
+              expectLayoutCorrectly();
+            }));
+          }));
+        }), video);
+      }), {once: true});
+      video.play();
+    }
+
     function expectLayoutCorrectly() {
       let totalWidth = 0;
       let children = buttonPanel.children;
@@ -48,6 +70,7 @@
           totalWidth += child.getBoundingClientRect().width;
       }
 
+      assert_true(buttonPanel.getBoundingClientRect().width > 0, 'control panel width should always be positive when we testing');
       assert_true(totalWidth <= buttonPanel.getBoundingClientRect().width,
                   'All element should fit in button panel');
     }
diff --git a/third_party/blink/web_tests/media/controls/overflow-menu-focus.html b/third_party/blink/web_tests/media/controls/overflow-menu-focus.html
new file mode 100644
index 0000000..a308da59
--- /dev/null
+++ b/third_party/blink/web_tests/media/controls/overflow-menu-focus.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<title>Overflow menu give focus back to last focused element when gaining focus</title>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script src="../../resources/run-after-layout-and-paint.js"></script>
+<script src="../media-controls.js"></script>
+<video controls></video>
+<script>
+async_test(t => {
+
+  const video  = document.querySelector('video');
+  video.src = '../content/test.ogv';
+  const menu = overflowMenu(video);
+  enableTestMode(video);
+
+  video.onloadedmetadata = t.step_func(() => {
+    singleTapOnControl(overflowButton(video), t.step_func(() => {
+      assert_true(isControlVisible(menu), 'Overflow menu should be visible');
+
+      let lastElement = getFocusedElement(video);
+
+      menu.focus();
+      setTimeout(t.step_func_done(() => {
+        assert_equals(getFocusedElement(video), lastElement, 'overflow menu should give focus back');
+      }));
+    }));
+  });
+});
+</script>
+</html>
diff --git a/third_party/blink/web_tests/media/media-controls.js b/third_party/blink/web_tests/media/media-controls.js
index 06f0ab9a..87ba2f5 100644
--- a/third_party/blink/web_tests/media/media-controls.js
+++ b/third_party/blink/web_tests/media/media-controls.js
@@ -186,6 +186,10 @@
     return null;
 }
 
+function getFocusedElement(video) {
+  return internals.shadowRoot(video).activeElement;
+}
+
 function mediaControlsButton(element, id)
 {
     var controlID = "-webkit-media-controls-" + id;
diff --git a/third_party/blink/web_tests/platform/linux/fast/text/color-emoji-expected.png b/third_party/blink/web_tests/platform/linux/fast/text/color-emoji-expected.png
index 9d28665..5d25c42 100644
--- a/third_party/blink/web_tests/platform/linux/fast/text/color-emoji-expected.png
+++ b/third_party/blink/web_tests/platform/linux/fast/text/color-emoji-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/css/css-grid/grid-model/grid-button-001-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/css/css-grid/grid-model/grid-button-001-expected.txt
new file mode 100644
index 0000000..6a44c0d
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/external/wpt/css/css-grid/grid-model/grid-button-001-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+FAIL [data-expected-width] 1 assert_equals: 
+<span class="item1" data-expected-width="100">item 1</span>
+width expected 100 but got 33
+FAIL [data-expected-width] 2 assert_equals: 
+<span class="item2" data-expected-width="200">item 2</span>
+width expected 200 but got 33
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/color-emoji-expected.png b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/color-emoji-expected.png
index 2233bd84..d41c535 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/color-emoji-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.10/fast/text/color-emoji-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/color-emoji-expected.png b/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/color-emoji-expected.png
index 35f46ab..21ddaa92 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/color-emoji-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.11/fast/text/color-emoji-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/color-emoji-expected.png b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/color-emoji-expected.png
index dcd0e5f7..b58c5e5 100644
--- a/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/color-emoji-expected.png
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/fast/text/color-emoji-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-grid/grid-model/grid-button-001-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-grid/grid-model/grid-button-001-expected.txt
new file mode 100644
index 0000000..34d1fe4
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-grid/grid-model/grid-button-001-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+FAIL [data-expected-width] 1 assert_equals: 
+<span class="item1" data-expected-width="100">item 1</span>
+width expected 100 but got 35
+FAIL [data-expected-width] 2 assert_equals: 
+<span class="item2" data-expected-width="200">item 2</span>
+width expected 200 but got 35
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-values/lh-rlh-on-root-001-expected.txt b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-values/lh-rlh-on-root-001-expected.txt
index 820171c..3b1438d 100644
--- a/third_party/blink/web_tests/platform/mac/external/wpt/css/css-values/lh-rlh-on-root-001-expected.txt
+++ b/third_party/blink/web_tests/platform/mac/external/wpt/css/css-values/lh-rlh-on-root-001-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 FAIL lh in line-height on root assert_approx_equals: the lh unit on the root element's line-height property uses font metrics corresponding to the initial values of the font or line-height properties expected 18 +/- 1 but got 164
 FAIL rlh in line-height on root assert_approx_equals: the rlh unit on the root element's line-height property uses font metrics corresponding to the initial values of the font or line-height properties expected 18 +/- 1 but got 164
-PASS lh in font-size on root
-PASS rlh in font-size on root
+FAIL lh in font-size on root assert_approx_equals: the lh unit on the root element's font-size property uses font metrics corresponding to the initial values of the font or line-height properties expected 18 +/- 1 but got 16
+FAIL rlh in font-size on root assert_approx_equals: the rlh unit on the root element's font-size property uses font metrics corresponding to the initial values of the font or line-height properties expected 18 +/- 1 but got 16
 FAIL 2lh in line-height on root assert_approx_equals: the lh unit on the root element's line-height property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 36 +/- 1 but got 164
 FAIL 2rlh in line-height on root assert_approx_equals: the rlh unit on the root element's line-height property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 36 +/- 1 but got 164
-FAIL 2lh in font-size on root assert_approx_equals: the lh unit on the root element's font-size property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 32 +/- 1 but got 16
+FAIL 2lh in font-size on root assert_approx_equals: the lh unit on the root element's font-size property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 36 +/- 1 but got 16
 FAIL 2rlh in font-size on root assert_approx_equals: the rlh unit on the root element's font-size property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 32 +/- 1 but got 16
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/mac/fast/text/color-emoji-expected.png b/third_party/blink/web_tests/platform/mac/fast/text/color-emoji-expected.png
index 26fbc205..0c2179c 100644
--- a/third_party/blink/web_tests/platform/mac/fast/text/color-emoji-expected.png
+++ b/third_party/blink/web_tests/platform/mac/fast/text/color-emoji-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/css/css-grid/grid-model/grid-button-001-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/css/css-grid/grid-model/grid-button-001-expected.txt
new file mode 100644
index 0000000..48086373
--- /dev/null
+++ b/third_party/blink/web_tests/platform/win/external/wpt/css/css-grid/grid-model/grid-button-001-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+FAIL [data-expected-width] 1 assert_equals: 
+<span class="item1" data-expected-width="100">item 1</span>
+width expected 100 but got 38
+FAIL [data-expected-width] 2 assert_equals: 
+<span class="item2" data-expected-width="200">item 2</span>
+width expected 200 but got 38
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/platform/win/external/wpt/css/css-values/lh-rlh-on-root-001-expected.txt b/third_party/blink/web_tests/platform/win/external/wpt/css/css-values/lh-rlh-on-root-001-expected.txt
index d4b313d..92f6f05 100644
--- a/third_party/blink/web_tests/platform/win/external/wpt/css/css-values/lh-rlh-on-root-001-expected.txt
+++ b/third_party/blink/web_tests/platform/win/external/wpt/css/css-values/lh-rlh-on-root-001-expected.txt
@@ -1,11 +1,11 @@
 This is a testharness.js-based test.
 FAIL lh in line-height on root assert_approx_equals: the lh unit on the root element's line-height property uses font metrics corresponding to the initial values of the font or line-height properties expected 20 +/- 1 but got 166
 FAIL rlh in line-height on root assert_approx_equals: the rlh unit on the root element's line-height property uses font metrics corresponding to the initial values of the font or line-height properties expected 20 +/- 1 but got 166
-PASS lh in font-size on root
-PASS rlh in font-size on root
+FAIL lh in font-size on root assert_approx_equals: the lh unit on the root element's font-size property uses font metrics corresponding to the initial values of the font or line-height properties expected 20 +/- 1 but got 16
+FAIL rlh in font-size on root assert_approx_equals: the rlh unit on the root element's font-size property uses font metrics corresponding to the initial values of the font or line-height properties expected 20 +/- 1 but got 16
 FAIL 2lh in line-height on root assert_approx_equals: the lh unit on the root element's line-height property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 40 +/- 1 but got 166
 FAIL 2rlh in line-height on root assert_approx_equals: the rlh unit on the root element's line-height property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 40 +/- 1 but got 166
-FAIL 2lh in font-size on root assert_approx_equals: the lh unit on the root element's font-size property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 32 +/- 1 but got 16
+FAIL 2lh in font-size on root assert_approx_equals: the lh unit on the root element's font-size property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 40 +/- 1 but got 16
 FAIL 2rlh in font-size on root assert_approx_equals: the rlh unit on the root element's font-size property actually works as a unit and doesn't merely cause a fallback that doesn't take the number of units into account expected 32 +/- 1 but got 16
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/platform/win/fast/text/color-emoji-expected.png b/third_party/blink/web_tests/platform/win/fast/text/color-emoji-expected.png
index b3ac576..1bbe8ac 100644
--- a/third_party/blink/web_tests/platform/win/fast/text/color-emoji-expected.png
+++ b/third_party/blink/web_tests/platform/win/fast/text/color-emoji-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win7/fast/text/color-emoji-expected.png b/third_party/blink/web_tests/platform/win7/fast/text/color-emoji-expected.png
index 397f259..051dda4 100644
--- a/third_party/blink/web_tests/platform/win7/fast/text/color-emoji-expected.png
+++ b/third_party/blink/web_tests/platform/win7/fast/text/color-emoji-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-sharedworker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-sharedworker-expected.txt
new file mode 100644
index 0000000..4f65337
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/importScripts-in-sharedworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL importScripts assert_equals: expected "gamma/script.js" but got "beta/script.js"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-sharedworker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-sharedworker-expected.txt
new file mode 100644
index 0000000..4bb4dcb
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/baseurl/alpha/xhr-in-sharedworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL xhr-worker assert_equals: expected "gamma\n" but got "beta\n"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-sharedworker-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-sharedworker-expected.txt
new file mode 100644
index 0000000..600eacd
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/external/wpt/workers/interfaces/WorkerGlobalScope/location/redirect-sharedworker-expected.txt
@@ -0,0 +1,4 @@
+This is a testharness.js-based test.
+FAIL redirect assert_equals: expected "/workers/interfaces/WorkerGlobalScope/location/redirect.js" but got "/common/redirect.py"
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/shared-worker-performance-timeline-expected.txt b/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/shared-worker-performance-timeline-expected.txt
new file mode 100644
index 0000000..de868f7
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/omt-worker-fetch/http/tests/workers/shared-worker-performance-timeline-expected.txt
@@ -0,0 +1,5 @@
+This is a testharness.js-based test.
+PASS User Timing
+FAIL Resource Timing assert_equals: expected 2 but got 3
+Harness: the test ran to completion.
+
diff --git a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
index 370651a..827521e 100644
--- a/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/virtual/stable/webexposed/global-interface-listing-expected.txt
@@ -4863,6 +4863,19 @@
     attribute @@toStringTag
     getter channel
     method constructor
+interface RTCError : DOMException
+    attribute @@toStringTag
+    getter errorDetail
+    getter httpRequestStatusCode
+    getter receivedAlert
+    getter sctpCauseCode
+    getter sdpLineNumber
+    getter sentAlert
+    method constructor
+interface RTCErrorEvent : Event
+    attribute @@toStringTag
+    getter error
+    method constructor
 interface RTCIceCandidate
     attribute @@toStringTag
     getter address
diff --git a/third_party/blink/web_tests/virtual/sxg-origin-trial-with-network-service/http/tests/loading/sxg/sxg-location-expected.txt b/third_party/blink/web_tests/virtual/sxg-origin-trial-with-network-service/http/tests/loading/sxg/sxg-location-expected.txt
deleted file mode 100644
index 29cf41a..0000000
--- a/third_party/blink/web_tests/virtual/sxg-origin-trial-with-network-service/http/tests/loading/sxg/sxg-location-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-main frame - didStartProvisionalLoadForFrame
-main frame - didCommitLoadForFrame
-main frame - didReceiveTitle: Location of SignedHTTPExchange
-main frame - didFinishDocumentLoadForFrame
-main frame - didHandleOnloadEventsForFrame
-main frame - didFinishLoadForFrame
-frame "sxg_iframe" - didReceiveTitle: 
-frame "sxg_iframe" - didStartProvisionalLoadForFrame
-frame "sxg_iframe" - didFailProvisionalLoadWithError
-This is a testharness.js-based test.
-FAIL Location of SignedHTTPExchange promise_test: Unhandled rejection with value: "timeout"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/sxg-origin-trial-with-network-service/http/tests/loading/sxg/sxg-usecounter-expected.txt b/third_party/blink/web_tests/virtual/sxg-origin-trial-with-network-service/http/tests/loading/sxg/sxg-usecounter-expected.txt
deleted file mode 100644
index 9fbbef8..0000000
--- a/third_party/blink/web_tests/virtual/sxg-origin-trial-with-network-service/http/tests/loading/sxg/sxg-usecounter-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-main frame - didStartProvisionalLoadForFrame
-main frame - didCommitLoadForFrame
-main frame - didReceiveTitle: SignedHTTPExchange UseCounter
-main frame - didFinishDocumentLoadForFrame
-main frame - didHandleOnloadEventsForFrame
-main frame - didFinishLoadForFrame
-frame "sxg_iframe" - didReceiveTitle: 
-frame "sxg_iframe" - didStartProvisionalLoadForFrame
-frame "sxg_iframe" - didFailProvisionalLoadWithError
-This is a testharness.js-based test.
-FAIL SignedHTTPExchange inner response documents are use counted. promise_test: Unhandled rejection with value: "timeout"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/sxg-origin-trial/http/tests/loading/sxg/sxg-location-expected.txt b/third_party/blink/web_tests/virtual/sxg-origin-trial/http/tests/loading/sxg/sxg-location-expected.txt
deleted file mode 100644
index 29cf41a..0000000
--- a/third_party/blink/web_tests/virtual/sxg-origin-trial/http/tests/loading/sxg/sxg-location-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-main frame - didStartProvisionalLoadForFrame
-main frame - didCommitLoadForFrame
-main frame - didReceiveTitle: Location of SignedHTTPExchange
-main frame - didFinishDocumentLoadForFrame
-main frame - didHandleOnloadEventsForFrame
-main frame - didFinishLoadForFrame
-frame "sxg_iframe" - didReceiveTitle: 
-frame "sxg_iframe" - didStartProvisionalLoadForFrame
-frame "sxg_iframe" - didFailProvisionalLoadWithError
-This is a testharness.js-based test.
-FAIL Location of SignedHTTPExchange promise_test: Unhandled rejection with value: "timeout"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/sxg-origin-trial/http/tests/loading/sxg/sxg-usecounter-expected.txt b/third_party/blink/web_tests/virtual/sxg-origin-trial/http/tests/loading/sxg/sxg-usecounter-expected.txt
deleted file mode 100644
index 9fbbef8..0000000
--- a/third_party/blink/web_tests/virtual/sxg-origin-trial/http/tests/loading/sxg/sxg-usecounter-expected.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-main frame - didStartProvisionalLoadForFrame
-main frame - didCommitLoadForFrame
-main frame - didReceiveTitle: SignedHTTPExchange UseCounter
-main frame - didFinishDocumentLoadForFrame
-main frame - didHandleOnloadEventsForFrame
-main frame - didFinishLoadForFrame
-frame "sxg_iframe" - didReceiveTitle: 
-frame "sxg_iframe" - didStartProvisionalLoadForFrame
-frame "sxg_iframe" - didFailProvisionalLoadWithError
-This is a testharness.js-based test.
-FAIL SignedHTTPExchange inner response documents are use counted. promise_test: Unhandled rejection with value: "timeout"
-Harness: the test ran to completion.
-
diff --git a/third_party/blink/web_tests/virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-desktop.html b/third_party/blink/web_tests/virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-desktop.html
new file mode 100644
index 0000000..c63f1f71
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-desktop.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/gesture-util.js"></script>
+<link href="synthetic-pinch-zoom-gesture.css" rel="stylesheet">
+<script src="synthetic-pinch-zoom-gesture.js"></script>
+
+<script>
+  // This is a less strict version of synthetic-pinch-zoom-gesture-touchpad.html
+  // See synthetic-pinch-zoom-gesture.js.
+
+  const MOUSE_INPUT = 2;  // Gesture source type from synthetic_gesture_params.h
+
+  const t = async_test(
+      "This tests that gpuBenchmarking.pinchBy is relatively accurate for quick touchpad pinch gestures.");
+  const testCases = [
+    { startingScale: 1, scale: 3, speed: 1000, gestureSource: MOUSE_INPUT, msg: "Zooming in quickly" },
+    { startingScale: 3, scale: 0.5, speed: 1000, gestureSource: MOUSE_INPUT, msg: "Zooming out quickly" },
+    { startingScale: 1, scale: 3, speed: 50000, gestureSource: MOUSE_INPUT, msg: "Zooming in instantly" },
+    { startingScale: 4, scale: 0.5, speed: 50000, gestureSource: MOUSE_INPUT, msg: "Zooming out instantly" },
+  ];
+  addEventListener('load', () => {
+    runAllTestCasesDesktop(t, testCases).then(t.done.bind(t));
+  });
+</script>
+<div></div>
diff --git a/third_party/blink/web_tests/virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow-desktop.html b/third_party/blink/web_tests/virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow-desktop.html
new file mode 100644
index 0000000..accbe61
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow-desktop.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="../../../resources/gesture-util.js"></script>
+<link href="synthetic-pinch-zoom-gesture.css" rel="stylesheet">
+<script src="synthetic-pinch-zoom-gesture.js"></script>
+
+<script>
+  // This is a less strict version of synthetic-pinch-zoom-gesture-touchpad-zoom-in-slow.html
+  // See synthetic-pinch-zoom-gesture.js.
+
+  const MOUSE_INPUT = 2;  // Gesture source type from synthetic_gesture_params.h
+
+  const t = async_test(
+      "This tests that gpuBenchmarking.pinchBy is relatively accurate when zooming in slowly for touchpad pinch.");
+  const testCases = [
+    { startingScale: 1, scale: 3, speed: 100, gestureSource: MOUSE_INPUT, msg: "Zooming in slowly" },
+  ];
+  addEventListener('load', () => {
+    runAllTestCasesDesktop(t, testCases).then(t.done.bind(t));
+  });
+</script>
+<div></div>
diff --git a/third_party/blink/web_tests/virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-out-slow-desktop.html b/third_party/blink/web_tests/virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-out-slow-desktop.html
deleted file mode 100644
index 42a28046..0000000
--- a/third_party/blink/web_tests/virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-out-slow-desktop.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<script src="../../../resources/testharness.js"></script>
-<script src="../../../resources/testharnessreport.js"></script>
-<script src="../../../resources/gesture-util.js"></script>
-<link href="synthetic-pinch-zoom-gesture.css" rel="stylesheet">
-<script src="synthetic-pinch-zoom-gesture.js"></script>
-
-<script>
-  // TODO(bokan) - This test is a temporary stop-gap to prevent regressing
-  // partially fixed pinch-zoom on desktops. This test uses very large error
-  // bounds on offset to account for existing bugs. Once those are fixed and
-  // the non -desktop.html version of the test is passing this test can be
-  // removed.
-
-  const TOUCH_INPUT = 1;  // Gesture source type from synthetic_gesture_params.h
-
-  const t = async_test(
-      "This tests that gpuBenchmarking.pinchBy is relatively accurate when zooming out slowly for touchscreen pinch.");
-  const testCases = [
-    { startingScale: 4, scale: 0.5, speed: 100, gestureSource: TOUCH_INPUT, msg: "Zooming out slowly" },
-  ];
-  addEventListener('load', () => {
-    runAllTestCasesDesktop(t, testCases).then(t.done.bind(t));
-  });
-</script>
-<div></div>
diff --git a/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt b/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt
index c0d97ff..f2115b2c 100644
--- a/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt
+++ b/third_party/blink/web_tests/virtual/wasm-site-isolated-code-cache/http/tests/devtools/wasm-isolated-code-cache/wasm-cache-test-expected.txt
@@ -30,23 +30,6 @@
 v8.wasm.compiledModule Properties:
 {
     data : {
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
         url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=large.wasm&cors
     }
     endTime : <number>
@@ -70,6 +53,23 @@
     startTime : <number>
     type : "v8.wasm.streamFromResponseCallback"
 }
+v8.wasm.compiledModule Properties:
+{
+    data : {
+        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.wasm.compiledModule"
+}
+v8.wasm.streamFromResponseCallback Properties:
+{
+    data : {
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.wasm.streamFromResponseCallback"
+}
 v8.wasm.moduleCacheHit Properties:
 {
     data : {
@@ -137,23 +137,6 @@
 v8.wasm.compiledModule Properties:
 {
     data : {
-        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.compiledModule"
-}
-v8.wasm.streamFromResponseCallback Properties:
-{
-    data : {
-    }
-    endTime : <number>
-    startTime : <number>
-    type : "v8.wasm.streamFromResponseCallback"
-}
-v8.wasm.compiledModule Properties:
-{
-    data : {
         url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=large.wasm&cors
     }
     endTime : <number>
@@ -177,6 +160,23 @@
     startTime : <number>
     type : "v8.wasm.streamFromResponseCallback"
 }
+v8.wasm.compiledModule Properties:
+{
+    data : {
+        url : http://127.0.0.1:8000/wasm/resources/load-wasm.php?name=small.wasm&cors
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.wasm.compiledModule"
+}
+v8.wasm.streamFromResponseCallback Properties:
+{
+    data : {
+    }
+    endTime : <number>
+    startTime : <number>
+    type : "v8.wasm.streamFromResponseCallback"
+}
 v8.wasm.moduleCacheHit Properties:
 {
     data : {
diff --git a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
index 18fd974..f366bea 100644
--- a/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
+++ b/third_party/blink/web_tests/virtual/webrtc-wpt-plan-b/external/wpt/webrtc/idlharness.https.window-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 494 tests; 335 PASS, 159 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 494 tests; 341 PASS, 153 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS Test driver for asyncInitCertificate
 FAIL Test driver for asyncInitTransports assert_unreached: Failed to run asyncInitTransports: ReferenceError: RTCSctpTransport is not defined Reached unreachable code
@@ -492,15 +492,15 @@
 FAIL RTCStatsEvent interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "RTCStatsEvent" expected property "RTCStatsEvent" missing
 FAIL RTCStatsEvent interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "RTCStatsEvent" expected property "RTCStatsEvent" missing
 FAIL RTCStatsEvent interface: attribute report assert_own_property: self does not have own property "RTCStatsEvent" expected property "RTCStatsEvent" missing
-FAIL RTCErrorEvent interface: existence and properties of interface object assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface object length assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface object name assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface: existence and properties of interface prototype object assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface: existence and properties of interface prototype object's "constructor" property assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface: existence and properties of interface prototype object's @@unscopables property assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent interface: attribute error assert_own_property: self does not have own property "RTCErrorEvent" expected property "RTCErrorEvent" missing
-FAIL RTCErrorEvent must be primary interface of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: RTCErrorEvent is not defined"
-FAIL Stringification of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: RTCErrorEvent is not defined"
-FAIL RTCErrorEvent interface: new RTCErrorEvent('error') must inherit property "error" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "ReferenceError: RTCErrorEvent is not defined"
+PASS RTCErrorEvent interface: existence and properties of interface object
+FAIL RTCErrorEvent interface object length assert_equals: wrong value for RTCErrorEvent.length expected 1 but got 2
+PASS RTCErrorEvent interface object name
+PASS RTCErrorEvent interface: existence and properties of interface prototype object
+PASS RTCErrorEvent interface: existence and properties of interface prototype object's "constructor" property
+PASS RTCErrorEvent interface: existence and properties of interface prototype object's @@unscopables property
+PASS RTCErrorEvent interface: attribute error
+FAIL RTCErrorEvent must be primary interface of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'RTCErrorEvent': 2 arguments required, but only 1 present."
+FAIL Stringification of new RTCErrorEvent('error') assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'RTCErrorEvent': 2 arguments required, but only 1 present."
+FAIL RTCErrorEvent interface: new RTCErrorEvent('error') must inherit property "error" with the proper type assert_equals: Unexpected exception when evaluating object expected null but got object "TypeError: Failed to construct 'RTCErrorEvent': 2 arguments required, but only 1 present."
 Harness: the test ran to completion.
 
diff --git a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
index 0b40f4b..26afcbd6 100644
--- a/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
+++ b/third_party/blink/web_tests/webexposed/global-interface-listing-expected.txt
@@ -4813,6 +4813,7 @@
     getter presentation
     getter product
     getter productSub
+    getter scheduling
     getter serial
     getter serviceWorker
     getter storage
@@ -5618,6 +5619,19 @@
     method getRemoteCertificates
     setter onerror
     setter onstatechange
+interface RTCError : DOMException
+    attribute @@toStringTag
+    getter errorDetail
+    getter httpRequestStatusCode
+    getter receivedAlert
+    getter sctpCauseCode
+    getter sdpLineNumber
+    getter sentAlert
+    method constructor
+interface RTCErrorEvent : Event
+    attribute @@toStringTag
+    getter error
+    method constructor
 interface RTCIceCandidate
     attribute @@toStringTag
     getter address
@@ -7055,6 +7069,10 @@
     getter zoomAndPan
     method constructor
     setter zoomAndPan
+interface Scheduling
+    attribute @@toStringTag
+    method constructor
+    method isInputPending
 interface Screen
     attribute @@toStringTag
     getter availHeight
@@ -10265,10 +10283,12 @@
     attribute @@toStringTag
     getter currentTime
     getter playState
+    getter playbackRate
     getter timeline
     method cancel
     method constructor
     method play
+    setter playbackRate
 interface WritableStream
     attribute @@toStringTag
     getter locked
diff --git a/third_party/inspector_protocol/README.chromium b/third_party/inspector_protocol/README.chromium
index b6eafdd..606a31f 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: d6a19bda35159094ad1fbfd6d81ce8fdc043943b
+Revision: 8dfd1432319592c9ed70676b6cc166ee98025664
 License: BSD
 License File: LICENSE
 Security Critical: no
diff --git a/third_party/inspector_protocol/lib/Allocator_h.template b/third_party/inspector_protocol/lib/Allocator_h.template
index d05ddae..15eaaaff 100644
--- a/third_party/inspector_protocol/lib/Allocator_h.template
+++ b/third_party/inspector_protocol/lib/Allocator_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Allocator_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Array_h.template b/third_party/inspector_protocol/lib/Array_h.template
index 3854f6e5..c420a0f 100644
--- a/third_party/inspector_protocol/lib/Array_h.template
+++ b/third_party/inspector_protocol/lib/Array_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Array_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/DispatcherBase_cpp.template b/third_party/inspector_protocol/lib/DispatcherBase_cpp.template
index 4b1b2b8..8ab681d 100644
--- a/third_party/inspector_protocol/lib/DispatcherBase_cpp.template
+++ b/third_party/inspector_protocol/lib/DispatcherBase_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by DispatcherBase_cpp.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/DispatcherBase_h.template b/third_party/inspector_protocol/lib/DispatcherBase_h.template
index 4708e032..a69d0220 100644
--- a/third_party/inspector_protocol/lib/DispatcherBase_h.template
+++ b/third_party/inspector_protocol/lib/DispatcherBase_h.template
@@ -1,3 +1,5 @@
+// This file is generated by DispatcherBase_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/ErrorSupport_cpp.template b/third_party/inspector_protocol/lib/ErrorSupport_cpp.template
index 7b858b8d..a5c2a79b 100644
--- a/third_party/inspector_protocol/lib/ErrorSupport_cpp.template
+++ b/third_party/inspector_protocol/lib/ErrorSupport_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by ErrorSupport_cpp.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/ErrorSupport_h.template b/third_party/inspector_protocol/lib/ErrorSupport_h.template
index 11934c3a..f317a3c 100644
--- a/third_party/inspector_protocol/lib/ErrorSupport_h.template
+++ b/third_party/inspector_protocol/lib/ErrorSupport_h.template
@@ -1,3 +1,5 @@
+// This file is generated by ErrorSupport_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Forward_h.template b/third_party/inspector_protocol/lib/Forward_h.template
index ac792e0..ff5e685 100644
--- a/third_party/inspector_protocol/lib/Forward_h.template
+++ b/third_party/inspector_protocol/lib/Forward_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Forward_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/FrontendChannel_h.template b/third_party/inspector_protocol/lib/FrontendChannel_h.template
index 4fba5be..cb04bec 100644
--- a/third_party/inspector_protocol/lib/FrontendChannel_h.template
+++ b/third_party/inspector_protocol/lib/FrontendChannel_h.template
@@ -1,3 +1,5 @@
+// This file is generated by FrontendChannel_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Maybe_h.template b/third_party/inspector_protocol/lib/Maybe_h.template
index 68b166f..22cfac6b 100644
--- a/third_party/inspector_protocol/lib/Maybe_h.template
+++ b/third_party/inspector_protocol/lib/Maybe_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Maybe_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Object_cpp.template b/third_party/inspector_protocol/lib/Object_cpp.template
index 91723a7..1640a11 100644
--- a/third_party/inspector_protocol/lib/Object_cpp.template
+++ b/third_party/inspector_protocol/lib/Object_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by Object_cpp.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Object_h.template b/third_party/inspector_protocol/lib/Object_h.template
index 9efed8e..ec953d0d 100644
--- a/third_party/inspector_protocol/lib/Object_h.template
+++ b/third_party/inspector_protocol/lib/Object_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Object_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Parser_cpp.template b/third_party/inspector_protocol/lib/Parser_cpp.template
index ea276520..ea7ecc5 100644
--- a/third_party/inspector_protocol/lib/Parser_cpp.template
+++ b/third_party/inspector_protocol/lib/Parser_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by Parser_cpp.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Parser_h.template b/third_party/inspector_protocol/lib/Parser_h.template
index 8397d3f5..1832c2e 100644
--- a/third_party/inspector_protocol/lib/Parser_h.template
+++ b/third_party/inspector_protocol/lib/Parser_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Parser_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Protocol_cpp.template b/third_party/inspector_protocol/lib/Protocol_cpp.template
index 1167ed4..88303a2 100644
--- a/third_party/inspector_protocol/lib/Protocol_cpp.template
+++ b/third_party/inspector_protocol/lib/Protocol_cpp.template
@@ -1,4 +1,4 @@
-// This file is generated.
+// This file is generated by Protocol_cpp.template.
 
 // Copyright 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
diff --git a/third_party/inspector_protocol/lib/ValueConversions_h.template b/third_party/inspector_protocol/lib/ValueConversions_h.template
index 6549e5ab..49ae9e2 100644
--- a/third_party/inspector_protocol/lib/ValueConversions_h.template
+++ b/third_party/inspector_protocol/lib/ValueConversions_h.template
@@ -1,3 +1,5 @@
+// This file is generated by ValueConversions_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Values_cpp.template b/third_party/inspector_protocol/lib/Values_cpp.template
index b9f0613..06b88881 100644
--- a/third_party/inspector_protocol/lib/Values_cpp.template
+++ b/third_party/inspector_protocol/lib/Values_cpp.template
@@ -1,3 +1,5 @@
+// This file is generated by Values_cpp.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/lib/Values_h.template b/third_party/inspector_protocol/lib/Values_h.template
index e8eb91d..69dde07 100644
--- a/third_party/inspector_protocol/lib/Values_h.template
+++ b/third_party/inspector_protocol/lib/Values_h.template
@@ -1,3 +1,5 @@
+// This file is generated by Values_h.template.
+
 // 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.
diff --git a/third_party/inspector_protocol/templates/Exported_h.template b/third_party/inspector_protocol/templates/Exported_h.template
index 3d36ecffa..a8c3d73 100644
--- a/third_party/inspector_protocol/templates/Exported_h.template
+++ b/third_party/inspector_protocol/templates/Exported_h.template
@@ -1,4 +1,4 @@
-// This file is generated
+// This file is generated by Exported_h.template.
 
 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
diff --git a/third_party/inspector_protocol/templates/Imported_h.template b/third_party/inspector_protocol/templates/Imported_h.template
index 4c9d24b..396c690 100644
--- a/third_party/inspector_protocol/templates/Imported_h.template
+++ b/third_party/inspector_protocol/templates/Imported_h.template
@@ -1,4 +1,4 @@
-// This file is generated
+// This file is generated by Imported_h.template.
 
 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
diff --git a/third_party/inspector_protocol/templates/TypeBuilder_cpp.template b/third_party/inspector_protocol/templates/TypeBuilder_cpp.template
index f99ce5f..b6a297c 100644
--- a/third_party/inspector_protocol/templates/TypeBuilder_cpp.template
+++ b/third_party/inspector_protocol/templates/TypeBuilder_cpp.template
@@ -1,4 +1,4 @@
-// This file is generated
+// This file is generated by TypeBuilder_cpp.template.
 
 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
diff --git a/third_party/inspector_protocol/templates/TypeBuilder_h.template b/third_party/inspector_protocol/templates/TypeBuilder_h.template
index 291b2a7..cb9ab31 100644
--- a/third_party/inspector_protocol/templates/TypeBuilder_h.template
+++ b/third_party/inspector_protocol/templates/TypeBuilder_h.template
@@ -1,4 +1,4 @@
-// This file is generated
+// This file is generated by TypeBuilder_h.template.
 
 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 70ba753d..e5dcae2 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -27,7 +27,7 @@
 # Do NOT CHANGE this if you don't know what you're doing -- see
 # https://chromium.googlesource.com/chromium/src/+/master/docs/updating_clang.md
 # Reverting problematic clang rolls is safe, though.
-CLANG_REVISION = '352138'
+CLANG_REVISION = '353069'
 
 use_head_revision = bool(os.environ.get('LLVM_FORCE_HEAD_REVISION', '0')
                          in ('1', 'YES'))
@@ -35,7 +35,7 @@
   CLANG_REVISION = 'HEAD'
 
 # This is incremented when pushing a new build of Clang at the same revision.
-CLANG_SUB_REVISION=3
+CLANG_SUB_REVISION=1
 
 PACKAGE_VERSION = "%s-%s" % (CLANG_REVISION, CLANG_SUB_REVISION)
 
diff --git a/tools/cygprofile/orderfile_generator_backend.py b/tools/cygprofile/orderfile_generator_backend.py
index 6649a8d..c84f2f2 100755
--- a/tools/cygprofile/orderfile_generator_backend.py
+++ b/tools/cygprofile/orderfile_generator_backend.py
@@ -466,6 +466,9 @@
       assert not options.profile_save_dir, (
           '--profile-save-dir cannot be used with --skip-profile')
 
+    # Outlined function handling enabled by default for all architectures.
+    self._order_outlined_functions = not options.noorder_outlined_functions
+
     self._output_data = {}
     self._step_recorder = StepRecorder(options.buildbot)
     self._compiler = None
@@ -584,7 +587,7 @@
     self._step_recorder.BeginStep('Patch Orderfile')
     patch_orderfile.GeneratePatchedOrderfile(
         self._GetUnpatchedOrderfileFilename(), self._compiler.lib_chrome_so,
-        self._GetPathToOrderfile())
+        self._GetPathToOrderfile(), self._order_outlined_functions)
 
   def _VerifySymbolOrder(self):
     self._step_recorder.BeginStep('Verify Symbol Order')
@@ -835,6 +838,8 @@
   parser.add_argument('--manual-objdir', default=None, type=str,
                       help=('Root of object file directory corresponding to '
                             '--manual-symbol-offsets.'))
+  parser.add_argument('--noorder-outlined-functions', action='store_true',
+                      help='Disable outlined functions in the orderfile.')
   parser.add_argument('--pregenerated-profiles', default=None, type=str,
                       help=('Pregenerated profiles to use instead of running '
                             'profile step. Cannot be used with '
diff --git a/tools/cygprofile/patch_orderfile.py b/tools/cygprofile/patch_orderfile.py
index 54f2991..abc563f0 100755
--- a/tools/cygprofile/patch_orderfile.py
+++ b/tools/cygprofile/patch_orderfile.py
@@ -28,6 +28,7 @@
 import argparse
 import collections
 import logging
+import re
 import sys
 
 import cyglog_to_orderfile
@@ -40,6 +41,10 @@
 _SUFFIXES = ('.clone.', '.part.', '.isra.', '.constprop.')
 
 
+# The pattern and format for a linker-generated outlined function.
+_OUTLINED_FUNCTION_RE = re.compile(r'OUTLINED_FUNCTION_(?P<index>\d+)$')
+_OUTLINED_FUNCTION_FORMAT = 'OUTLINED_FUNCTION_{}'
+
 def RemoveSuffixes(name):
   """Strips method name suffixes from cloning and splitting.
 
@@ -98,11 +103,81 @@
   return sym_to_matching
 
 
+def _GetMaxOutlinedIndex(sym_dict):
+  """Find the largest index of an outlined functions.
+
+  See _OUTLINED_FUNCTION_RE for the definition of the index. In practice the
+  maximum index equals the total number of outlined functions. This function
+  asserts that the index is near the total number of outlined functions.
+
+  Args:
+    sym_dict: Dict with symbol names as keys.
+
+  Returns:
+    The largest index of an outlined function seen in the keys of |sym_dict|.
+  """
+  seen = set()
+  for sym in sym_dict:
+    m = _OUTLINED_FUNCTION_RE.match(sym)
+    if m:
+      seen.add(int(m.group('index')))
+  max_index = max(seen)
+  # Assert that the number of outlined functions is reasonable compared to the
+  # indices we've seen. At the time of writing, outlined functions are indexed
+  # consecutively from 0. If this radically changes, then other outlining
+  # behavior may have changed to violate some assumptions.
+  assert max_index < 2 * len(seen)
+  return max_index
+
+
 def _StripSuffixes(section_list):
   """Remove all suffixes on items in a list of symbols."""
   return [RemoveSuffixes(section) for section in section_list]
 
 
+def _PatchedSymbols(symbol_to_matching, profiled_symbols, max_outlined_index):
+  """Internal computation of an orderfile.
+
+  Args:
+    symbol_to_matching: ({symbol name -> [symbols at same offset]}), as from
+      _GroupSymbolsByOffset.
+    profiled_symbols: ([symbol names]) as from the unpatched orderfile.
+    max_outlined_index: (int or None) if not None, add outlined function names
+      to the end of the patched orderfile.
+
+  Yields:
+    Patched symbols, in a consistent order to profiled_symbols.
+  """
+  missing_symbol_count = 0
+  seen_symbols = set()
+  for sym in profiled_symbols:
+    if _OUTLINED_FUNCTION_RE.match(sym):
+      continue
+    if sym in seen_symbols:
+      continue
+    if sym not in symbol_to_matching:
+      missing_symbol_count += 1
+      continue
+    for matching in symbol_to_matching[sym]:
+      if matching in seen_symbols:
+        continue
+      if _OUTLINED_FUNCTION_RE.match(matching):
+        continue
+      yield matching
+      seen_symbols.add(matching)
+    assert sym in seen_symbols
+  logging.warning('missing symbol count = %d', missing_symbol_count)
+
+  if max_outlined_index is not None:
+    # The number of outlined functions may change with each build, so only
+    # ordering the outlined functions currently in the binary will not
+    # guarantee ordering after code changes before the next orderfile is
+    # generated. So we double the number of outlined functions as a measure of
+    # security.
+    for idx in xrange(2 * max_outlined_index + 1):
+      yield _OUTLINED_FUNCTION_FORMAT.format(idx)
+
+
 @_UniqueGenerator
 def ReadOrderfile(orderfile):
   """Reads an orderfile and cleans up symbols.
@@ -121,30 +196,27 @@
 
 
 def GeneratePatchedOrderfile(unpatched_orderfile, native_lib_filename,
-                             output_filename):
+                             output_filename, order_outlined=False):
   """Writes a patched orderfile.
 
   Args:
     unpatched_orderfile: (str) Path to the unpatched orderfile.
     native_lib_filename: (str) Path to the native library.
     output_filename: (str) Path to the patched orderfile.
+    order_outlined: (bool) If outlined function symbols are present in the
+      native library, then add ordering of them to the orderfile. If there
+      are no outlined function symbols present then this flag has no effect.
   """
   symbol_to_matching = _GroupSymbolsByOffset(native_lib_filename)
+  if order_outlined:
+    max_outlined_index = _GetMaxOutlinedIndex(symbol_to_matching)
+    if not max_outlined_index:
+      # Only generate ordered outlined functions if they already appeared in
+      # the library.
+      max_outlined_index = None
+  else:
+    max_outlined_index = None  # Ignore outlining.
   profiled_symbols = ReadOrderfile(unpatched_orderfile)
-  missing_symbol_count = 0
-  seen_symbols = set()
-  patched_symbols = []
-  for sym in profiled_symbols:
-    if sym not in symbol_to_matching:
-      missing_symbol_count += 1
-      continue
-    if sym in seen_symbols:
-      continue
-    for matching in symbol_to_matching[sym]:
-      patched_symbols.append(matching)
-      seen_symbols.add(matching)
-    assert sym in seen_symbols
-  logging.warning('missing symbol count = %d', missing_symbol_count)
 
   with open(output_filename, 'w') as f:
     # Make sure the anchor functions are located in the right place, here and
@@ -157,10 +229,11 @@
                           '__cxx_global_var_init'):
       f.write(first_section + '\n')
 
-    for sym in patched_symbols:
+    for sym in _PatchedSymbols(symbol_to_matching, profiled_symbols,
+                               max_outlined_index):
       f.write(sym + '\n')
 
-    f.write('dummy_function_end_of_ordered_text')
+    f.write('dummy_function_end_of_ordered_text\n')
 
 
 def _CreateArgumentParser():
diff --git a/tools/cygprofile/patch_orderfile_unittest.py b/tools/cygprofile/patch_orderfile_unittest.py
index db225b2..ee7b1381 100755
--- a/tools/cygprofile/patch_orderfile_unittest.py
+++ b/tools/cygprofile/patch_orderfile_unittest.py
@@ -30,6 +30,34 @@
 
     self.assertEqual(list(TestIterator()), [1,2,3])
 
+  def testMaxOutlinedIndex(self):
+    self.assertEquals(7, patch_orderfile._GetMaxOutlinedIndex(
+        {'OUTLINED_FUNCTION_{}'.format(idx): None
+         for idx in [1, 2, 3, 7]}))
+    self.assertRaises(AssertionError, patch_orderfile._GetMaxOutlinedIndex,
+                      {'OUTLINED_FUNCTION_{}'.format(idx): None
+                       for idx in [1, 200, 3, 11]})
 
-if __name__ == "__main__":
+  def testPatchedSymbols(self):
+    # From input symbols a b c d, symbols a and d match themselves, symbol
+    # b matches b and x, and symbol c is missing.
+    self.assertEquals(list('abxd'),
+                      list(patch_orderfile._PatchedSymbols(
+                          {'a': 'a', 'b': 'bx', 'd': 'd'},
+                          'abcd', None)))
+
+  def testPatchedSymbolsWithOutlining(self):
+    # As above, but add outlined functions at the end. The aliased outlined
+    # function should be ignored.
+    self.assertEquals(list('abd') +
+                      ['OUTLINED_FUNCTION_{}'.format(i)
+                       for i in xrange(5)],
+                      list(patch_orderfile._PatchedSymbols(
+                          {'a': 'a',
+                           'b': ['b', 'OUTLINED_FUNCTION_4'],
+                           'd': 'd'},
+                          ['a', 'b', 'OUTLINED_FUNCTION_2', 'c', 'd'], 2)))
+
+
+if __name__ == '__main__':
   unittest.main()
diff --git a/tools/cygprofile/symbol_extractor.py b/tools/cygprofile/symbol_extractor.py
index c087f5ed..6912732 100644
--- a/tools/cygprofile/symbol_extractor.py
+++ b/tools/cygprofile/symbol_extractor.py
@@ -168,8 +168,11 @@
         name_to_offsets[symbol_info.name].append(symbol_info.offset)
       symbol_infos.append(symbol_info)
 
+  # Outlined functions are known to be repeated often, so ignore them in the
+  # repeated symbol count.
   repeated_symbols = filter(lambda s: len(name_to_offsets[s]) > 1,
-                            name_to_offsets.iterkeys())
+                            (k for k in name_to_offsets.keys()
+                             if not k.startswith('OUTLINED_FUNCTION_')))
   if repeated_symbols:
     # Log the first 5 repeated offsets of the first 10 repeated symbols.
     logging.warning('%d symbols repeated with multiple offsets:\n %s',
diff --git a/tools/cygprofile/symbol_extractor_unittest.py b/tools/cygprofile/symbol_extractor_unittest.py
index 033ae94e..175cf7b 100755
--- a/tools/cygprofile/symbol_extractor_unittest.py
+++ b/tools/cygprofile/symbol_extractor_unittest.py
@@ -128,6 +128,18 @@
     self.assertEquals('_ZZL11get_globalsvENK3$_1clEv', symbol_info.name)
     self.assertEquals('.text', symbol_info.section)
 
+  def testOutlinedFunction(self):
+    # Test that an outlined function is reported normally. Also note that
+    # outlined functions are in 64 bit builds which have longer addresses.
+    line = ('00000000020fab4c l     F .text\t0000000000000014' + SPACES +
+            'OUTLINED_FUNCTION_4')
+    symbol_info = symbol_extractor._FromObjdumpLine(line)
+    self.assertIsNotNone(symbol_info)
+    self.assertEquals(0x20fab4c, symbol_info.offset)
+    self.assertEquals(0x14, symbol_info.size)
+    self.assertEquals('OUTLINED_FUNCTION_4', symbol_info.name)
+    self.assertEquals('.text', symbol_info.section)
+
 
 class TestSymbolInfosFromStream(unittest.TestCase):
 
diff --git a/tools/gritsettings/resource_ids b/tools/gritsettings/resource_ids
index bc56e877..bcce629 100644
--- a/tools/gritsettings/resource_ids
+++ b/tools/gritsettings/resource_ids
@@ -415,16 +415,16 @@
     "messages": [27350],
   },
   "ui/views/resources/views_resources.grd": {
-    "structures": [27550],
+    "structures": [27580],
   },
   "ui/webui/resources/webui_resources.grd": {
-    "includes": [27850],
-    "structures": [28050],
+    "includes": [27880],
+    "structures": [28080],
   },
 
   # This file is generated during the build.
   "<(SHARED_INTERMEDIATE_DIR)/devtools/devtools_resources.grd": {
-    "includes": [28850],
+    "includes": [28880],
   },
 
   # END "everything else" section.
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index ab0bbf22..9fa6beb 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -2210,6 +2210,15 @@
   </description>
 </action>
 
+<action name="ArcAppReinstall_Clicked">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <owner>robsc@chromium.org</owner>
+  <owner>napper@chromium.org</owner>
+  <description>
+    A click on an app reinstall app candidate from Arc++.
+  </description>
+</action>
+
 <action name="AutoDetectChange">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
@@ -19873,6 +19882,11 @@
   </description>
 </action>
 
+<action name="Tab.Screenshot.WithoutStoragePermission">
+  <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
+  <description>Please enter the description of the metric.</description>
+</action>
+
 <action name="Tab_DropURLBetweenTabs">
   <owner>Please list the metric's owners. Add more owner tags as needed.</owner>
   <description>Please enter the description of this user action.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b60abaa..dc29cbc 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -15370,6 +15370,7 @@
   <int value="515" label="PluginVmLicenseKey"/>
   <int value="516" label="ExtensionAllowInsecureUpdates"/>
   <int value="517" label="BrowserSwitcherEnabled"/>
+  <int value="518" label="CertificateManagementAllowed"/>
 </enum>
 
 <enum name="EnterprisePolicyInvalidations">
@@ -21498,6 +21499,8 @@
   <int value="2774" label="CSSValueAppearanceButtonForNonButtonRendered"/>
   <int value="2775" label="CSSValueAppearanceButtonForOthersRendered"/>
   <int value="2776" label="CustomCursorIntersectsViewport"/>
+  <int value="2777" label="ClientHintsLang"/>
+  <int value="2778" label="LinkRelPreloadImageSrcset"/>
 </enum>
 
 <enum name="FeaturePolicyFeature">
@@ -32160,6 +32163,7 @@
   <int value="651421878" label="VideoRotateToFullscreen:enabled"/>
   <int value="651844675" label="EasyUnlockPromotions:enabled"/>
   <int value="652561231" label="CustomContextMenu:enabled"/>
+  <int value="656864700" label="FillOnAccountSelectHttp:disabled"/>
   <int value="659086147" label="OverlayScrollbarFlashWhenMouseEnter:enabled"/>
   <int value="663027937" label="NewTabPageCustomLinks:enabled"/>
   <int value="664363259" label="SiteCharacteristicsDatabase:enabled"/>
@@ -32651,6 +32655,7 @@
   <int value="1487341558" label="MacViewsAutofillPopup:enabled"/>
   <int value="1488193175" label="ChromeOSAssistant:enabled"/>
   <int value="1488700164" label="password-import:disabled"/>
+  <int value="1488998575" label="FillOnAccountSelectHttp:enabled"/>
   <int value="1489915799" label="disable-permissions-blacklist"/>
   <int value="1490043732" label="enable-fill-on-account-select"/>
   <int value="1490255042" label="enable-overlay-scrollbar"/>
@@ -35010,6 +35015,7 @@
   <int value="12" label="Not Supported (NotSupportedError)"/>
   <int value="13" label="Failed Due To Shutdown (NotAllowedError)"/>
   <int value="14" label="Kill Switch On (NotAllowedError)"/>
+  <int value="15" label="Permission Denied By System (NotAllowedError)"/>
 </enum>
 
 <enum name="MediaStreamRequestState">
@@ -45341,6 +45347,15 @@
   <int value="2" label="Fixed timer"/>
 </enum>
 
+<enum name="ReinstallResponseParseResult">
+  <int value="0" label="Good result."/>
+  <int value="1" label="Timeout."/>
+  <int value="2" label="No acccount"/>
+  <int value="3" label="Null data"/>
+  <int value="4" label="Bundle error."/>
+  <int value="5" label="Unknown error."/>
+</enum>
+
 <enum name="RelaunchNotificationShowResult">
   <int value="0" label="Shown"/>
   <int value="1" label="Not shown for unknown reason"/>
@@ -45751,7 +45766,7 @@
   <int value="63" label="IDC_WRITING_DIRECTION_DEFAULT"/>
   <int value="64" label="IDC_WRITING_DIRECTION_LTR"/>
   <int value="65" label="IDC_WRITING_DIRECTION_RTL"/>
-  <int value="66" label="IDC_CONTENT_CONTEXT_LOAD_ORIGINAL_IMAGE"/>
+  <int value="66" label="IDC_CONTENT_CONTEXT_LOAD_IMAGE"/>
   <int value="67" label="IDC_CONTENT_CONTEXT_FORCESAVEPASSWORD"/>
   <int value="68" label="IDC_ROUTE_MEDIA"/>
   <int value="69" label="IDC_CONTENT_CONTEXT_COPYLINKTEXT"/>
@@ -52973,6 +52988,20 @@
   <int value="6" label="All services were enabled during opt-in"/>
 </enum>
 
+<enum name="UnifiedConsentSyncDataTypesOffAfterAdvancedOptIn">
+  <int value="0" label="None"/>
+  <int value="1" label="Apps"/>
+  <int value="2" label="Bookmarks"/>
+  <int value="3" label="Extensions"/>
+  <int value="4" label="History"/>
+  <int value="5" label="Settings"/>
+  <int value="6" label="Themes"/>
+  <int value="7" label="Tabs"/>
+  <int value="8" label="Passwords"/>
+  <int value="9" label="Autofill"/>
+  <int value="10" label="Payments"/>
+</enum>
+
 <enum name="UniformityTrialGroupNotActive">
   <int value="0" label="Invalid"/>
   <int value="1" label="Group not reported"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 02ff22b..843bcb98 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -594,7 +594,7 @@
 
 <histogram name="ActivityTracker.Collect.AnalyzerCreationError"
     enum="ActivityTrackerAnalyzerCreationError">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     The analyzer creation error code. Logged each time analyzer creation fails,
     at most once per processed stability debug file.
@@ -603,7 +603,7 @@
 
 <histogram name="ActivityTracker.Collect.InitStatus"
     enum="ActivityTrackerCollectInitStatus">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Status of the initialization to collect stability debug files. Logged once,
     during the initialization of the stability debug file collection.
@@ -611,7 +611,7 @@
 </histogram>
 
 <histogram name="ActivityTracker.Collect.StabilityFileCount" units="count">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Number of files found during stability file collection. Logged each time the
     stability file collection proceeds (at most once per launch).
@@ -620,7 +620,7 @@
 
 <histogram name="ActivityTracker.Collect.Status"
     enum="ActivityTrackerCollectStatus">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Status for the collection of a stability debug file. Logged each time a
     debug file collection attempt is made.
@@ -629,7 +629,7 @@
 
 <histogram name="ActivityTracker.Collect.SystemSessionAnalysisStatus"
     enum="ActivityTrackerSystemSessionAnalysisStatus">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Status for the analysis of the system session state. Logged each time a
     debug file is collected.
@@ -637,7 +637,7 @@
 </histogram>
 
 <histogram name="ActivityTracker.Collect.TotalTime" units="ms">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Time spent collecting stability debug information. Logged each time a
     collection is performed.
@@ -669,7 +669,7 @@
 
 <histogram name="ActivityTracker.Collect.WriteDumpStatus"
     enum="ActivityTrackerWriteDumpStatus">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Status of the minidump writing. Logged each time a writing a minidump is
     attempted.
@@ -678,7 +678,7 @@
 
 <histogram name="ActivityTracker.CollectCrash.Event"
     enum="ActivityTrackerCollectOnCrashEvent">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Events that occur during crash collection of the debug file. Logged each
     time an event of interest occurs during crash debug file collection.
@@ -700,7 +700,7 @@
 
 <histogram name="ActivityTracker.CollectCrash.Status"
     enum="ActivityTrackerCollectStatus">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Status for the collection of a stability debug file. Logged each time a
     debug file collection attempt is made from the crash handler.
@@ -709,7 +709,7 @@
 
 <histogram name="ActivityTracker.Record.Event"
     enum="ActivityTrackerRecordEvent">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Events pertaining to recording to the debug file. Logged each time an event
     of interest occurs wrt debug file recording.
@@ -729,7 +729,7 @@
 </histogram>
 
 <histogram name="ActivityTracker.Record.SetupTime" units="ms">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Time spent setting up the stability debugging instrumentation. Logged once,
     during setup of the stability debugging instrumentation.
@@ -2150,7 +2150,7 @@
 
 <histogram name="Android.IsLastSharedAppInfoRetrieved"
     enum="BooleanIsLastSharedAppInfoRetrieved">
-  <owner>jaekyun@chromium.org</owner>
+  <owner>clank-team@google.com</owner>
   <summary>
     Signifies whether the last shared app information is retrieved successfully
     or not. This is logged when configuring the direct sharing menu item.
@@ -2266,7 +2266,7 @@
 </histogram>
 
 <histogram name="Android.ModerateBindingCount" units="bindings">
-  <owner>jaekyun@chromium.org</owner>
+  <owner>clank-team@google.com</owner>
   <summary>
     The number of moderate bindings which were kept while Chrome process is in
     the foreground. This is logged right before Chrome process goes into the
@@ -2409,6 +2409,8 @@
 
 <histogram name="Android.Omnibox.InvalidMatch" enum="MatchResult">
   <owner>tedchoc@chromium.org</owner>
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Recorded every time AutocompleteController.java interacts with
     autocomplete_controller_android.cc via an index reference to an item.
@@ -2421,6 +2423,8 @@
   <owner>mdjones@chromium.org</owner>
   <owner>twellington@chromium.org</owner>
   <owner>amaralp@chromium.org</owner>
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>Records how the omnibox was focused.</summary>
 </histogram>
 
@@ -3640,6 +3644,34 @@
   <summary>The state of a Play Store app search request.</summary>
 </histogram>
 
+<histogram name="Apps.AppListRecommendedResponse"
+    enum="ReinstallResponseParseResult">
+  <owner>napper@chromium.org</owner>
+  <owner>robsc@chromium.org</owner>
+  <summary>
+    Response when parsing the recommended app list from Play Store for candidate
+    app list, recorded every time a response from the play store is parsed in
+    client.
+  </summary>
+</histogram>
+
+<histogram name="Apps.AppListRecommendedResponse.Count">
+  <owner>napper@chromium.org</owner>
+  <owner>robsc@chromium.org</owner>
+  <summary>
+    Number of responses from server in Apps.AppListRecommendedResponse.count
+    when fetching appListRecommendded GetAppReinstallCandidates.
+  </summary>
+</histogram>
+
+<histogram name="Apps.AppListRecommendedResponse.Latency" units="ms">
+  <owner>napper@chromium.org</owner>
+  <owner>robsc@chromium.org</owner>
+  <summary>
+    Milliseconds elapsed to fetch appListRecommended GetAppReinstallCandidates.
+  </summary>
+</histogram>
+
 <histogram name="Apps.AppListSearchBoxActivated"
     enum="SearchBoxActivationSource">
   <owner>newcomer@chromium.org</owner>
@@ -3663,6 +3695,8 @@
     enum="AppListOmniboxResult" expires_after="2019-12-31">
   <owner>jennyz@chromium.org</owner>
   <owner>newcomer@chromium.org</owner>
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The type of app list search omnibox result that was opened by the user. This
     is gathered per OmniboxResult opened in the app list's launcher suggestion
@@ -5026,7 +5060,7 @@
 
 <histogram name="Ash.Login.Lock.AuthMethod.Switched"
     enum="AuthMethodSwitchType">
-  <owner>xiaoyinh@google.com</owner>
+  <owner>jdufault@google.com</owner>
   <summary>
     The count of auth method switching actions in ChromeOS lock screen.
   </summary>
@@ -5034,7 +5068,7 @@
 
 <histogram name="Ash.Login.Lock.AuthMethod.Used.ClamShellMode"
     enum="AuthMethod">
-  <owner>xiaoyinh@google.com</owner>
+  <owner>jdufault@google.com</owner>
   <summary>
     The usage of different auth methods (PIN / Password / Smartlock /
     Fingerprint) in ChromeOS lock screen clamshell mode.
@@ -5042,7 +5076,7 @@
 </histogram>
 
 <histogram name="Ash.Login.Lock.AuthMethod.Used.TabletMode" enum="AuthMethod">
-  <owner>xiaoyinh@google.com</owner>
+  <owner>jdufault@google.com</owner>
   <summary>
     The usage of different auth methods (PIN / Password / Smartlock /
     Fingerprint) in ChromeOS lock screen tablet mode.
@@ -5050,7 +5084,7 @@
 </histogram>
 
 <histogram name="Ash.Login.Lock.NumPasswordAttempts.UntilFailure" units="count">
-  <owner>xiaoyinh@google.com</owner>
+  <owner>jdufault@google.com</owner>
   <summary>
     The number of incorrect password entered in ChromeOS lock screen until the
     user gives up (switch pods or user sign out the current session or shutdown
@@ -5059,7 +5093,7 @@
 </histogram>
 
 <histogram name="Ash.Login.Lock.NumPasswordAttempts.UntilSuccess" units="count">
-  <owner>xiaoyinh@google.com</owner>
+  <owner>jdufault@google.com</owner>
   <summary>
     The number of incorrect password entered in ChromeOS lock screen until a
     successful attempt.
@@ -5067,7 +5101,7 @@
 </histogram>
 
 <histogram name="Ash.Login.Lock.UserClicks" enum="LockScreenUserClickTarget">
-  <owner>xiaoyinh@google.com</owner>
+  <owner>jdufault@google.com</owner>
   <summary>
     The numbers of times that users click on the shelf buttons, trays and lock
     screen note on the ChromeOS lock screen.
@@ -5112,8 +5146,7 @@
 </histogram>
 
 <histogram name="Ash.NumberOfVisibleWindowsInPrimaryDisplay" units="Windows">
-  <owner>tdanderson@google.com</owner>
-  <owner>bruthig@google.com</owner>
+  <owner>jamescook@chromium.org</owner>
   <summary>
     An upper bound on the number of windows visible to the user on the primary
     display. Determined by processing the windows in increasing z-order and
@@ -5274,7 +5307,7 @@
 </histogram>
 
 <histogram name="Ash.Shelf.Palette.InLaserPointerMode" units="ms">
-  <owner>xiaoyinh@chromium.org</owner>
+  <owner>jdufault@chromium.org</owner>
   <summary>
     The amount of time spend in Palette Laser pointer mode. Recorded when the
     Laser pointer mode is exited.
@@ -5282,7 +5315,7 @@
 </histogram>
 
 <histogram name="Ash.Shelf.Palette.InMagnifyMode" units="ms">
-  <owner>xiaoyinh@chromium.org</owner>
+  <owner>jdufault@chromium.org</owner>
   <summary>
     The amount of time spend in Palette Magnify mode. Recorded when the Magnify
     mode is exited.
@@ -5291,7 +5324,7 @@
 
 <histogram name="Ash.Shelf.Palette.ModeCancellation"
     enum="PaletteModeCancelType">
-  <owner>xiaoyinh@chromium.org</owner>
+  <owner>jdufault@chromium.org</owner>
   <summary>
     Tracks the number of times a palette mode is explicitly cancelled or
     switched out of.
@@ -5299,7 +5332,7 @@
 </histogram>
 
 <histogram name="Ash.Shelf.Palette.Usage" enum="PaletteTrayOptions">
-  <owner>xiaoyinh@chromium.org</owner>
+  <owner>jdufault@chromium.org</owner>
   <summary>
     Recorded every time that the palette option has been selected from the
     palette that has been opened manually (not via a stylus eject event).
@@ -5307,7 +5340,7 @@
 </histogram>
 
 <histogram name="Ash.Shelf.Palette.Usage.AutoOpened" enum="PaletteTrayOptions">
-  <owner>xiaoyinh@chromium.org</owner>
+  <owner>jdufault@chromium.org</owner>
   <summary>
     Recorded every time that the palette option has been selected from the
     palette that has been opened automatically (by a stylus eject event).
@@ -5398,7 +5431,6 @@
 
 <histogram name="Ash.SystemMenu.PercentageOfWorkAreaHeightCoveredByMenu"
     units="%">
-  <owner>tdanderson@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
     The height of the system menu divided by the height of the Ash desktop work
@@ -5408,7 +5440,6 @@
 </histogram>
 
 <histogram name="Ash.SystemMenu.Rows" units="rows">
-  <owner>tdanderson@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
     The number of user-visible rows in the system menu's default view. Recorded
@@ -5518,7 +5549,6 @@
 </histogram>
 
 <histogram name="Ash.TouchStartAfterEnd" units="ms">
-  <owner>tdanderson@chromium.org</owner>
   <owner>kuscher@google.com</owner>
   <summary>
     The interval between the end of a touch-sequence and the start of the next
@@ -5768,8 +5798,7 @@
 </histogram>
 
 <histogram name="Ash.WindowSelector.AnimationSmoothness.Close" units="%">
-  <owner>wutao@chromium.org</owner>
-  <owner>tdanderson@chromium.org</owner>
+  <owner>estade@chromium.org</owner>
   <summary>
     Relative smoothness of animations when closing a window in overview mode.
     100% represents ideally smooth 60 frames per second.
@@ -5777,8 +5806,7 @@
 </histogram>
 
 <histogram name="Ash.WindowSelector.AnimationSmoothness.Enter" units="%">
-  <owner>wutao@chromium.org</owner>
-  <owner>tdanderson@chromium.org</owner>
+  <owner>estade@chromium.org</owner>
   <summary>
     Relative smoothness of animations when entering overview mode. 100%
     represents ideally smooth 60 frames per second.
@@ -5786,8 +5814,7 @@
 </histogram>
 
 <histogram name="Ash.WindowSelector.AnimationSmoothness.Exit" units="%">
-  <owner>wutao@chromium.org</owner>
-  <owner>tdanderson@chromium.org</owner>
+  <owner>estade@chromium.org</owner>
   <summary>
     Relative smoothness of animations when exiting overview mode. 100%
     represents ideally smooth 60 frames per second.
@@ -5796,7 +5823,6 @@
 
 <histogram name="Ash.WindowSelector.ArrowKeyPresses">
   <owner>flackr@chromium.org</owner>
-  <owner>tdanderson@chromium.org</owner>
   <summary>
     The number of times the arrow keys are pressed in overview mode per session,
     i.e. between bringing up overview mode and ending it. This is only measured
@@ -5827,7 +5853,6 @@
 </histogram>
 
 <histogram name="Ash.WindowSelector.ItemsWhenTextFilteringUsed" units="items">
-  <owner>tdanderson@chromium.org</owner>
   <owner>flackr@chromium.org</owner>
   <summary>
     The number of items showing in overview mode at the moment when an item is
@@ -5838,7 +5863,6 @@
 
 <histogram name="Ash.WindowSelector.KeyPressesOverItemsRatio" units="%">
   <owner>flackr@chromium.org</owner>
-  <owner>tdanderson@chromium.org</owner>
   <summary>
     The ratio between the arrow key presses and the number of overview items,
     expressed as a percentage for a single session.
@@ -5847,15 +5871,13 @@
 
 <histogram name="Ash.WindowSelector.OverviewClosedItems">
   <owner>flackr@chromium.org</owner>
-  <owner>tdanderson@chromium.org</owner>
   <summary>
     The number of items closed from the window overview for a single session.
   </summary>
 </histogram>
 
 <histogram name="Ash.WindowSelector.SelectionDepth" units="items">
-  <owner>wutao@chromium.org</owner>
-  <owner>tdanderson@chromium.org</owner>
+  <owner>estade@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
     When a window is selected in overview mode, records that window's position
@@ -5866,7 +5888,6 @@
 
 <histogram name="Ash.WindowSelector.TextFilteringStringLength"
     units="characters">
-  <owner>tdanderson@chromium.org</owner>
   <owner>flackr@chromium.org</owner>
   <summary>
     The length of the string entered into the text filtering textfield at the
@@ -5875,7 +5896,6 @@
 </histogram>
 
 <histogram name="Ash.WindowSelector.TextFilteringTextfieldCleared">
-  <owner>tdanderson@chromium.org</owner>
   <owner>flackr@chromium.org</owner>
   <summary>
     The number of times the text filtering textfield has had all of its text
@@ -5888,7 +5908,6 @@
 <histogram name="Ash.WindowSelector.TimeBetweenActiveWindowChanges"
     units="seconds">
   <owner>bruthig@chromium.org</owner>
-  <owner>tdanderson@chromium.org</owner>
   <summary>
     The amount of time between endings of overview mode sessions which were
     caused by the user selecting a window which was not previously active. Only
@@ -5919,7 +5938,6 @@
 </histogram>
 
 <histogram name="Ash.WindowSelector.TimeInOverviewWithTextFiltering" units="ms">
-  <owner>tdanderson@chromium.org</owner>
   <owner>flackr@chromium.org</owner>
   <summary>
     The amount of time spent in overview mode when text filtering is used. The
@@ -11958,6 +11976,16 @@
   </summary>
 </histogram>
 
+<histogram name="Bluetooth.Availability" enum="BluetoothAvailability"
+    expires_after="2019-12-31">
+  <owner>kenrb@chromium.org</owner>
+  <owner>kpaulhamus@chromium.org</owner>
+  <summary>
+    Determines the availability and capabilities of the Bluetooth driver. This
+    metric is logged on startup.
+  </summary>
+</histogram>
+
 <histogram name="Bluetooth.ConnectedDeviceCount" units="devices">
   <owner>adlr@chromium.org</owner>
   <summary>
@@ -28105,7 +28133,6 @@
 
 <histogram name="Event.DownEventCount.PerInputFormFactorDestinationCombination"
     enum="DownEventInputFormFactorDestinationCombination">
-  <owner>xiaoyinh@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
   <summary>
     The number of down events received per input, form factor, and destination
@@ -31592,7 +31619,8 @@
 </histogram>
 
 <histogram name="ExtensionContentHashFetcher.CreateHashesTime" units="ms">
-  <owner>asargent@chromium.org</owner>
+  <owner>lazyboy@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The time taken to create the computed_hashes.json file for an extension.
     This happens once for each extension after we get signed values of the
@@ -31603,7 +31631,8 @@
 </histogram>
 
 <histogram name="ExtensionContentHashReader.InitLatency" units="ms">
-  <owner>asargent@chromium.org</owner>
+  <owner>lazyboy@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The time taken to initialize the ContentHashReader for an extension resource
     load. (The work done is to read in the verified contents and computed hashes
@@ -32292,6 +32321,7 @@
     units="ms" expires_after="2019-11-30">
   <owner>lazyboy@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     How long a successful initialization of computed_hashes.json file takes.
     Recorded when content verification needs to retrieve block hashes.
@@ -32302,6 +32332,7 @@
     enum="BooleanSuccess" expires_after="2019-11-30">
   <owner>lazyboy@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether or not computed_hashes.json file read and parse succeeded. Recorded
     when content verification needs to retrieve block hashes.
@@ -32312,6 +32343,7 @@
     enum="BooleanSuccess" expires_after="2019-11-30">
   <owner>lazyboy@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether or not fetching verified_contents.json succeeded. Recorded when the
     file wasn't available locally and we needed to fetch it from network.
@@ -32333,6 +32365,7 @@
     enum="BooleanSuccess" expires_after="2019-11-30">
   <owner>lazyboy@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether or not initializing verified_contents.json succeeded. Recorded
     during an extension load completion or during an on demand content
@@ -32344,6 +32377,7 @@
     units="ms" expires_after="2019-11-30">
   <owner>lazyboy@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     How long a successful initialization of verified_contents.json file takes.
     Recorded if Extensions.ContentVerification.VerifiedContentsInitResult = true
@@ -32361,7 +32395,8 @@
 </histogram>
 
 <histogram name="Extensions.CorruptExtensionBecameDisabled">
-  <owner>asargent@chromium.org</owner>
+  <owner>lazyboy@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Fired each time an extension was detected to be corrupted (contents not
     matching an expected content hash from the webstore) and was disabled.
@@ -32370,7 +32405,9 @@
 
 <histogram name="Extensions.CorruptExtensionDisabledReason"
     enum="CorruptExtensionDisabledReason">
+  <owner>lazyboy@chromium.org</owner>
   <owner>rockot@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The reason why an extension was detected to be corrupted. Recorded each time
     an extension is disabled due to corruption detection.
@@ -32378,7 +32415,8 @@
 </histogram>
 
 <histogram name="Extensions.CorruptExtensionTotalDisables">
-  <owner>asargent@chromium.org</owner>
+  <owner>lazyboy@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Logged once at startup, this is the value of a counter that is incremented
     anytime we disable a corrupted extension because its content didn't match an
@@ -32387,7 +32425,8 @@
 </histogram>
 
 <histogram name="Extensions.CorruptExtensionWouldBeDisabled">
-  <owner>asargent@chromium.org</owner>
+  <owner>lazyboy@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Simiar to Extensions.CorruptExtensionBecameDisabled, but fires when we're in
     a bootstrapping mode and would have disabled an extension.
@@ -32409,8 +32448,9 @@
 
 <histogram name="Extensions.CorruptPolicyExtensionDetected2" enum="BooleanHit"
     expires_after="2019-05-31">
-  <owner>extensions-core@chromium.org</owner>
+  <owner>lazyboy@chromium.org</owner>
   <owner>poromov@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Fires when we detect corruption in an enterprise policy forced install
     extension and begin the process of reinstalling it. Compare to
@@ -32420,7 +32460,8 @@
 
 <histogram name="Extensions.CorruptPolicyExtensionResolved" units="ms"
     expires_after="2019-05-31">
-  <owner>asargent@chromium.org</owner>
+  <owner>lazyboy@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Fires when we've successfully resinstalled a corrupt enterprise policy
     force-installed extension, with a value indicating how long it took
@@ -32445,12 +32486,14 @@
 </histogram>
 
 <histogram name="Extensions.CrxFetchError" enum="NetErrorCodes">
-  <owner>asargent@chromium.org</owner>
+  <owner>waffles@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>Net error results from URLFetcher.</summary>
 </histogram>
 
 <histogram name="Extensions.CrxFetchFailureRetryCountGoogleUrl">
-  <owner>asargent@chromium.org</owner>
+  <owner>waffles@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Number of times chrome retried to download an extension with a url on a
     google.com domain, before eventually giving up.
@@ -32459,6 +32502,7 @@
 
 <histogram name="Extensions.CrxFetchFailureRetryCountOtherUrl"
     expires_after="2018-08-30">
+  <owner>waffles@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -32468,7 +32512,8 @@
 </histogram>
 
 <histogram name="Extensions.CrxFetchSuccessRetryCountGoogleUrl">
-  <owner>asargent@chromium.org</owner>
+  <owner>waffles@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Number of times chrome retried to download an extension with a url on a
     google.com domain, before eventually succeeding.
@@ -32477,6 +32522,7 @@
 
 <histogram name="Extensions.CrxFetchSuccessRetryCountOtherUrl"
     expires_after="2018-08-30">
+  <owner>waffles@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -32486,6 +32532,7 @@
 </histogram>
 
 <histogram name="Extensions.CrxInstallDirPathLength" expires_after="2018-08-30">
+  <owner>waffles@chromium.org</owner>
   <owner>rdevlin.cronin@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -32710,7 +32757,7 @@
 </histogram>
 
 <histogram name="Extensions.DeclarativeSetIconWasVisible" enum="BooleanVisible"
-    expires_after="2019-02-01">
+    expires_after="2019-09-07">
   <owner>dbertoni@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -32934,7 +32981,7 @@
 </histogram>
 
 <histogram name="Extensions.DisableReason" enum="ExtensionDisableReason">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The count of disabled extensions at startup grouped by disble reason from
     disable_reason::DisableReason. When an extension is disabled, it can be for
@@ -32954,7 +33001,7 @@
 </histogram>
 
 <histogram name="Extensions.DynamicExtensionActionIconWasVisible"
-    enum="BooleanVisible" expires_after="2019-02-01">
+    enum="BooleanVisible" expires_after="2019-09-07">
   <owner>dbertoni@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -32964,7 +33011,7 @@
 </histogram>
 
 <histogram name="Extensions.DynamicExtensionActionIconWasVisibleRendered"
-    enum="BooleanVisible" expires_after="2019-08-21">
+    enum="BooleanVisible" expires_after="2019-09-07">
   <owner>dbertoni@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -32994,7 +33041,7 @@
 </histogram>
 
 <histogram name="Extensions.ErrorCodeFromCrxOpen">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     If opening the CRX file for unpacking fails, this integer is the error code
     given by the OS.
@@ -33552,7 +33599,7 @@
 <histogram name="Extensions.HasPermissions_AutoDisable3" enum="Boolean"
     expires_after="2018-08-30">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when it is
     automatically disabled due to a permission increase (e.g., after an
@@ -33566,7 +33613,7 @@
 
 <histogram name="Extensions.HasPermissions_Install3" enum="Boolean">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when it was
     installed. To find places where this histogram may be emitted, look for
@@ -33577,7 +33624,7 @@
 
 <histogram name="Extensions.HasPermissions_InstallAbort3" enum="Boolean">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when installation
     was aborted (e.g. because the parent window of the confirmation dialog went
@@ -33591,7 +33638,7 @@
 <histogram name="Extensions.HasPermissions_InstallCancel3" enum="Boolean"
     expires_after="2018-08-30">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when installation
     was canceled. To find places where this histogram may be emitted, look for
@@ -33602,7 +33649,7 @@
 
 <histogram name="Extensions.HasPermissions_Load3" enum="Boolean">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when it was
     loaded (which happens at profile open or extension install). To find places
@@ -33614,7 +33661,7 @@
 <histogram name="Extensions.HasPermissions_ReEnable3" enum="Boolean"
     expires_after="2018-08-30">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when it was
     re-enabled from a confirmation prompt. To find places where this histogram
@@ -33627,7 +33674,7 @@
 <histogram name="Extensions.HasPermissions_ReEnableAbort3" enum="Boolean"
     expires_after="2018-08-30">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when the
     re-enable prompt was aborted (e.g. because the parent window of the
@@ -33641,7 +33688,7 @@
 <histogram name="Extensions.HasPermissions_ReEnableCancel3" enum="Boolean"
     expires_after="2018-08-30">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when the
     re-enable was canceled from the confirmation prompt. To find places where
@@ -33654,7 +33701,7 @@
 <histogram name="Extensions.HasPermissions_Uninstall3" enum="Boolean"
     expires_after="2018-08-30">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when it was
     uninstalled. To find places where this histogram may be emitted, look for
@@ -33665,7 +33712,7 @@
 
 <histogram name="Extensions.HasPermissions_WebStoreInstall3" enum="Boolean">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when it was
     installed through the web store. To find places where this histogram may be
@@ -33679,7 +33726,7 @@
 <histogram name="Extensions.HasPermissions_WebStoreInstallAbort3"
     enum="Boolean">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when installation
     from the web store was aborted (e.g. because the parent window of the
@@ -33693,7 +33740,7 @@
 <histogram name="Extensions.HasPermissions_WebStoreInstallCancel3"
     enum="Boolean">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Whether there were any permissions present in an extension when installation
     from the web store was canceled. To find places where this histogram may be
@@ -34175,7 +34222,7 @@
 </histogram>
 
 <histogram name="Extensions.ManifestIconSetIconWasVisibleForPacked"
-    enum="BooleanVisible" expires_after="2019-02-01">
+    enum="BooleanVisible" expires_after="2019-09-07">
   <owner>dbertoni@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -34206,7 +34253,7 @@
 </histogram>
 
 <histogram name="Extensions.ManifestIconSetIconWasVisibleForUnpacked"
-    enum="BooleanVisible" expires_after="2019-02-01">
+    enum="BooleanVisible" expires_after="2019-09-07">
   <owner>dbertoni@chromium.org</owner>
   <owner>extensions-core@chromium.org</owner>
   <summary>
@@ -34397,7 +34444,7 @@
 <histogram name="Extensions.Permissions_AutoDisable3"
     enum="ExtensionPermission3">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when it is automatically disabled
     due to a permission increase (e.g., after an extension upgrade). To find
@@ -34432,7 +34479,7 @@
 
 <histogram name="Extensions.Permissions_Install3" enum="ExtensionPermission3">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when it was installed. To find
     places where this histogram may be emitted, look for calls to
@@ -34470,7 +34517,7 @@
 <histogram name="Extensions.Permissions_InstallAbort3"
     enum="ExtensionPermission3">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when installation was aborted (e.g.
     because the parent window of the confirmation dialog went away), not
@@ -34508,7 +34555,7 @@
 <histogram name="Extensions.Permissions_InstallCancel3"
     enum="ExtensionPermission3">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when installation was canceled. To
     find places where this histogram may be emitted, look for calls to
@@ -34537,7 +34584,7 @@
 
 <histogram name="Extensions.Permissions_Load3" enum="ExtensionPermission3">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when it was loaded (which happens at
     profile open or extension install). To find places where this histogram may
@@ -34573,7 +34620,7 @@
 <histogram name="Extensions.Permissions_ReEnable3" enum="ExtensionPermission3"
     expires_after="2018-08-30">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when it was re-enabled from a
     confirmation prompt. To find places where this histogram may be emitted,
@@ -34588,7 +34635,7 @@
     Deprecated as of 5/2014, replaced by Extensions.Permissions_ReEnableAbort2.
   </obsolete>
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when the re-enable prompt was
     aborted, not including installation errors and manual user cancels.
@@ -34601,7 +34648,7 @@
     Deprecated as of 6/2015, replaced by Extensions.Permissions_ReEnableAbort3.
   </obsolete>
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when the re-enable prompt was
     aborted, not including installation errors and manual user cancels.
@@ -34611,7 +34658,7 @@
 <histogram name="Extensions.Permissions_ReEnableAbort3"
     enum="ExtensionPermission3" expires_after="2018-08-30">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when the re-enable prompt was
     aborted (e.g. because the parent window of the confirmation dialog went
@@ -34651,7 +34698,7 @@
 <histogram name="Extensions.Permissions_ReEnableCancel3"
     enum="ExtensionPermission3" expires_after="2018-08-30">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when the re-enable was canceled from
     the confirmation prompt. To find places where this histogram may be emitted,
@@ -34684,7 +34731,7 @@
 
 <histogram name="Extensions.Permissions_Uninstall3" enum="ExtensionPermission3">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when it was uninstalled. To find
     places where this histogram may be emitted, look for calls to
@@ -34724,7 +34771,7 @@
 <histogram name="Extensions.Permissions_WebStoreInstall3"
     enum="ExtensionPermission3">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when it was installed through the
     web store. To find places where this histogram may be emitted, look for
@@ -34765,7 +34812,7 @@
 <histogram name="Extensions.Permissions_WebStoreInstallAbort3"
     enum="ExtensionPermission3">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when installation from the web store
     was aborted (e.g. because the parent window of the confirmation dialog went
@@ -34807,7 +34854,7 @@
 <histogram name="Extensions.Permissions_WebStoreInstallCancel3"
     enum="ExtensionPermission3">
   <owner>rdevlin.cronin@chromium.org</owner>
-  <owner>rpaquay@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The permissions present in an extension when installation from the web store
     was canceled. To find places where this histogram may be emitted, look for
@@ -34870,7 +34917,7 @@
 </histogram>
 
 <histogram name="Extensions.ResourceDirectoryTimestampQueryLatency" units="ms">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The initialization latency (in milliseconds) introduced to each extension
     resource request by querying the directory timestamp.
@@ -34931,7 +34978,7 @@
 </histogram>
 
 <histogram name="Extensions.SandboxUnpackFailure">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Count the number of times a sandboxed extension unpack fails.
   </summary>
@@ -34939,7 +34986,7 @@
 
 <histogram name="Extensions.SandboxUnpackFailureReason"
     enum="ExtensionUnpackFailureReason">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>What caused a sandboxed extension unpack to fail?</summary>
 </histogram>
 
@@ -34975,42 +35022,42 @@
 </histogram>
 
 <histogram name="Extensions.SandboxUnpackRate">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Rate at which a CRX file is unpacked in Kilobytes per second.
   </summary>
 </histogram>
 
 <histogram name="Extensions.SandboxUnpackRate1To2mB">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Rate at which CRX files 1MB to 2MB are unpacked in Kilobytes per second.
   </summary>
 </histogram>
 
 <histogram name="Extensions.SandboxUnpackRate2To5mB">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Rate at which CRX files 2MB to 5MB are unpacked in Kilobytes per second.
   </summary>
 </histogram>
 
 <histogram name="Extensions.SandboxUnpackRate50kBTo1mB">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Rate at which CRX files 50kB to 1MB are unpacked in Kilobytes per second.
   </summary>
 </histogram>
 
 <histogram name="Extensions.SandboxUnpackRate5To10mB">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Rate at which CRX files 5MB to 10 MB are unpacked in Kilobytes per second.
   </summary>
 </histogram>
 
 <histogram name="Extensions.SandboxUnpackRateOver10mB">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Rate at which CRX files larger than 10MB are unpacked in Kilobytes per
     second.
@@ -35018,7 +35065,7 @@
 </histogram>
 
 <histogram name="Extensions.SandboxUnpackRateUnder50kB">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Rate at which CRX files under 50 KB are unpacked in Kilobytes per second.
   </summary>
@@ -35041,7 +35088,7 @@
 </histogram>
 
 <histogram name="Extensions.SandboxUnpackSuccessCrxSize">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>Size of the .crx file, in KB, when the unpack succeeds.</summary>
 </histogram>
 
@@ -35271,7 +35318,7 @@
 
 <histogram name="Extensions.UnpackFailureInstallCause"
     enum="ExtensionInstallCause">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Count failing CRX installs, grouped by the way an extension can be
     installed.
@@ -35280,7 +35327,7 @@
 
 <histogram name="Extensions.UnpackFailureInstallSource"
     enum="ExtensionLocation">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Count successful CRX installs, grouped by the location property in prefs.
     installed.
@@ -35289,7 +35336,7 @@
 
 <histogram name="Extensions.UnpackSuccessInstallCause"
     enum="ExtensionInstallCause">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Count successful CRX installs, grouped by the cause of the install.
   </summary>
@@ -35297,7 +35344,7 @@
 
 <histogram name="Extensions.UnpackSuccessInstallSource"
     enum="ExtensionLocation">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     Count successful CRX installs, grouped by the location property in prefs.
   </summary>
@@ -35863,7 +35910,7 @@
 </histogram>
 
 <histogram name="ExtensionUrlRequest.OnReadCompleteError" enum="NetErrorCodes">
-  <owner>asargent@chromium.org</owner>
+  <owner>extensions-core@chromium.org</owner>
   <summary>
     The error code for failures of incremental reads of a file stream for a
     chrome-extension:// URL. (See also ExtensionUrlRequest.OnReadCompleteResult
@@ -36955,7 +37002,7 @@
 
 <histogram name="Fingerprint.Unlock.AttemptsCountBeforeSuccess" units="count"
     expires_after="2019-08-09">
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
     Counts the number of fingerprint attempts until successful screen unlock.
@@ -36964,7 +37011,7 @@
 
 <histogram name="Fingerprint.Unlock.AuthSuccessful" enum="BooleanSuccess"
     expires_after="2019-08-09">
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
     Counts the number of times that the fingerprint match successfully vs.
@@ -36975,7 +37022,7 @@
 <histogram name="Fingerprint.Unlock.EnrolledFingerCount" units="count"
     expires_after="2019-08-14">
   <owner>norvez@chromium.org</owner>
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>Counts the number of fingers enrolled by the user.</summary>
 </histogram>
@@ -36983,7 +37030,7 @@
 <histogram name="Fingerprint.Unlock.Match.Duration.Capture" units="ms"
     expires_after="2019-08-14">
   <owner>norvez@chromium.org</owner>
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
     Measures the time it took to capture the fingerprint image in the 'match'
@@ -36994,7 +37041,7 @@
 <histogram name="Fingerprint.Unlock.Match.Duration.Matcher" units="ms"
     expires_after="2019-08-14">
   <owner>norvez@chromium.org</owner>
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
     Measures the time it took to run matcher in the 'match' case.
@@ -37004,7 +37051,7 @@
 <histogram name="Fingerprint.Unlock.Match.Duration.Overall" units="ms"
     expires_after="2019-08-14">
   <owner>norvez@chromium.org</owner>
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
     Measures the time it took between the detection of a finger and the 'match'
@@ -37015,7 +37062,7 @@
 <histogram name="Fingerprint.Unlock.NoMatch.Duration.Capture" units="ms"
     expires_after="2019-08-14">
   <owner>norvez@chromium.org</owner>
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
     Measures the time it took to capture the fingerprint image in the 'no-match'
@@ -37026,7 +37073,7 @@
 <histogram name="Fingerprint.Unlock.NoMatch.Duration.Matcher" units="ms"
     expires_after="2019-08-14">
   <owner>norvez@chromium.org</owner>
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
     Measures the time it took to run the matcher in the 'no-match' case.
@@ -37036,7 +37083,7 @@
 <histogram name="Fingerprint.Unlock.NoMatch.Duration.Overall" units="ms"
     expires_after="2019-08-14">
   <owner>norvez@chromium.org</owner>
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
     Measures the time it took between the detection of a finger and the
@@ -37046,7 +37093,7 @@
 
 <histogram name="Fingerprint.UnlockEnabled" enum="BooleanEnabled"
     expires_after="2019-08-09">
-  <owner>yuluwu@chromium.org</owner>
+  <owner>yulunwu@chromium.org</owner>
   <owner>jessejames@chromium.org</owner>
   <summary>
     Track whether fingerprint is enabled to unlock the screen, when the user
@@ -39008,7 +39055,6 @@
     enum="GpuDriverBugWorkaroundEntry" expires_after="never">
 <!-- expires-never: For monitoring new driver bugs. -->
 
-  <owner>jbauman@chromium.org</owner>
   <owner>vmiura@chromium.org</owner>
   <owner>kbr@chromium.org</owner>
   <owner>zmo@chromium.org</owner>
@@ -50488,6 +50534,44 @@
   </summary>
 </histogram>
 
+<histogram name="Media.WebMediaPlayerImpl.HLS.IsCorsCrossOrigin" enum="Boolean"
+    expires_after="2019-12-31">
+  <owner>sandersd@chromium.org</owner>
+  <owner>tguilbert@chromium.org</owner>
+  <summary>
+    When an HLS manifest is found during loading (on Android only), records
+    whether the request was CORS cross-origin. These are cases that could not be
+    implemented using fetch(). Note: subresources referenced by the manifest are
+    not considered; they may have different origins or CORS configurations.
+  </summary>
+</histogram>
+
+<histogram name="Media.WebMediaPlayerImpl.HLS.IsMixedContent" enum="Boolean"
+    expires_after="2019-12-31">
+  <owner>sandersd@chromium.org</owner>
+  <owner>tguilbert@chromium.org</owner>
+  <summary>
+    When an HLS manifest is found during loading (on Android only), records
+    whether the request would be mixed content. These are cases that could not
+    be implemented using fetch(). Note: subresources referenced by the manifest
+    are not considered; they may have different origins.
+  </summary>
+</histogram>
+
+<histogram name="Media.WebMediaPlayerImpl.HLS.WouldTaintOrigin" enum="Boolean"
+    expires_after="2019-12-31">
+  <owner>sandersd@chromium.org</owner>
+  <owner>tguilbert@chromium.org</owner>
+  <summary>
+    When an HLS manifest is found during loading (on Android only), records
+    whether the request was CORS cross-origin or redirected between origins.
+    These are cases that may or may not be able to be implemented using fetch(),
+    depending on details of the redirection and the setting of the crossorigin
+    attribute. Note: subresources referenced by the manifest are not considered;
+    they may have different origins or CORS configurations.
+  </summary>
+</histogram>
+
 <histogram base="true" name="Media.WebMediaPlayerImpl.Memory" units="KB">
   <owner>wolenetz@chromium.org</owner>
   <summary>
@@ -51384,8 +51468,7 @@
 
 <histogram name="Memory.Experimental.Debug.FailedProcessDumpsPerGlobalDump"
     units="failures">
-  <owner>hjd@chromium.org</owner>
-  <owner>primiano@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <summary>
     Records how many failed process dumps there were for a given global dump
@@ -51396,8 +51479,7 @@
 </histogram>
 
 <histogram name="Memory.Experimental.Debug.GlobalDumpDuration" units="ms">
-  <owner>hjd@chromium.org</owner>
-  <owner>primiano@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <summary>
     Records how long it took the memory instrumentation service
@@ -51410,8 +51492,7 @@
 
 <histogram name="Memory.Experimental.Debug.GlobalDumpQueueLength"
     units="requests">
-  <owner>hjd@chromium.org</owner>
-  <owner>primiano@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
   <owner>erikchen@chromium.org</owner>
   <summary>
     Number of global memory dump requests queued in by the memory
@@ -52205,7 +52286,7 @@
 <!-- Name completed by histogram_suffixes name="MemoryFDsBroswerGpuAndRendererProcess" and name="MemoryFDsAllProcesses" -->
 
   <owner>dcastagna@chromium.org</owner>
-  <owner>primiano@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
   <summary>
     The total number of open file descriptors opened per process. Recorded once
     per UMA ping.
@@ -52216,7 +52297,7 @@
 <!-- Name completed by histogram_suffixes name="MemoryFDsBroswerGpuAndRendererProcess" -->
 
   <owner>dcastagna@chromium.org</owner>
-  <owner>primiano@chromium.org</owner>
+  <owner>ssid@chromium.org</owner>
   <summary>
     The limit of open file descriptors that can be opened per process. Recorded
     once per UMA ping.
@@ -53869,6 +53950,8 @@
 
 <histogram name="MobileOmnibox.PopupOpenDuration" units="ms">
   <owner>stkhapugin@chromium.org</owner>
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Recorded when the omnibox popup is closed. Indicates the duration it was
     open.
@@ -53876,7 +53959,7 @@
 </histogram>
 
 <histogram name="MobileOmnibox.PressedClipboardSuggestionAge" units="ms">
-  <owner>jif@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
   <summary>
     When a user presses an omnibox suggestion based on the content of the
@@ -55013,6 +55096,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     When the browser started, what happened with the NaCl helper process?
   </summary>
@@ -55022,6 +55106,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     When a NaCl application process was created, what had happened with the NaCl
     helper process when the browser was started?
@@ -55031,6 +55116,7 @@
 <histogram name="NaCl.Client.OSArch" enum="NaClOSArchEnum">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>The OS/Architecture of a nexe that was loaded.</summary>
 </histogram>
 
@@ -55038,6 +55124,7 @@
     enum="NaClHttpStatusCodeClass" expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The status code returned when trying to load a manifest inside an installed
     app.
@@ -55048,6 +55135,7 @@
     enum="NaClHttpStatusCodeClass">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The status code returned when trying to load a manifest from a source other
     than an installed app.
@@ -55058,6 +55146,7 @@
     enum="NaClHttpStatusCodeClass">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The status code returned when trying to load a NaCl executable inside an
     installed app.
@@ -55068,6 +55157,7 @@
     enum="NaClHttpStatusCodeClass" expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The status code returned when trying to load a NaCl executable from a source
     other than an installed app.
@@ -55077,6 +55167,7 @@
 <histogram name="NaCl.LoadStatus.Plugin" enum="NaClPluginErrorCode">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>The error code returned by NaCl's Chrome plugin.</summary>
 </histogram>
 
@@ -55084,6 +55175,7 @@
     enum="NaClPluginErrorCode">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The error code returned by NaCl's Chrome plugin, but only for installed
     apps.
@@ -55094,6 +55186,7 @@
     enum="NaClPluginErrorCode">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The error code returned by NaCl's Chrome plugin, but excluding installed
     apps.
@@ -55103,6 +55196,7 @@
 <histogram name="NaCl.LoadStatus.SelLdr" enum="NaClSelLdrErrorCode">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>The error code returned by NaCl's sel_ldr.</summary>
 </histogram>
 
@@ -55110,6 +55204,7 @@
     enum="NaClSelLdrErrorCode">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The error code returned by NaCl's sel_ldr, but only for installed apps.
   </summary>
@@ -55119,6 +55214,7 @@
     enum="NaClSelLdrErrorCode" expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The error code returned by NaCl's sel_ldr, but excluding installed apps.
   </summary>
@@ -55128,6 +55224,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     Was the manifest specified as a data URI rather than a .nmf file?
   </summary>
@@ -55146,6 +55243,7 @@
 <histogram name="NaCl.ModuleUptime.Crash" units="ms" expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>The time a NaCl module ran before it crashed.</summary>
 </histogram>
 
@@ -55153,6 +55251,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>The time a NaCl module ran without crashing, at shutdown.</summary>
 </histogram>
 
@@ -55203,6 +55302,7 @@
 <histogram name="NaCl.Options.PNaCl.OptLevel" enum="PNaClOptionsOptLevelEnum">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The optimization level set for the initial Portable Native Client
     translation from bitcode to native code.
@@ -55220,6 +55320,7 @@
 <histogram name="NaCl.Perf.PNaClCache.IsHit" enum="PNaClTranslationCacheEnum">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     Did the Portable Native Client translation cache find an executable
     translated from bitcode?
@@ -55229,6 +55330,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.CompileKBPerSec" units="KB/s">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The rate for compiling a Portable Native Client bitcode file to an object
     file in Kilobytes per second.
@@ -55238,6 +55340,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.CompileTime" units="ms">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took to compile a Portable Native Client bitcode file to an
     object file.
@@ -55248,6 +55351,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took to link a Portable Native Client generated object file into
     a Native Client executable.
@@ -55257,6 +55361,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.LoadCompiler" units="ms">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took to load and validate the Portable Native Client compiler.
   </summary>
@@ -55266,6 +55371,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took to load and validate the Portable Native Client linker.
   </summary>
@@ -55275,6 +55381,7 @@
     units="%">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The percentage of a Portable Native Client application that is compiled by
     the time the application is fully downloaded (compile and download happen in
@@ -55285,6 +55392,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec" units="KB/s">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The rate for completely translating a Portable Native Client bitcode file
     into a Native Client executable and caching the result in Kilobytes per
@@ -55295,6 +55403,7 @@
 <histogram name="NaCl.Perf.PNaClLoadTime.TotalUncachedTime" units="ms">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The total time it took to completely translate a Portable Native Client
     bitcode file into a Native Client executable, and cache the result.
@@ -55305,18 +55414,21 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>The time it took the NaCl module to shut down.</summary>
 </histogram>
 
 <histogram name="NaCl.Perf.Size.Manifest" units="KB" expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>The size of the manifest file.</summary>
 </histogram>
 
 <histogram name="NaCl.Perf.Size.Nexe" units="KB">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The size of the main .nexe file downloaded for a Native Client module.
   </summary>
@@ -55325,6 +55437,7 @@
 <histogram name="NaCl.Perf.Size.Pexe" units="KB" expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The size of the main .pexe bitcode file downloaded for a Portable Native
     Client module.
@@ -55334,6 +55447,7 @@
 <histogram name="NaCl.Perf.Size.PexeNexeSizePct" units="%">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The size of the main .pexe bitcode file divided by the size of the .nexe
     that is the result of translating the bitcode file, times 100.
@@ -55343,6 +55457,7 @@
 <histogram name="NaCl.Perf.Size.PNaClTranslatedNexe" units="KB">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The size of the main .nexe file that is the result of translating a Portable
     Native Client .pexe bitcode file. This reflects the amount of cache
@@ -55354,6 +55469,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>The time it took to load the NaCl module into sel_ldr.</summary>
 </histogram>
 
@@ -55361,6 +55477,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took to load the NaCl module into sel_ldr. Normalized by the
     size of the .nexe, in megabytes.
@@ -55371,6 +55488,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took to download the manifset file for a Native Client module.
   </summary>
@@ -55380,6 +55498,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took between the Native Client plugin initialization and when
     proxied execution of the NaCl module begins. This is the general startup
@@ -55391,6 +55510,7 @@
     units="milliseconds/MB">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took between the Native Client plugin initialization and when
     proxied execution of the NaCl module begins. This is the general startup
@@ -55403,6 +55523,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took to download the main .nexe for a Native Client module.
   </summary>
@@ -55412,6 +55533,7 @@
     units="milliseconds/MB">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took to download the main .nexe for a Native Client module.
     Normalized by the size of the .nexe, in megabytes.
@@ -55421,6 +55543,7 @@
 <histogram name="NaCl.Perf.StartupTime.Total" units="ms">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took between the Native Client plugin initialization and when
     the NaCl module is ready to be used.
@@ -55431,6 +55554,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     The time it took between the Native Client plugin initialization and when
     the NaCl module is ready to be used. Normalized by the size of the .nexe, in
@@ -55453,6 +55577,7 @@
 <histogram name="NaCl.ValidationCache.Query" enum="NaClValidationCacheEnum">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     Did a validation cache query find a previously known validation result?
   </summary>
@@ -55462,6 +55587,7 @@
     expires_after="2019-12-31">
   <owner>dschuff@chromium.org</owner>
   <owner>adamk@chromium.org</owner>
+  <owner>bbudge@chromium.org</owner>
   <summary>
     Was the validation cache updated with a new validation result?
   </summary>
@@ -57577,7 +57703,8 @@
 
 <histogram name="Net.CertificateTransparency.CanInclusionCheckSCT"
     enum="SCTCanBeChecked">
-  <owner>eranm@chromium.org</owner>
+  <owner>estark@chromium.org</owner>
+  <owner>rsleevi@chromium.org</owner>
   <summary>
     Whether an observed Signed Certificate Timestamp (SCT) can be checked for
     inclusion. An SCT can be checked for inclusion if the client has a valid
@@ -57831,7 +57958,8 @@
 
 <histogram name="Net.CertificateTransparency.InclusionCheckResult"
     enum="CTLogEntryInclusionCheckResult">
-  <owner>eranm@chromium.org</owner>
+  <owner>estark@chromium.org</owner>
+  <owner>rsleevi@chromium.org</owner>
   <summary>
     The result of an inclusion check for a Certificate Transparency log entry
     (composed of a TLS certificate observed together with Signed Certificate
@@ -57853,7 +57981,8 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.PilotSTHAge" units="ms">
-  <owner>eranm@chromium.org</owner>
+  <owner>estark@chromium.org</owner>
+  <owner>rsleevi@chromium.org</owner>
   <summary>
     Age of Pilot's Signed Tree Head, as observed by the client, in minutes.
     Measuring the age of a particular log's Signed Tree Head will allow more
@@ -57902,7 +58031,8 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.SCTOrigin" enum="SCTOrigin">
-  <owner>eranm@chromium.org</owner>
+  <owner>estark@chromium.org</owner>
+  <owner>rsleevi@chromium.org</owner>
   <summary>
     The origin breakdown of Signed Certificate Timestamps (SCTs). Emitted once
     for every SCT when first validated, which means 0 or more times during every
@@ -57924,7 +58054,8 @@
 </histogram>
 
 <histogram name="Net.CertificateTransparency.SCTStatus" enum="SCTVerifyStatus">
-  <owner>eranm@chromium.org</owner>
+  <owner>estark@chromium.org</owner>
+  <owner>rsleevi@chromium.org</owner>
   <summary>
     Breakdown of Signed Certificate Timestamps (SCTs) validation status. Emitted
     once for every SCT when first validated, which means 0 or more times during
@@ -69241,8 +69372,9 @@
 
 <histogram name="NewTabPage.ContentSuggestions.Preferences.RemoteSuggestions"
     enum="BooleanEnabled">
-  <owner>dgn@chromium.org</owner>
+  <owner>fgorski@chromium.org</owner>
   <owner>finkm@chromium.org</owner>
+  <owner>google-on-content-chrome@google.com</owner>
   <summary>
     Whether content suggestions from the remote service are enabled. It is
     recored at startup. Note: This histogram is not specific to the New Tab
@@ -74337,6 +74469,7 @@
 </histogram>
 
 <histogram name="Omnibox.AnswerParseSuccess" enum="BooleanSuccess">
+  <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <summary>
     For each answer received in suggest responses, the number that are
@@ -74345,6 +74478,7 @@
 </histogram>
 
 <histogram name="Omnibox.AnswerParseType" enum="SuggestionAnswerType">
+  <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <summary>
     The number of times each omnibox suggestion answer type (e.g., weather,
@@ -74363,6 +74497,7 @@
 
   <owner>asvitkine@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Records the time taken between a keystroke being typed in the omnibox and
     the text being painted. If there are multiple keystrokes before a paint,
@@ -74397,6 +74532,7 @@
   </obsolete>
   <owner>asvitkine@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     A refinement of the Omnibox.CharTypedToRepaintLatency metric that attempts
     to more accurately track the latency by measuring until the point when the
@@ -74411,6 +74547,7 @@
 <histogram name="Omnibox.CharTypedToRepaintLatency.ToPaint" units="ms">
   <owner>asvitkine@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Records the time taken between a keystroke being typed in the omnibox and
     the time when we're ready to paint the omnibox. This is a breakdown
@@ -74421,6 +74558,7 @@
 
 <histogram name="Omnibox.ClipboardSuggestionShownAge" units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Recorded every time the omnibox is focussed and a recent URL from the user's
     clipboard is suggested. The value indicates the estimated age of the
@@ -74437,6 +74575,7 @@
 
 <histogram name="Omnibox.ClipboardSuggestionShownNumTimes">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Recorded every time the omnibox is focussed and a recent URL from the user's
     clipboard is suggested. The number emitted is the number of times the URL
@@ -74465,6 +74604,7 @@
 <histogram name="Omnibox.ClipboardSuggestionShownWithCurrentURL"
     enum="BooleanPresent">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Recorded every time the omnibox is focussed and a recent URL from the user's
     clipboard is suggested. The value indicates whether the current URL was
@@ -74488,6 +74628,7 @@
 
 <histogram name="Omnibox.CutOrCopyAllText" units="count">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The number of cut or copy commands on all selected text in the omnibox.
     Gathered on desktop platforms (Win, Mac, Linux, Chrome OS).
@@ -74496,6 +74637,8 @@
 
 <histogram name="Omnibox.DocumentSuggest.Requests"
     enum="OmniboxDocumentSuggestRequests">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>skare@chromium.org</owner>
   <summary>
     Counts the number of document suggest requests the omnibox sent, were
@@ -74505,6 +74648,8 @@
 
 <histogram name="Omnibox.DocumentSuggest.ResultCount" units="count"
     expires_after="M74">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>skare@chromium.org</owner>
   <summary>
     Number of results returned in each document suggestion reply. Logged for
@@ -74514,6 +74659,8 @@
 
 <histogram name="Omnibox.EditUrlSuggestionAction"
     enum="OmniboxEditUrlSuggestionAction">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>mdjones@chromium.org</owner>
   <summary>
     The action performed on the edit-URL omnibox suggestion that contains the
@@ -74536,6 +74683,7 @@
 
 <histogram name="Omnibox.EnteredKeywordMode2" enum="OmniboxEnteredKeywordMode2">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The new enumeration of how many times users enter keyword hint mode
     &quot;Search ___ for:&quot; and how. Note that we don't recognize the
@@ -74596,6 +74744,7 @@
 
 <histogram name="Omnibox.FocusToOpenTimeAnyPopupState3" units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The length of time between when a user focused on the omnibox and opened an
     omnibox match (which could be what they typed or a suggestion). This is
@@ -74609,6 +74758,8 @@
 </histogram>
 
 <histogram name="Omnibox.HardwareKeyboardModeEnabled" enum="BooleanEnabled">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>pkl@chromium.org</owner>
   <summary>
     iOS: Records whether a hardware keyboard is used to input text. This is
@@ -74632,6 +74783,7 @@
 
 <histogram name="Omnibox.HistoryQuickHistoryIDSetFromWords" units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Times URLIndexPrivateData::HistoryIDSetFromWords(), which is called by the
     omnibox's HistoryQuick provider.
@@ -74643,6 +74795,7 @@
 
 <histogram name="Omnibox.InputType" enum="OmniboxInputType">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The kind of input the user provided when using the omnibox to go somewhere.
     The type can be misleading. For example if the user typed 'http:/', it gets
@@ -74653,6 +74806,7 @@
 
 <histogram name="Omnibox.IsPasteAndGo" enum="Boolean">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Whether an omnibox interaction is a paste-and-search/paste-and-go action.
     (This histogram records both of these in the &quot;True&quot; bucket for
@@ -74664,6 +74818,7 @@
 
 <histogram name="Omnibox.IsPopupOpen" enum="Boolean">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Whether the omnibox popup (a.k.a. dropdown) is open at the time the user
     used the omnibox to go somewhere. It can be closed if, for instance, the
@@ -74676,6 +74831,7 @@
 
 <histogram name="Omnibox.JustDeletedText" enum="Boolean">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Whether the user deleted text immediately before selecting an omnibox
     suggestion. This is usually the result of pressing backspace or delete.
@@ -74684,11 +74840,13 @@
 
 <histogram name="Omnibox.NumEvents" enum="UsedOmnibox">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>The number of times users used the omnibox to go somewhere.</summary>
 </histogram>
 
 <histogram name="Omnibox.NumTypedTerms" units="terms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The number of terms in the text the user entered in the omnibox when they
     used the omnibox to go somewhere. Terms are defined by splitting on
@@ -74698,6 +74856,7 @@
 
 <histogram name="Omnibox.PageContext" enum="OmniboxPageContext">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     What the user was viewing when the user used the omnibox to go somewhere.
   </summary>
@@ -74706,6 +74865,7 @@
 <histogram name="Omnibox.PaintTime" units="ms">
   <owner>asvitkine@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Records the time to paint the omnibox contents. This is a subcomponent of
     Omnibox.CharTypedToRepaintLatency. Implemented on desktop platforms.
@@ -74714,6 +74874,7 @@
 
 <histogram name="Omnibox.Paste" units="count">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The number of paste commands on the text in the omnibox. Reported every time
     a paste command is done.
@@ -74722,6 +74883,7 @@
 
 <histogram name="Omnibox.PasteAndGo" units="count" expires_after="2019-07-30">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The number of paste-and-go commands on the text in the omnibox. Reported
     every time a paste-and-go command is done.
@@ -74797,6 +74959,7 @@
 
 <histogram name="Omnibox.ProviderTime2" units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The length of time taken by the named provider&quot;s synchronous pass.
   </summary>
@@ -74813,6 +74976,8 @@
 </histogram>
 
 <histogram name="Omnibox.QueryGeolocationAcquisitionTime" units="ms">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>stkhapugin@chromium.org</owner>
   <summary>
     The elapsed time to acquire the location sent in the X-Geo header for an
@@ -74821,6 +74986,8 @@
 </histogram>
 
 <histogram name="Omnibox.QueryGeolocationHorizontalAccuracy" units="meters">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>stkhapugin@chromium.org</owner>
   <summary>
     The estimated horizontal accuracy of the location sent in the X-Geo header
@@ -74830,6 +74997,8 @@
 
 <histogram name="Omnibox.QueryIosLocationAuthorizationStatus"
     enum="IosLocationAuthorizationStatus">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>kiyun@google.com</owner>
   <summary>
     For iOS, whether the application is authorized to use location services when
@@ -74851,6 +75020,7 @@
 
 <histogram name="Omnibox.QueryTime2" units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Time it takes for the omnibox to become responsive to user input after the
     user has typed N characters. This measures the time it takes to start all
@@ -74861,6 +75031,7 @@
 <histogram name="Omnibox.SaveStateForTabSwitch.UserInputInProgress"
     units="count">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     When a user switches tabs, whether the omnibox had an edit in progress.
   </summary>
@@ -74880,6 +75051,7 @@
 
 <histogram name="Omnibox.SearchEngineType" enum="OmniboxSearchEngineType">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The type of search engine associated with a match opened from the omnibox.
   </summary>
@@ -74887,6 +75059,7 @@
 
 <histogram name="Omnibox.SearchProvider.AddHistoryResultsTime" units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Time it takes to add all the raw history results to the list of matches.
   </summary>
@@ -74927,6 +75100,7 @@
 
 <histogram name="Omnibox.SelectedPosition" units="position">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The index of the item that the user selected in the omnibox popup (a.k.a.
     dropdown) list. 0 means the inline suggestion shown within the omnibox. This
@@ -74994,6 +75168,7 @@
 
 <histogram name="Omnibox.SuggestionUsed.OfferedTabMatch" enum="BooleanOffered">
   <owner>krb@chromium.org</owner>
+  <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures whether the suggestion that was selected by the user offered a tab
@@ -75010,6 +75185,7 @@
 
 <histogram name="Omnibox.SuggestionUsed.Provider" enum="OmniboxProviderType">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The provider of the suggestion the user selected when the user used the
     omnibox to go somewhere.
@@ -75019,6 +75195,7 @@
 <histogram base="true" name="Omnibox.SuggestionUsed.Provider.ByPageContext"
     enum="OmniboxProviderType">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The provider of the suggestion the user selected when the user used the
     omnibox to go somewhere, only for omnibox interactions that start in the
@@ -75029,6 +75206,7 @@
 <histogram name="Omnibox.SuggestionUsed.ProviderAndResultType"
     enum="OmniboxProviderAndResultType">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The provider and result type of the suggestion the user selected when the
     user used the omnibox to go somewhere.
@@ -75039,6 +75217,7 @@
     name="Omnibox.SuggestionUsed.ProviderAndResultType.ByPageContext"
     enum="OmniboxProviderAndResultType">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The provider and result type of the suggestion the user selected when the
     user used the omnibox to go somewhere, only for omnibox interactions that
@@ -75050,6 +75229,7 @@
     name="Omnibox.SuggestionUsed.Search.Experimental.ForegroundToFirstMeaningfulPaint.Prerender"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from the page first appearing in the foreground to its
     first meaningful paint. Only recorded on navigations that use a prerender
@@ -75061,6 +75241,7 @@
     name="Omnibox.SuggestionUsed.Search.Experimental.NavigationToFirstMeaningfulPaint"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from navigation start to first meaningful paint. Only
     recorded for a search query suggestion selected from the omnibox.
@@ -75071,6 +75252,7 @@
     name="Omnibox.SuggestionUsed.Search.ForegroundToFirstContentfulPaint.Prerender"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from the page first appearing in the foreground to its
     first contentful paint. Only recorded on navigations that use a prerender
@@ -75082,6 +75264,7 @@
     name="Omnibox.SuggestionUsed.Search.NavigationToFirstContentfulPaint"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from navigation start to first contentful paint. Only
     recorded for a search query suggestion selected from the omnibox.
@@ -75092,6 +75275,7 @@
     name="Omnibox.SuggestionUsed.Search.NavigationToFirstForeground.Prerender"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from a page being navigated to in prerender to it first
     showing up in foreground. Only recorded on navigations that used a prerender
@@ -75102,7 +75286,8 @@
 
 <histogram name="Omnibox.SuggestionUsed.SearchVsUrl"
     enum="OmniboxSummarizedResultType">
-  <owner>mpearson@google.com</owner>
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The rough type of the suggestion the user selected when the user used the
     omnibox to go somewhere.
@@ -75111,7 +75296,8 @@
 
 <histogram base="true" name="Omnibox.SuggestionUsed.SearchVsUrl.ByPageContext"
     enum="OmniboxSummarizedResultType">
-  <owner>mpearson@google.com</owner>
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The rough type of the suggestion the user selected when the user used the
     omnibox to go somewhere, only for omnibox interactions that start in the
@@ -75122,6 +75308,7 @@
 <histogram name="Omnibox.SuggestionUsed.SelectedTabMatch"
     enum="BooleanSelected">
   <owner>krb@chromium.org</owner>
+  <owner>mpearson@chromium.org</owner>
   <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures whether the suggestion that was selected by the user offered a tab
@@ -75146,6 +75333,7 @@
     name="Omnibox.SuggestionUsed.URL.Experimental.ForegroundToFirstMeaningfulPaint.Prerender"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from the page first appearing in the foreground to its
     first meaningful paint. Only recorded on navigations that use a prerender
@@ -75157,6 +75345,7 @@
     name="Omnibox.SuggestionUsed.URL.Experimental.NavigationToFirstMeaningfulPaint"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from navigation start to first meaningful paint. Only
     recorded for a URL suggestion selected from the omnibox.
@@ -75167,6 +75356,7 @@
     name="Omnibox.SuggestionUsed.URL.ForegroundToFirstContentfulPaint.Prerender"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from the page first appearing in the foreground to its
     first contentful paint. Only recorded on navigations that use a prerender
@@ -75177,6 +75367,7 @@
 <histogram name="Omnibox.SuggestionUsed.URL.NavigationToFirstContentfulPaint"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from navigation start to first contentful paint. Only
     recorded for a URL suggestion selected from the omnibox.
@@ -75187,6 +75378,7 @@
     name="Omnibox.SuggestionUsed.URL.NavigationToFirstForeground.Prerender"
     units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Measures the time from a page being navigated to in prerender to it first
     showing up in foreground. Only recorded on navigations that used a prerender
@@ -75197,6 +75389,7 @@
 
 <histogram name="Omnibox.SuggestRequest.Failure.GoogleResponseTime" units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The time elapsed between the sending of a suggest request to Google until
     the time the request was returned with status==failed. Ignores requests that
@@ -75206,6 +75399,7 @@
 
 <histogram name="Omnibox.SuggestRequest.Success.GoogleResponseTime" units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The time elapsed between the sending of a suggest request to Google until
     the time the request was returned with status==success. Ignores requests
@@ -75215,14 +75409,28 @@
 
 <histogram name="Omnibox.SuggestRequests" enum="OmniboxSuggestRequests">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Counts about the number of suggest requests the omnibox sent, invalidated,
     and replies received.
   </summary>
 </histogram>
 
+<histogram base="true" name="Omnibox.TimeUntilFirst" units="ms"
+    expires_after="2020-01-01">
+  <owner>mdjones@chromium.org</owner>
+  <owner>lzbylut@google.com</owner>
+  <summary>
+    The amount of time that passes in ms between the user focusing the omnibox
+    and performing some action on Android. This is only recorded the first time
+    the user performs this action per omnibox focus event. Other
+    Omnibox.TimeUntilFirst.* events are not recorded past the first event.
+  </summary>
+</histogram>
+
 <histogram name="Omnibox.TypedLength" units="characters">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The length of the text the user entered in the omnibox when they used the
     omnibox to go somewhere. Switched on March 27 2018 from an bucketing where
@@ -75233,6 +75441,7 @@
 
 <histogram name="Omnibox.TypingDuration" units="ms">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The amount of time, in milliseconds, since the user first began modifying
     the text in the omnibox until the user used the omnibox to go somewhere. If
@@ -75245,6 +75454,7 @@
 
 <histogram name="Omnibox.URLNavigationScheme" enum="NavigationScheme">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     The scheme of the destination URL for the selected omnibox suggestion. This
     could be a what-you-typed suggestion (if the user fully typed a URL), an
@@ -75258,6 +75468,8 @@
 </histogram>
 
 <histogram name="Omnibox.URLNavigationTimeToRedirectToHTTPS" units="ms">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>cthomp@chromium.org</owner>
   <summary>
     The amount of time, in milliseconds, between the start of a typed URL
@@ -75276,6 +75488,7 @@
 <histogram name="Omnibox.UserTextCleared" enum="OmniboxUserTextCleared">
   <owner>kenjibaheux@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Counts the number of times that the user text is cleared. IME users are
     sometimes in the situation that IME was unintentionally turned on and failed
@@ -75294,6 +75507,7 @@
 <histogram name="Omnibox.WarmupTime" units="ms">
   <owner>etienneb@chromium.org</owner>
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Time it takes for the omnibox to process the first user interaction after
     startup. This measures the time it takes to start all autocomplete providers
@@ -75306,6 +75520,7 @@
 <histogram name="Omnibox.ZeroSuggest.Eligible.OnFocus"
     enum="ZeroSuggestEligibleOnFocus">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Whether the user has settings configured so that the current page URL can be
     sent to the suggest server to request contextual suggestions. For example,
@@ -75333,6 +75548,7 @@
 <histogram name="Omnibox.ZeroSuggest.Eligible.OnProfileOpen"
     enum="BooleanSupported">
   <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <summary>
     Whether the user has settings configured so that the current page URL could
     be sent to the suggest server to request contextual suggestions. For
@@ -75357,6 +75573,8 @@
 </histogram>
 
 <histogram name="Omnibox.ZeroSuggest.MostVisitedResultsCounterfactual">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>hfung@chromium.org</owner>
   <summary>
     The number of most visited suggestions returned when ZeroSuggest would have
@@ -75366,6 +75584,8 @@
 </histogram>
 
 <histogram name="Omnibox.ZeroSuggestRequests" enum="OmniboxZeroSuggestRequests">
+  <owner>mpearson@chromium.org</owner>
+  <owner>jdonnelly@chromium.org</owner>
   <owner>hfung@chromium.org</owner>
   <summary>
     Counts about the number of zero suggest requests (requests for suggestions
@@ -75585,6 +75805,9 @@
 
 <histogram name="OSX.BluetoothAvailability" enum="BluetoothAvailability"
     expires_after="2018-08-30">
+  <obsolete>
+    Obsolete as of Chrome 73. This has been replaced by Bluetooth.Availability.
+  </obsolete>
   <owner>erikchen@chromium.org</owner>
   <summary>
     The availability and capabilities of the Bluetooth driver on OSX devices.
@@ -75992,7 +76215,7 @@
 </histogram>
 
 <histogram name="P2P.Client.Canceled.WaitingTimeSeconds" units="seconds">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The wall-clock time spent until a lookup was canceled. This is reported
     every time p2p is used to find a candidate but the request was canceled.
@@ -76000,7 +76223,7 @@
 </histogram>
 
 <histogram name="P2P.Client.Found.CandidateCount" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The number of candidates on the LAN, i.e. the number of peers on the LAN
     offering at least N bytes of the requested file X. This is reported after
@@ -76009,7 +76232,7 @@
 </histogram>
 
 <histogram name="P2P.Client.Found.ConnectionCount" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The number of p2p downloads of the peer that the returned URL points to.
     This is reported after examining responses from all peers on the LAN and
@@ -76018,7 +76241,7 @@
 </histogram>
 
 <histogram name="P2P.Client.Found.WaitingTimeSeconds" units="seconds">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The wall-clock time spent waiting for the LAN-wide number of p2p downloads
     (i.e. the sum of p2p downloads from each peer on the LAN) to drop below the
@@ -76028,7 +76251,7 @@
 </histogram>
 
 <histogram name="P2P.Client.LookupResult" enum="P2PLookupResult">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The result of the lookup. Possible values include &quot;Found&quot; (if a
     candidate - i.e. a peer offering at least N bytes of file X - was chosen),
@@ -76042,7 +76265,7 @@
 </histogram>
 
 <histogram name="P2P.Client.NumPeers" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The number of peers implementing p2p file sharing on the network. This is
     reported every time p2p is used to look up a resource on a network where
@@ -76051,7 +76274,7 @@
 </histogram>
 
 <histogram name="P2P.Client.Vanished.WaitingTimeSeconds" units="seconds">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The wall-clock time spent waiting for one or more candidates (i.e. peers
     offering at least N bytes of file X) that all vanished before the LAN-wide
@@ -76061,7 +76284,7 @@
 </histogram>
 
 <histogram name="P2P.Server.ClientCount" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The number of currently connected HTTP clients. This is reported every time
     a HTTP client connects.
@@ -76069,7 +76292,7 @@
 </histogram>
 
 <histogram name="P2P.Server.ContentServedInterruptedMB" units="MB">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     Number of megabytes (1,000,000 bytes) served from the device (via HTTP)
     where the client disconnects prematurely. This is reported every time a file
@@ -76078,7 +76301,7 @@
 </histogram>
 
 <histogram name="P2P.Server.ContentServedSuccessfullyMB" units="MB">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     Number of megabytes (1,000,000 bytes) served from the device (via HTTP).
     This is reported every time a file have been served successfully.
@@ -76086,7 +76309,7 @@
 </histogram>
 
 <histogram name="P2P.Server.DownloadSpeedKBps" units="kB/s">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The average speed at which the download was served at, in kB/s. This is
     reported every time a file have been served successfully.
@@ -76094,7 +76317,7 @@
 </histogram>
 
 <histogram name="P2P.Server.FileCount" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The number of files available via p2p. This is reported every time a file is
     added or removed to the /var/cache/p2p directory.
@@ -76102,7 +76325,7 @@
 </histogram>
 
 <histogram name="P2P.Server.RangeBeginPercentage" units="%">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     When a client resumes a download, the HTTP request includes range specifier
     to skip the bytes it already has. This metric conveys this as a percentage
@@ -76112,7 +76335,7 @@
 </histogram>
 
 <histogram name="P2P.Server.RequestResult" enum="P2PServerResult">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>ahassani@chromium.org</owner>
   <summary>
     The result of the HTTP request. Possible values include &quot;Response
     Sent&quot; (the resource was found and the response was successfully sent),
@@ -86365,6 +86588,8 @@
 
 <histogram name="Power.DarkResumeWakeDurationMs" units="ms">
   <owner>chirantan@chromium.org</owner>
+  <owner>abhishebh@chromium.org</owner>
+  <owner>ravisadineni@chromium.org</owner>
   <summary>
     The amount of time a system spent awake every time it woke up in dark
     resume.
@@ -86373,7 +86598,8 @@
 
 <histogram name="Power.DarkResumeWakeDurationMs.Other" units="ms">
   <owner>chirantan@chromium.org</owner>
-  <owner>samueltan@chromium.org</owner>
+  <owner>abhishebh@chromium.org</owner>
+  <owner>ravisadineni@chromium.org</owner>
   <summary>
     The amount of time a system spent awake every time it woke up in dark resume
     triggered by an unknown or unsupported wake trigger.
@@ -86382,7 +86608,8 @@
 
 <histogram name="Power.DarkResumeWakeDurationMs.WiFi.Disconnect" units="ms">
   <owner>chirantan@chromium.org</owner>
-  <owner>samueltan@chromium.org</owner>
+  <owner>abhishebh@chromium.org</owner>
+  <owner>ravisadineni@chromium.org</owner>
   <summary>
     The amount of time a system spent awake every time it woke up in dark resume
     triggered by a WiFi disconnect.
@@ -86391,7 +86618,8 @@
 
 <histogram name="Power.DarkResumeWakeDurationMs.WiFi.Pattern" units="ms">
   <owner>chirantan@chromium.org</owner>
-  <owner>samueltan@chromium.org</owner>
+  <owner>abhishebh@chromium.org</owner>
+  <owner>ravisadineni@chromium.org</owner>
   <summary>
     The amount of time a system spent awake every time it woke up in dark resume
     triggered by a WiFi packet pattern match.
@@ -86400,7 +86628,8 @@
 
 <histogram name="Power.DarkResumeWakeDurationMs.WiFi.SSID" units="ms">
   <owner>chirantan@chromium.org</owner>
-  <owner>samueltan@chromium.org</owner>
+  <owner>abhishebh@chromium.org</owner>
+  <owner>ravisadineni@chromium.org</owner>
   <summary>
     The amount of time a system spent awake every time it woke up in dark resume
     triggered by a net detect SSID match.
@@ -110533,7 +110762,7 @@
 </histogram>
 
 <histogram name="Stability.Internals.DataDiscardCount" units="counts">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Number of times stability data was discarded. This is accumulated since the
     last report, even across versions. This is logged during stability metric
@@ -110543,7 +110772,7 @@
 
 <histogram name="Stability.Internals.InitialStabilityLogDeferredCount"
     units="counts">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Number of times the initial stability log upload was deferred to the next
     startup. This is logged during stability metric recording for the following
@@ -110560,7 +110789,7 @@
 </histogram>
 
 <histogram name="Stability.Internals.VersionMismatchCount" units="counts">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Number of times the version number stored in prefs did not match the
     serialized system profile version number. This is logged during stability
@@ -113085,7 +113314,7 @@
 </histogram>
 
 <histogram name="Suggestions.LocalBlacklistSize" units="URLcount">
-  <owner>manzagop@chromium.org</owner>
+  <owner>siggi@chromium.org</owner>
   <summary>
     Number of URLs present in the Suggestions local blacklist when the
     Suggestions service is created.
@@ -116017,7 +116246,7 @@
 </histogram>
 
 <histogram name="Tab.RendererCrashStatus" enum="TabRendererCrashStatus">
-  <owner>jaekyun@chromium.org</owner>
+  <owner>clank-team@google.com</owner>
   <summary>
     [Android] The status of a tab and an application when a renderer crashes.
     This is recorded only for Android when a renderer crashes.
@@ -116326,7 +116555,7 @@
 </histogram>
 
 <histogram name="Tab.TotalTabCount.BeforeLeavingApp" units="tabs">
-  <owner>jaekyun@chromium.org</owner>
+  <owner>clank-team@google.com</owner>
   <summary>
     [Android] The total count of tabs which were kept while Chrome process is in
     the foreground. This is recorded only for Android right before Chrome
@@ -117714,8 +117943,7 @@
 </histogram>
 
 <histogram name="Tabs.TabCountPerWindow" units="tabs">
-  <owner>bruthig@chromium.org</owner>
-  <owner>tdanderson@chromium.org</owner>
+  <owner>pkasting@chromium.org</owner>
   <summary>
     The number of tabs open per window (counting app-mode windows) when a load
     completes.
@@ -117727,7 +117955,7 @@
 </histogram>
 
 <histogram name="Tabs.TabOffsetOfSwitch">
-  <owner>rlanday@chromium.org</owner>
+  <owner>yusufo@chromium.org</owner>
   <summary>
     How many tabs a user moved forward or backward in the Android tab switcher.
   </summary>
@@ -119042,13 +119270,13 @@
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Event" enum="TranslateCompactUIEvent">
-  <owner>ramyasharma@google.com</owner>
+  <owner>anthonyvd@chromium.org</owner>
   <summary>Various user actions performed in the translate infobar.</summary>
 </histogram>
 
 <histogram name="Translate.CompactInfobar.Language.AlwaysTranslate"
     enum="CLD3LanguageCode">
-  <owner>ramyasharma@google.com</owner>
+  <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the hashcode of the source language when always translate this
     language option is clicked in the menu.
@@ -119057,7 +119285,7 @@
 
 <histogram name="Translate.CompactInfobar.Language.MoreLanguages"
     enum="CLD3LanguageCode">
-  <owner>ramyasharma@google.com</owner>
+  <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the hashcode of the language clicked on the more languages menu.
   </summary>
@@ -119065,7 +119293,7 @@
 
 <histogram name="Translate.CompactInfobar.Language.NeverTranslate"
     enum="CLD3LanguageCode">
-  <owner>ramyasharma@google.com</owner>
+  <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the hashcode of the source language when never translate this
     language option is clicked in the menu.
@@ -119074,7 +119302,7 @@
 
 <histogram name="Translate.CompactInfobar.Language.PageNotIn"
     enum="CLD3LanguageCode">
-  <owner>ramyasharma@google.com</owner>
+  <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the hashcode of the language clicked on the menu to indicate the
     page is not in the selected language.
@@ -119083,7 +119311,7 @@
 
 <histogram name="Translate.CompactInfobar.Language.Translate"
     enum="CLD3LanguageCode">
-  <owner>ramyasharma@google.com</owner>
+  <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the hashcode of the language clicked on the infobar.
   </summary>
@@ -119091,7 +119319,7 @@
 
 <histogram name="Translate.CompactInfobar.TranslationsPerPage"
     units="translations">
-  <owner>ramyasharma@google.com</owner>
+  <owner>anthonyvd@chromium.org</owner>
   <summary>
     Records the number of times a page is translated, every time the page is
     translated. For instance on a page a) translation from A to B, we record
@@ -119193,7 +119421,7 @@
 </histogram>
 
 <histogram name="Translate.InfobarShown" enum="BooleanHit">
-  <owner>ramyasharma@google.com</owner>
+  <owner>anthonyvd@chromium.org</owner>
   <summary>
     The number of times the translate infobar was shown in the old translate UI
     on Android. Only true is recorded.
@@ -120937,6 +121165,19 @@
   </summary>
 </histogram>
 
+<histogram
+    name="UnifiedConsent.SyncAndGoogleServicesSettings.AfterAdvancedOptIn.SyncDataTypesOff"
+    enum="UnifiedConsentSyncDataTypesOffAfterAdvancedOptIn"
+    expires_after="2020-01-31">
+  <owner>tangltom@chromium.org</owner>
+  <owner>droger@chromium.org</owner>
+  <owner>msarda@chromium.org</owner>
+  <summary>
+    All *off* sync data types are recorded when the user confirmed the sync
+    setup after going through the advanced opt-in flow.
+  </summary>
+</histogram>
+
 <histogram name="UpdateClient.BackgroundDownloaderJobs" units="jobs">
   <owner>sorin@chromium.org</owner>
   <summary>
@@ -120958,7 +121199,7 @@
 
 <histogram name="UpdateEngine.Attempt.ConnectionType"
     enum="UpdateEngineConnectionType">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The network connection type when the attempt begins. Possible values include
     &quot;Unknown&quot;, &quot;Ethernet&quot;, &quot;Wifi&quot;,
@@ -120973,7 +121214,7 @@
 
 <histogram name="UpdateEngine.Attempt.DownloadErrorCode"
     enum="UpdateEngineDownloadErrorCode">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     A more detailed description of the last Payload transfer error when
     downloading the payload.
@@ -120987,7 +121228,7 @@
 
 <histogram name="UpdateEngine.Attempt.DownloadSource"
     enum="UpdateEngineDownloadSource">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The download source used, possible values include &quot;HTTPS Server&quot;,
     &quot;HTTP Server&quot; and &quot;HTTP Peer&quot;.
@@ -120999,7 +121240,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.DurationMinutes" units="minutes">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The number of minutes the update attempt took including the time the device
     spent sleeping.
@@ -121011,7 +121252,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.DurationUptimeMinutes" units="minutes">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The number of minutes the update attempt took excluding the time the device
     spent sleeping.
@@ -121024,7 +121265,7 @@
 
 <histogram name="UpdateEngine.Attempt.InternalErrorCode"
     enum="UpdateEngineErrorCode">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     A more detailed description of the last internal error. The possible values
     correspond to the ErrorCode enumeration in the update_engine source code.
@@ -121036,7 +121277,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.Number" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The attempt number which starts at 0 for the initial attempt and keeps
     increasing for subsequent attempts.
@@ -121048,7 +121289,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.PayloadBytesDownloadedMiB" units="MiB">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The number of payload mebibytes (1048576 bytes) actually download.
 
@@ -121059,7 +121300,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.PayloadDownloadSpeedKBps" units="KBps">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The payload download speed, in kilobytes per second (1000 bytes/second).
     This is calculated as the number of bytes downloaded divided by the duration
@@ -121072,7 +121313,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.PayloadSizeMiB" units="MiB">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The payload size, in mebibytes (1048576 bytes).
 
@@ -121084,7 +121325,7 @@
 
 <histogram name="UpdateEngine.Attempt.PayloadType"
     enum="UpdateEnginePayloadFormat">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The payload type, possible values include &quot;Delta&quot; (if Omaha
     specified to download a delta payload); and &quot;Full&quot; (if Omaha
@@ -121098,7 +121339,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Attempt.Result" enum="UpdateEngineAttemptResult">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The result of the update attempt.
 
@@ -121110,7 +121351,7 @@
 
 <histogram name="UpdateEngine.Attempt.TimeSinceLastAttemptMinutes"
     units="minutes">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The number of minutes since the last attempt including the time the device
     spent sleeping.
@@ -121124,7 +121365,7 @@
 
 <histogram name="UpdateEngine.Attempt.TimeSinceLastAttemptUptimeMinutes"
     units="minutes">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The number of minutes since the last attempt excluding the time the device
     spent sleeping.
@@ -121165,7 +121406,7 @@
 
 <histogram name="UpdateEngine.Check.DownloadErrorCode"
     enum="UpdateEngineDownloadErrorCode">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     If unable to download a response from Omaha, a more detailed error code is
     reported in this metric.
@@ -121178,7 +121419,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.Reaction" enum="UpdateEngineCheckReaction">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     If there is an update available, this metric will track what the device does
     with the information. Possible values include &quot;Applying update&quot;,
@@ -121192,7 +121433,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.Result" enum="UpdateEngineCheckResult">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The response from Omaha. Possible values include &quot;No update
     available&quot;, &quot;Update available&quot;, &quot;Download error&quot;,
@@ -121241,7 +121482,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Check.TimeSinceLastCheckMinutes" units="minutes">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The number of minutes since the last check including the time the device
     spent sleeping.
@@ -121254,7 +121495,7 @@
 
 <histogram name="UpdateEngine.Check.TimeSinceLastCheckUptimeMinutes"
     units="minutes">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The number of minutes since the last check excluding the time the device
     spent sleeping.
@@ -121266,7 +121507,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Daily.OSAgeDays" units="days">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The age of the OS in days, defined as the age of the /etc/lsb-release file.
 
@@ -121311,7 +121552,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.FailedUpdateCount" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The number of consecutive times a device has failed to boot an update that
     successfully applied.
@@ -121325,7 +121566,7 @@
 
 <histogram name="UpdateEngine.InstallDateProvisioningSource"
     enum="UpdateEngineInstallDateProvisioningSource">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The source used to provision the install-date-days value sent to Omaha with
     every request.
@@ -121380,7 +121621,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.Rollback.Result" enum="BooleanSuccess">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     Whether rollback worked.
 
@@ -121391,7 +121632,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.AttemptCount" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The total number of update attempts required to update the device.
 
@@ -121402,7 +121643,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.BytesDownloadedMiB" units="MiB">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The total number of bytes downloaded in mebibytes (1048576 bytes) using all
     available sources (e.g. HTTP, HTTPS, HTTP Peer).
@@ -121415,7 +121656,7 @@
 
 <histogram name="UpdateEngine.SuccessfulUpdate.BytesDownloadedMiBHttpPeer"
     units="MiB">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The total number of bytes downloaded in mebibytes (1048576 bytes) using HTTP
     from a local peer.
@@ -121428,7 +121669,7 @@
 
 <histogram name="UpdateEngine.SuccessfulUpdate.BytesDownloadedMiBHttpServer"
     units="MiB">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The total number of bytes downloaded in mebibytes (1048576 bytes) using
     HTTP.
@@ -121441,7 +121682,7 @@
 
 <histogram name="UpdateEngine.SuccessfulUpdate.BytesDownloadedMiBHttpsServer"
     units="MiB">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The total number of bytes downloaded in mebibytes (1048576 bytes) using
     HTTPS.
@@ -121454,7 +121695,7 @@
 
 <histogram name="UpdateEngine.SuccessfulUpdate.DownloadOverheadPercentage"
     units="%">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The ratio between bytes downloaded and payload size minus 100.
 
@@ -121466,7 +121707,7 @@
 
 <histogram name="UpdateEngine.SuccessfulUpdate.DownloadSourcesUsed"
     enum="UpdateEngineDownloadSources">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The various download sources used - this is a combination of the values
     &quot;HTTPS Server&quot;, &quot;HTTP Server&quot; and &quot;HTTP Peer&quot;.
@@ -121507,7 +121748,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.PayloadSizeMiB" units="MiB">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The size of the payload, in mebibytes (1048576 bytes).
 
@@ -121519,7 +121760,7 @@
 
 <histogram name="UpdateEngine.SuccessfulUpdate.PayloadType"
     enum="UpdateEnginePayloadFormat">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The payload type (&quot;Delta&quot;, &quot;Full&quot;,
     &quot;ForcedFull&quot;) used.
@@ -121531,7 +121772,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.RebootCount" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The total number of reboots during the update.
 
@@ -121543,7 +121784,7 @@
 
 <histogram name="UpdateEngine.SuccessfulUpdate.TotalDurationMinutes"
     units="minutes">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The total number of minutes from when an update was detected until an update
     (possibly another update) was applied. This includes the time waiting for
@@ -121571,7 +121812,7 @@
 
 <histogram name="UpdateEngine.SuccessfulUpdate.UpdatesAbandonedCount"
     units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The total number of updates that were abandoned since the last successful
     update.
@@ -121583,7 +121824,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.SuccessfulUpdate.UrlSwitchCount" units="count">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The total number of times the URL was switched (from e.g. HTTPS to HTTP)
     because of failures.
@@ -121595,7 +121836,7 @@
 </histogram>
 
 <histogram name="UpdateEngine.TimeToRebootMinutes" units="minutes">
-  <owner>zeuthen@chromium.org</owner>
+  <owner>senj@chromium.org</owner>
   <summary>
     The duration between when an update has successfully completed and the user
     is presented with the &quot;reboot arrow&quot; and when the system has
@@ -130555,6 +130796,18 @@
   </summary>
 </histogram>
 
+<histogram name="Windows.InspectModule.ConnectionError"
+    enum="BooleanConnectionError">
+  <owner>pmonette@chromium.org</owner>
+  <summary>
+    Whether connection error has happened for the UtilWin service. Every time a
+    connection is made to the service, a &quot;false&quot; value is reported to
+    provide a baseline. Every time a mojo connection error happens, a
+    &quot;true&quot; value is reported, which typically means the utility
+    process crashed.
+  </summary>
+</histogram>
+
 <histogram name="Windows.IsPinnedToTaskbar" enum="IsPinnedToTaskbarResult">
   <owner>pmonette@chromium.org</owner>
   <summary>
@@ -138816,6 +139069,13 @@
   <affected-histogram name="Tab.TabUnderAction"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="OmniboxFocusFirstAction" separator=".">
+  <suffix name="Copy" label="Copy action was performed."/>
+  <suffix name="Cut" label="Cut action was performed."/>
+  <suffix name="Share" label="Share action was performed."/>
+  <affected-histogram name="Omnibox.TimeUntilFirst"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="OmniboxPageContext" separator=".">
   <suffix name="APP_HOME" label="home screen"/>
   <suffix name="APP_MAPS" label="maps app"/>
@@ -142206,6 +142466,7 @@
   <suffix name="harmful_subresource"/>
   <suffix name="malware"/>
   <suffix name="malware_subresource"/>
+  <suffix name="origin_policy"/>
   <suffix name="phishing"/>
   <suffix name="phishing_subresource"/>
   <suffix name="social_engineering_ads"/>
@@ -142246,6 +142507,9 @@
   <affected-histogram
       name="interstitial.malware_subresource.decision.repeat_visit"/>
   <affected-histogram name="interstitial.malware_subresource.interaction"/>
+  <affected-histogram name="interstitial.origin_policy.decision"/>
+  <affected-histogram name="interstitial.origin_policy.decision.repeat_visit"/>
+  <affected-histogram name="interstitial.origin_policy.interaction"/>
   <affected-histogram name="interstitial.phishing.decision"/>
   <affected-histogram name="interstitial.phishing.decision.repeat_visit"/>
   <affected-histogram name="interstitial.phishing.interaction"/>
@@ -143788,7 +144052,11 @@
 
 <histogram_suffixes name="TaskSchedulerWorkerPool" separator=".">
   <suffix name="BackgroundBlockingPool"
-      label="Applies to the BackgroundBlocking worker pool."/>
+      label="Applies to the BackgroundBlocking worker pool.">
+    <obsolete>
+      Deprecated January 2019 as the pool no longer exists.
+    </obsolete>
+  </suffix>
   <suffix name="BackgroundFileIOPool"
       label="Applies to the BackgroundFileIO worker pool.">
     <obsolete>
@@ -143797,7 +144065,11 @@
   </suffix>
   <suffix name="BackgroundPool" label="Applies to the Background worker pool."/>
   <suffix name="ForegroundBlockingPool"
-      label="Applies to the ForegroundBlocking worker pool."/>
+      label="Applies to the ForegroundBlocking worker pool.">
+    <obsolete>
+      Deprecated January 2019 as the pool no longer exists.
+    </obsolete>
+  </suffix>
   <suffix name="ForegroundFileIOPool"
       label="Applies to the ForegroundFileIO worker pool.">
     <obsolete>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 12bb1fec..0791751 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -3964,6 +3964,13 @@
       enum ManagerAutofillEvent.
     </summary>
   </metric>
+  <metric name="ManagerFill.Assistance">
+    <summary>
+      Records for each successful submission the degree to which the user has
+      been assisted by the password manager. Recorded values correspond to the
+      enum password_manager::PasswordFormMetricsRecorder::FillingAssistance.
+    </summary>
+  </metric>
   <metric name="ParsingComparison">
     <summary>
       Records comparison result of old and new password form parsing algorithms.
diff --git a/tools/perf/contrib/vr_benchmarks/BUILD.gn b/tools/perf/contrib/vr_benchmarks/BUILD.gn
index ea31c49..7996169b 100644
--- a/tools/perf/contrib/vr_benchmarks/BUILD.gn
+++ b/tools/perf/contrib/vr_benchmarks/BUILD.gn
@@ -23,7 +23,7 @@
     "//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk",
     "//third_party/gvr-android-sdk/test-apks/vr_keyboard/vr_keyboard_current.apk",
     "//chrome/test/data/xr/webvr_info/samples/",
-    "//chrome/test/data/xr/webxr_samples/",
+    "//third_party/webxr_test_pages/webxr-samples",
   ]
   data_deps = [
     "//chrome/android:vr_nfc_simulator_apk",
diff --git a/tools/perf/contrib/vr_benchmarks/vr_sample_page.py b/tools/perf/contrib/vr_benchmarks/vr_sample_page.py
index 2aa3db1..73a5f08 100644
--- a/tools/perf/contrib/vr_benchmarks/vr_sample_page.py
+++ b/tools/perf/contrib/vr_benchmarks/vr_sample_page.py
@@ -14,8 +14,8 @@
 
 
 WEBXR_SAMPLE_DIR = os.path.join(
-    os.path.dirname(__file__), '..', '..', '..', '..', 'chrome', 'test',
-    'data', 'xr', 'webxr_samples')
+    os.path.dirname(__file__), '..', '..', '..', '..', 'third_party',
+        'webxr_test_pages', 'webxr-samples')
 
 
 class _VrXrSamplePage(page.Page):
diff --git a/tools/perf/contrib/vr_benchmarks/webxr_sample_pages.py b/tools/perf/contrib/vr_benchmarks/webxr_sample_pages.py
index 2ba465d..6e3afb2c 100644
--- a/tools/perf/contrib/vr_benchmarks/webxr_sample_pages.py
+++ b/tools/perf/contrib/vr_benchmarks/webxr_sample_pages.py
@@ -17,6 +17,9 @@
         extra_browser_args=extra_browser_args)
 
   def RunPageInteractions(self, action_runner):
+    # The user-visible text differs per-page (e.g. "Enter VR"), but the
+    # element's "title" attribute is currently always "Enter XR".
+    action_runner.WaitForElement(selector='button[title="Enter XR"]')
     action_runner.TapElement(selector='button[title="Enter XR"]')
     action_runner.MeasureMemory(True)
     # We don't want to be in VR or on a page with a WebGL canvas at the end of
diff --git a/tools/perf/core/perf_data_generator.py b/tools/perf/core/perf_data_generator.py
index 968d173..8e32c97f 100755
--- a/tools/perf/core/perf_data_generator.py
+++ b/tools/perf/core/perf_data_generator.py
@@ -980,7 +980,6 @@
     'dimension_sets': [
       tester_config['dimension']
     ],
-    'upload_test_results': True,
     'shards': shards,
   }
   return result
diff --git a/tools/perf/core/perf_data_generator_unittest.py b/tools/perf/core/perf_data_generator_unittest.py
index 2e6b27e2..00009d3 100644
--- a/tools/perf/core/perf_data_generator_unittest.py
+++ b/tools/perf/core/perf_data_generator_unittest.py
@@ -89,7 +89,6 @@
           'expiration': 7200,
           'io_timeout': 1800,
           'hard_timeout': 36000,
-          'upload_test_results': True,
           'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
           'shards': 1
         },
@@ -140,7 +139,6 @@
           'expiration': 7200,
           'io_timeout': 1800,
           'hard_timeout': 36000,
-          'upload_test_results': True,
           'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
           'shards': 26
         },
@@ -189,7 +187,6 @@
           'expiration': 7200,
           'io_timeout': 1800,
           'hard_timeout': 36000,
-          'upload_test_results': True,
           'dimension_sets': [[{'os': 'SkyNet', 'pool': 'T-RIP'}]],
           'shards': 26
         },
diff --git a/tools/traffic_annotation/OWNERS b/tools/traffic_annotation/OWNERS
index b57a96b1..01d4a759 100644
--- a/tools/traffic_annotation/OWNERS
+++ b/tools/traffic_annotation/OWNERS
@@ -1,4 +1,3 @@
 battre@chromium.org
-georgesak@chromium.org
 msramek@chromium.org
 rhalavati@chromium.org
diff --git a/tools/traffic_annotation/bin/OWNERS b/tools/traffic_annotation/bin/OWNERS
index 33258f6..f8e2f4f 100644
--- a/tools/traffic_annotation/bin/OWNERS
+++ b/tools/traffic_annotation/bin/OWNERS
@@ -1,3 +1,2 @@
-georgesak@chromium.org
 nicolaso@chromium.org
 rhalavati@chromium.org
diff --git a/tools/traffic_annotation/summary/OWNERS b/tools/traffic_annotation/summary/OWNERS
index 35033cb..31ce182 100644
--- a/tools/traffic_annotation/summary/OWNERS
+++ b/tools/traffic_annotation/summary/OWNERS
@@ -1,7 +1,6 @@
 set noparent
 
 dullweber@chromium.org
-georgesak@chromium.org
 mkwst@chromium.org
 msramek@chromium.org
 nicolaso@chromium.org
diff --git a/ui/accessibility/ax_table_info.cc b/ui/accessibility/ax_table_info.cc
index 250b91d..d4e9f40b 100644
--- a/ui/accessibility/ax_table_info.cc
+++ b/ui/accessibility/ax_table_info.cc
@@ -38,16 +38,17 @@
 // for each row find its cells and add them to |cell_nodes_per_row| as a
 // 2-dimensional array.
 //
-// We recursively check generic containers like <div> and any
-// nodes that are ignored, but we don't search any other roles
-// in-between a table and its rows.
+// We only recursively check for the following roles in between a table and
+// its rows: generic containers like <div>, any nodes that are ignored, and
+// table sections (which have Role::kGroup).
 void FindRowsAndThenCells(
     AXNode* node,
     std::vector<AXNode*>* row_nodes,
     std::vector<std::vector<AXNode*>>* cell_nodes_per_row) {
   for (AXNode* child : node->children()) {
     if (child->data().HasState(ax::mojom::State::kIgnored) ||
-        child->data().role == ax::mojom::Role::kGenericContainer) {
+        child->data().role == ax::mojom::Role::kGenericContainer ||
+        child->data().role == ax::mojom::Role::kGroup) {
       FindRowsAndThenCells(child, row_nodes, cell_nodes_per_row);
     } else if (child->data().role == ax::mojom::Role::kRow) {
       row_nodes->push_back(child);
diff --git a/ui/accessibility/ax_tree.cc b/ui/accessibility/ax_tree.cc
index a0b8c3c..7f9a33d 100644
--- a/ui/accessibility/ax_tree.cc
+++ b/ui/accessibility/ax_tree.cc
@@ -89,6 +89,10 @@
   }
 }
 
+bool IsCollapsed(const AXNode* node) {
+  return node && node->data().HasState(ax::mojom::State::kCollapsed);
+}
+
 }  // namespace
 
 // Intermediate state to keep track of during a tree update.
@@ -938,6 +942,17 @@
   for (int i = 0; i < local_parent->child_count(); ++i) {
     const AXNode* child = local_parent->GetUnignoredChildAtIndex(i);
 
+    // Invisible children should not be counted.
+    // However, in the collapsed container case (e.g. a combobox), items can
+    // still be chosen/navigated. However, the options in these collapsed
+    // containers are historically marked invisible. Therefore, in that case,
+    // count the invisible items. Only check 2 levels up, as combobox containers
+    // are never higher.
+    if (child->data().HasState(ax::mojom::State::kInvisible) &&
+        !IsCollapsed(local_parent) && !IsCollapsed(local_parent->parent())) {
+      continue;
+    }
+
     // If role of node is kRadioButton, only add other kRadioButtons.
     if (node_is_radio_button &&
         child->data().role == ax::mojom::Role::kRadioButton)
diff --git a/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java b/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java
index b24a8b57..7d1f995d1 100644
--- a/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java
+++ b/ui/android/java/src/org/chromium/ui/DropdownPopupWindowImpl.java
@@ -95,7 +95,7 @@
         rectProvider.setInsetPx(0, /* top= */ paddingRect.bottom, 0, /* bottom= */ paddingRect.top);
         mHorizontalPadding = paddingRect.right + paddingRect.left;
         mAnchoredPopupWindow.setPreferredHorizontalOrientation(
-                AnchoredPopupWindow.HORIZONTAL_ORIENTATION_CENTER);
+                AnchoredPopupWindow.HorizontalOrientation.CENTER);
         mAnchoredPopupWindow.setUpdateOrientationOnChange(true);
         mAnchoredPopupWindow.setOutsideTouchable(true);
     }
diff --git a/ui/android/java/src/org/chromium/ui/modaldialog/DialogDismissalCause.java b/ui/android/java/src/org/chromium/ui/modaldialog/DialogDismissalCause.java
index 1058df1..c784600 100644
--- a/ui/android/java/src/org/chromium/ui/modaldialog/DialogDismissalCause.java
+++ b/ui/android/java/src/org/chromium/ui/modaldialog/DialogDismissalCause.java
@@ -13,7 +13,8 @@
         DialogDismissalCause.NEGATIVE_BUTTON_CLICKED, DialogDismissalCause.ACTION_ON_CONTENT,
         DialogDismissalCause.DISMISSED_BY_NATIVE,
         DialogDismissalCause.NAVIGATE_BACK_OR_TOUCH_OUTSIDE, DialogDismissalCause.TAB_SWITCHED,
-        DialogDismissalCause.TAB_DESTROYED, DialogDismissalCause.ACTIVITY_DESTROYED})
+        DialogDismissalCause.TAB_DESTROYED, DialogDismissalCause.ACTIVITY_DESTROYED,
+        DialogDismissalCause.NOT_ATTACHED_TO_WINDOW})
 @Retention(RetentionPolicy.SOURCE)
 public @interface DialogDismissalCause {
     // Please do not remove or change the order of the existing values, and add new value at the end
@@ -38,4 +39,6 @@
     int TAB_DESTROYED = 7;
     /** The activity associated with the dialog is destroyed. */
     int ACTIVITY_DESTROYED = 8;
+    /** The content view of the activity associated with the dialog is not attached to window. */
+    int NOT_ATTACHED_TO_WINDOW = 9;
 }
diff --git a/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java b/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java
index bad9968..5ad86ef6 100644
--- a/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java
+++ b/ui/android/java/src/org/chromium/ui/widget/AnchoredPopupWindow.java
@@ -50,34 +50,36 @@
     }
 
     /** VerticalOrientation preferences for the popup */
-    @IntDef({VERTICAL_ORIENTATION_MAX_AVAILABLE_SPACE, VERTICAL_ORIENTATION_BELOW,
-            VERTICAL_ORIENTATION_ABOVE})
+    @IntDef({VerticalOrientation.MAX_AVAILABLE_SPACE, VerticalOrientation.BELOW,
+            VerticalOrientation.ABOVE})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface VerticalOrientation {}
-    /**
-     * Vertically position to whichever side of the anchor has more available space. The popup
-     * will be sized to ensure it fits on screen.
-     */
-    public static final int VERTICAL_ORIENTATION_MAX_AVAILABLE_SPACE = 0;
-    /** Position below the anchor if there is enough space. */
-    public static final int VERTICAL_ORIENTATION_BELOW = 1;
-    /** Position above the anchor if there is enough space. */
-    public static final int VERTICAL_ORIENTATION_ABOVE = 2;
+    public @interface VerticalOrientation {
+        /**
+         * Vertically position to whichever side of the anchor has more available space. The popup
+         * will be sized to ensure it fits on screen.
+         */
+        int MAX_AVAILABLE_SPACE = 0;
+        /** Position below the anchor if there is enough space. */
+        int BELOW = 1;
+        /** Position above the anchor if there is enough space. */
+        int ABOVE = 2;
+    }
 
     /** HorizontalOrientation preferences for the popup */
-    @IntDef({HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, HORIZONTAL_ORIENTATION_CENTER})
+    @IntDef({HorizontalOrientation.MAX_AVAILABLE_SPACE, HorizontalOrientation.CENTER})
     @Retention(RetentionPolicy.SOURCE)
-    public @interface HorizontalOrientation {}
-    /**
-     * Horizontally position to whichever side of the anchor has more available space. The popup
-     * will be sized to ensure it fits on screen.
-     */
-    public static final int HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE = 0;
-    /**
-     * Horizontally center with respect to the anchor, so long as the popup still fits on the
-     * screen.
-     */
-    public static final int HORIZONTAL_ORIENTATION_CENTER = 1;
+    public @interface HorizontalOrientation {
+        /**
+         * Horizontally position to whichever side of the anchor has more available space. The popup
+         * will be sized to ensure it fits on screen.
+         */
+        int MAX_AVAILABLE_SPACE = 0;
+        /**
+         * Horizontally center with respect to the anchor, so long as the popup still fits on the
+         * screen.
+         */
+        int CENTER = 1;
+    }
 
     // Cache Rect objects for querying View and Screen coordinate APIs.
     private final Rect mCachedPaddingRect = new Rect();
@@ -137,10 +139,10 @@
     // Preferred orientation for the popup with respect to the anchor.
     // Preferred vertical orientation for the popup with respect to the anchor.
     @VerticalOrientation
-    private int mPreferredVerticalOrientation = VERTICAL_ORIENTATION_MAX_AVAILABLE_SPACE;
+    private int mPreferredVerticalOrientation = VerticalOrientation.MAX_AVAILABLE_SPACE;
     // Preferred horizontal orientation for the popup with respect to the anchor.
     @HorizontalOrientation
-    private int mPreferredHorizontalOrientation = HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE;
+    private int mPreferredHorizontalOrientation = HorizontalOrientation.MAX_AVAILABLE_SPACE;
 
     /**
      * Tracks whether or not we are in the process of updating the popup, which might include a
@@ -423,14 +425,14 @@
             if (!currentPositionBelow && idealFitsAbove) mPositionBelow = false;
         }
 
-        if (mPreferredVerticalOrientation == VERTICAL_ORIENTATION_BELOW && idealFitsBelow) {
+        if (mPreferredVerticalOrientation == VerticalOrientation.BELOW && idealFitsBelow) {
             mPositionBelow = true;
         }
-        if (mPreferredVerticalOrientation == VERTICAL_ORIENTATION_ABOVE && idealFitsAbove) {
+        if (mPreferredVerticalOrientation == VerticalOrientation.ABOVE && idealFitsAbove) {
             mPositionBelow = false;
         }
 
-        if (mPreferredHorizontalOrientation == HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE) {
+        if (mPreferredHorizontalOrientation == HorizontalOrientation.MAX_AVAILABLE_SPACE) {
             int spaceLeftOfAnchor =
                     getSpaceLeftOfAnchor(anchorRect, mCachedWindowRect, mHorizontalOverlapAnchor);
             int spaceRightOfAnchor =
@@ -523,7 +525,7 @@
             boolean positionToLeft) {
         int x;
 
-        if (horizontalOrientation == HORIZONTAL_ORIENTATION_CENTER) {
+        if (horizontalOrientation == HorizontalOrientation.CENTER) {
             x = anchorRect.left + (anchorRect.width() - popupWidth) / 2 + marginPx;
         } else if (positionToLeft) {
             x = (overlapAnchor ? anchorRect.right : anchorRect.left) - popupWidth;
diff --git a/ui/android/junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java b/ui/android/junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java
index ce165e48..34bc97f5 100644
--- a/ui/android/junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java
+++ b/ui/android/junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java
@@ -50,7 +50,7 @@
         assertFalse("positionToLeft incorrect.", positionToLeft);
 
         int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, false,
-                AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, false);
+                AnchoredPopupWindow.HorizontalOrientation.MAX_AVAILABLE_SPACE, false);
         int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, false, true);
 
         assertEquals("Wrong x position.", 20, x);
@@ -73,7 +73,7 @@
         assertFalse("positionToLeft incorrect.", positionToLeft);
 
         int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, true,
-                AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, false);
+                AnchoredPopupWindow.HorizontalOrientation.MAX_AVAILABLE_SPACE, false);
         int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, true, true);
 
         assertEquals("Wrong x position.", 10, x);
@@ -84,7 +84,7 @@
     public void testGetPopupPosition_BelowCenter() {
         Rect anchorRect = new Rect(295, 10, 305, 20);
         int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, false,
-                AnchoredPopupWindow.HORIZONTAL_ORIENTATION_CENTER, false);
+                AnchoredPopupWindow.HorizontalOrientation.CENTER, false);
         int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, false, true);
 
         assertEquals("Wrong x position.", 225, x);
@@ -107,7 +107,7 @@
         assertTrue("positionToLeft incorrect.", positionToLeft);
 
         int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, false,
-                AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, positionToLeft);
+                AnchoredPopupWindow.HorizontalOrientation.MAX_AVAILABLE_SPACE, positionToLeft);
         int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, false, false);
 
         assertEquals("Wrong x position.", 250, x);
@@ -130,7 +130,7 @@
         assertTrue("positionToLeft incorrect.", positionToLeft);
 
         int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, true,
-                AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, true);
+                AnchoredPopupWindow.HorizontalOrientation.MAX_AVAILABLE_SPACE, true);
         int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, true, false);
 
         assertEquals("Wrong x position.", 260, x);
@@ -141,7 +141,7 @@
     public void testGetPopupPosition_ClampedLeftEdge() {
         Rect anchorRect = new Rect(10, 10, 20, 20);
         int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 20, false,
-                AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, true);
+                AnchoredPopupWindow.HorizontalOrientation.MAX_AVAILABLE_SPACE, true);
 
         assertEquals("Wrong x position.", 20, x);
     }
@@ -150,7 +150,7 @@
     public void testGetPopupPosition_ClampedRightEdge() {
         Rect anchorRect = new Rect(590, 800, 600, 820);
         int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 20, false,
-                AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, true);
+                AnchoredPopupWindow.HorizontalOrientation.MAX_AVAILABLE_SPACE, true);
 
         assertEquals("Wrong x position.", 430, x);
     }
diff --git a/ui/aura/BUILD.gn b/ui/aura/BUILD.gn
index 37e49d7..d385261 100644
--- a/ui/aura/BUILD.gn
+++ b/ui/aura/BUILD.gn
@@ -193,7 +193,6 @@
     "//ui/base",
     "//ui/base/clipboard",
     "//ui/base/ime",
-    "//ui/base/user_activity",
     "//ui/display",
     "//ui/events",
     "//ui/events:dom_keyboard_layout",
@@ -430,7 +429,6 @@
     "//ui/aura_extra:tests",
     "//ui/base:test_support",
     "//ui/base/clipboard:clipboard_types",
-    "//ui/base/user_activity",
     "//ui/compositor:test_support",
     "//ui/compositor_extra",
     "//ui/display:test_support",
diff --git a/ui/aura/client/aura_constants.cc b/ui/aura/client/aura_constants.cc
index afc117d..55fc193d 100644
--- a/ui/aura/client/aura_constants.cc
+++ b/ui/aura/client/aura_constants.cc
@@ -51,6 +51,9 @@
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kCreatedByUserGesture, false);
 DEFINE_UI_CLASS_PROPERTY_KEY(bool, kDrawAttentionKey, false);
 DEFINE_UI_CLASS_PROPERTY_KEY(FocusClient*, kFocusClientKey, nullptr);
+DEFINE_UI_CLASS_PROPERTY_KEY(bool,
+                             kGestureDragFromClientAreaTopMovesWindow,
+                             false);
 DEFINE_UI_CLASS_PROPERTY_KEY(Window*, kHostWindowKey, nullptr);
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Size, kMaximumSize, nullptr);
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Size, kMinimumSize, nullptr);
diff --git a/ui/aura/client/aura_constants.h b/ui/aura/client/aura_constants.h
index b142ac2..ca4b6c26 100644
--- a/ui/aura/client/aura_constants.h
+++ b/ui/aura/client/aura_constants.h
@@ -82,6 +82,12 @@
 // A property key to store the focus client on the window.
 AURA_EXPORT extern const WindowProperty<FocusClient*>* const kFocusClientKey;
 
+// Should be set to true for fullscreen/maximized windows that want to be
+// drag-moved in response to gesture events in the top of the client
+// area/screen.
+AURA_EXPORT extern const WindowProperty<bool>* const
+    kGestureDragFromClientAreaTopMovesWindow;
+
 // A property key to store the host window of a window. This lets
 // WebContentsViews find the windows that should constrain NPAPI plugins.
 AURA_EXPORT extern const WindowProperty<Window*>* const kHostWindowKey;
diff --git a/ui/aura/mus/client_side_window_move_handler.cc b/ui/aura/mus/client_side_window_move_handler.cc
index e270f01..f13facc8 100644
--- a/ui/aura/mus/client_side_window_move_handler.cc
+++ b/ui/aura/mus/client_side_window_move_handler.cc
@@ -5,6 +5,7 @@
 #include "ui/aura/mus/client_side_window_move_handler.h"
 
 #include "base/bind.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/screen_position_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/mus/window_tree_client.h"
@@ -12,6 +13,8 @@
 #include "ui/aura/window.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/base/hit_test.h"
+#include "ui/display/display.h"
+#include "ui/display/screen.h"
 #include "ui/events/event.h"
 #include "ui/events/gestures/gesture_recognizer.h"
 
@@ -19,8 +22,55 @@
 
 namespace {
 
-void WindowMoveEnded(Window* window, bool success) {
-  window->env()->gesture_recognizer()->CancelActiveTouches(window);
+// TODO(estade,mukai): De-dupe this constant and the following logic with
+// WmToplevelWindowEventHandler.
+constexpr int kDragStartTopEdgeInset = 8;
+
+Window* GetToplevelTargetForEvent(ui::LocatedEvent* event, int* component) {
+  DCHECK(!event->handled());
+  auto* window = static_cast<Window*>(event->target());
+
+  if (!window || !window->delegate())
+    return nullptr;
+
+  *component = window->delegate()->GetNonClientComponent(event->location());
+
+  if (ui::CanPerformDragOrResize(*component)) {
+    DCHECK_EQ(window, window->GetToplevelWindow());
+    return window;
+  }
+
+  // Gestures can sometimes trigger drags from the client area. All other events
+  // and hit locations will not trigger a drag.
+  if (event->type() != ui::ET_GESTURE_SCROLL_BEGIN || *component != HTCLIENT)
+    return nullptr;
+
+  window = window->GetToplevelWindow();
+  if (!window->GetProperty(client::kGestureDragFromClientAreaTopMovesWindow))
+    return nullptr;
+
+  if (event->AsGestureEvent()->details().scroll_y_hint() < 0)
+    return nullptr;
+
+  const gfx::Point location_in_screen =
+      event->target()->GetScreenLocation(*event);
+  const gfx::Rect work_area_bounds =
+      display::Screen::GetScreen()->GetDisplayNearestWindow(window).work_area();
+
+  gfx::Rect hit_bounds_in_screen(work_area_bounds);
+  hit_bounds_in_screen.set_height(kDragStartTopEdgeInset);
+
+  // There may be a bezel sensor off screen logically above
+  // |hit_bounds_in_screen|. Handles the ET_GESTURE_SCROLL_BEGIN event
+  // triggered in the bezel area too.
+  bool in_bezel = location_in_screen.y() < hit_bounds_in_screen.y() &&
+                  location_in_screen.x() >= hit_bounds_in_screen.x() &&
+                  location_in_screen.x() < hit_bounds_in_screen.right();
+
+  if (hit_bounds_in_screen.Contains(location_in_screen) || in_bezel)
+    return window;
+
+  return nullptr;
 }
 
 int GetHitTestComponent(Window* window, const gfx::Point& location) {
@@ -48,18 +98,18 @@
 void ClientSideWindowMoveHandler::MaybeSetupLastTarget(
     ui::LocatedEvent* event) {
   last_target_.RemoveAll();
-  Window* window = static_cast<Window*>(event->target());
-  if (!window || !window->delegate())
+
+  Window* window = GetToplevelTargetForEvent(event, &last_component_);
+  if (!window) {
+    last_component_ = HTNOWHERE;
     return;
-  int component = window->delegate()->GetNonClientComponent(event->location());
-  if (!ui::CanPerformDragOrResize(component))
-    return;
+  }
 
   last_target_.Add(window);
   last_location_ = event->location();
-  last_component_ = component;
-  UpdateWindowResizeShadow(
-      window, ui::IsResizingComponent(component) ? component : HTNOWHERE);
+  UpdateWindowResizeShadow(window, ui::IsResizingComponent(last_component_)
+                                       ? last_component_
+                                       : HTNOWHERE);
 }
 
 void ClientSideWindowMoveHandler::UpdateWindowResizeShadow(Window* window,
@@ -73,22 +123,26 @@
     ui::LocatedEvent* event,
     ws::mojom::MoveLoopSource source) {
   Window* target = static_cast<Window*>(event->target());
-  if (!target || !last_target_.Contains(target) || !target->delegate())
+  if (!last_target_.Contains(target->GetToplevelWindow()))
     return;
 
   gfx::Point screen_location = last_location_;
   auto* screen_position_client =
       aura::client::GetScreenPositionClient(target->GetRootWindow());
-  // screen_position_client may be null.in test.
+  // screen_position_client may be null in tests.
   if (screen_position_client)
     screen_position_client->ConvertPointToScreen(target, &screen_location);
+
   WindowTreeHostMus::ForWindow(target)->PerformWindowMove(
       target, source, screen_location, last_component_,
-      base::BindOnce(&WindowMoveEnded, target));
+      base::BindOnce(&ClientSideWindowMoveHandler::OnWindowMoveDone,
+                     base::Unretained(this)));
 
   // Clear |last_target_| so that event->target() won't match with
   // |last_target_| anymore.
   last_target_.RemoveAll();
+
+  dragging_window_ = target;
   event->SetHandled();
 }
 
@@ -148,10 +202,22 @@
   // TODO(mukai): create a common class in ash/public/cpp to share the logic.
   if (event->handled())
     return;
+
+  // A drag may have already been started and gesture events transferred, but
+  // in-flight gesture events may still come through, targetting the original
+  // window. These need to be set to handled so that the client area doesn't
+  // complain about unexpected scroll updates or ends.
+  if (dragging_window_) {
+    event->SetHandled();
+    return;
+  }
+
   switch (event->type()) {
-    case ui::ET_GESTURE_TAP_DOWN:
+    case ui::ET_GESTURE_SCROLL_BEGIN:
       MaybeSetupLastTarget(event);
-      return;
+      if (!last_target_.windows().empty())
+        event->SetHandled();
+      break;
 
     case ui::ET_GESTURE_SCROLL_UPDATE:
       MaybePerformWindowMove(event, ws::mojom::MoveLoopSource::TOUCH);
@@ -163,4 +229,10 @@
   }
 }
 
+void ClientSideWindowMoveHandler::OnWindowMoveDone(bool success) {
+  dragging_window_->env()->gesture_recognizer()->CancelActiveTouches(
+      dragging_window_);
+  dragging_window_ = nullptr;
+}
+
 }  // namespace aura
diff --git a/ui/aura/mus/client_side_window_move_handler.h b/ui/aura/mus/client_side_window_move_handler.h
index 107458fa..f88bf67a 100644
--- a/ui/aura/mus/client_side_window_move_handler.h
+++ b/ui/aura/mus/client_side_window_move_handler.h
@@ -43,10 +43,19 @@
   void OnMouseEvent(ui::MouseEvent* event) override;
   void OnGestureEvent(ui::GestureEvent* event) override;
 
+  void OnWindowMoveDone(bool success);
+
   Env* env_;
   WindowTreeClient* client_;
   WindowTracker last_shadow_target_;
+
+  // |last_target_| tracks the toplevel Window that will be the subject of a
+  // potential window move.
   WindowTracker last_target_;
+
+  // |dragging_window_| is the window that's currently being moved, if any.
+  Window* dragging_window_ = nullptr;
+
   gfx::Point last_location_;
   int last_component_ = HTNOWHERE;
 
diff --git a/ui/aura/mus/client_side_window_move_handler_unittest.cc b/ui/aura/mus/client_side_window_move_handler_unittest.cc
index 1a4fe5d..63135b6e 100644
--- a/ui/aura/mus/client_side_window_move_handler_unittest.cc
+++ b/ui/aura/mus/client_side_window_move_handler_unittest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "testing/gtest/include/gtest/gtest.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/aura/mus/in_flight_change.h"
 #include "ui/aura/mus/window_tree_client_test_observer.h"
 #include "ui/aura/mus/window_tree_host_mus.h"
@@ -29,10 +30,21 @@
   WindowMoveTestDelegate() = default;
   ~WindowMoveTestDelegate() override = default;
 
+  // Makes the window behave as if fullscreened: the entire window is HTCLIENT,
+  // and the flag to enable drags from the top of the screen/window is enabled.
+  void Fullscreen() {
+    fullscreen_ = true;
+    window_->SetProperty(aura::client::kGestureDragFromClientAreaTopMovesWindow,
+                         true);
+  }
+
   void set_window(Window* window) { window_ = window; }
 
  private:
   int GetNonClientComponent(const gfx::Point& point) const override {
+    if (fullscreen_)
+      return HTCLIENT;
+
     gfx::Size size = window_->bounds().size();
     if (point.y() < kBorderThickness) {
       if (point.x() < kBorderThickness)
@@ -60,6 +72,7 @@
     return HTCAPTION;
   }
 
+  bool fullscreen_ = false;
   Window* window_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(WindowMoveTestDelegate);
@@ -143,11 +156,11 @@
 
   gfx::Rect GetWindowBounds() { return test_window_->GetBoundsInScreen(); }
 
- private:
   WindowMoveTestDelegate test_delegate_;
   std::unique_ptr<Window> test_window_;
   std::unique_ptr<ui::test::EventGenerator> event_generator_;
 
+ private:
   DISALLOW_COPY_AND_ASSIGN(ClientSideWindowMoveHandlerTest);
 };
 
@@ -213,7 +226,7 @@
   EXPECT_FALSE(observer.in_window_move());
 }
 
-TEST_P(ClientSideWindowMoveHandlerTest, ClientAreaDontStartMove) {
+TEST_P(ClientSideWindowMoveHandlerTest, ClientAreaDoesntStartMove) {
   WindowMoveObserver observer(window_tree_client_impl());
 
   MoveInputTo(GetWindowBounds().CenterPoint());
@@ -222,6 +235,29 @@
   MoveInputBy(20, 20);
   EXPECT_FALSE(observer.in_window_move());
   window_tree()->AckAllChanges();
+
+  // Simulate fullscreen; the events still don't trigger a drag because the y
+  // coordinate is too great.
+  test_delegate_.Fullscreen();
+  MoveInputTo(GetWindowBounds().CenterPoint());
+
+  PressInput();
+  MoveInputBy(20, 20);
+  EXPECT_FALSE(observer.in_window_move());
+  window_tree()->AckAllChanges();
+}
+
+TEST_P(ClientSideWindowMoveHandlerTest, ClientAreaCanStartMove) {
+  WindowMoveObserver observer(window_tree_client_impl());
+  test_delegate_.Fullscreen();
+
+  MoveInputTo(GetWindowBounds().top_center());
+
+  PressInput();
+  MoveInputBy(0, 20);
+  // The window will be moved for a gesture sequence but not a mouse sequence.
+  EXPECT_NE(IsInputMouse(), observer.in_window_move());
+  window_tree()->AckAllChanges();
 }
 
 TEST_P(ClientSideWindowMoveHandlerTest, MouseExitDoesNotCancelResize) {
diff --git a/ui/aura/mus/mus_context_factory.cc b/ui/aura/mus/mus_context_factory.cc
index 066a6f3..9137e496 100644
--- a/ui/aura/mus/mus_context_factory.cc
+++ b/ui/aura/mus/mus_context_factory.cc
@@ -47,6 +47,8 @@
   // doesn't need to use GetForAcceleratedWidget().
   CHECK(window_port);
 
+  DCHECK_EQ(host->compositor(), compositor.get());
+
   scoped_refptr<viz::ContextProvider> context_provider =
       gpu_->CreateContextProvider(gpu_channel);
   // If the binding fails, then we need to return early since the compositor
@@ -79,12 +81,9 @@
     shared_worker_context_provider_factory_.reset();
   }
 
-  std::unique_ptr<cc::LayerTreeFrameSink> layer_tree_frame_sink =
-      window_port->RequestLayerTreeFrameSink(
-          std::move(context_provider),
-          std::move(shared_worker_context_provider),
-          gpu_->gpu_memory_buffer_manager());
-  compositor->SetLayerTreeFrameSink(std::move(layer_tree_frame_sink));
+  window_port->CreateLayerTreeFrameSink(
+      std::move(context_provider), std::move(shared_worker_context_provider),
+      gpu_->gpu_memory_buffer_manager());
 }
 
 void MusContextFactory::CreateLayerTreeFrameSink(
diff --git a/ui/aura/mus/window_mus.h b/ui/aura/mus/window_mus.h
index 9562f0ff..716b76b1 100644
--- a/ui/aura/mus/window_mus.h
+++ b/ui/aura/mus/window_mus.h
@@ -127,6 +127,8 @@
 
   virtual float GetDeviceScaleFactor() = 0;
 
+  virtual void DidSetWindowTreeHostBoundsFromServer() = 0;
+
  private:
   // Just for set_server_id(), which other places should not call.
   friend class WindowTreeClient;
diff --git a/ui/aura/mus/window_port_mus.cc b/ui/aura/mus/window_port_mus.cc
index 88fd879d..535fe070 100644
--- a/ui/aura/mus/window_port_mus.cc
+++ b/ui/aura/mus/window_port_mus.cc
@@ -29,6 +29,7 @@
 #include "ui/aura/mus/window_tree_client_delegate.h"
 #include "ui/aura/window_delegate.h"
 #include "ui/aura/window_observer.h"
+#include "ui/aura/window_tree_host.h"
 #include "ui/base/class_property.h"
 #include "ui/base/ui_base_features.h"
 #include "ui/base/ui_base_switches_util.h"
@@ -42,6 +43,12 @@
 static const char* kMus = "Mus";
 }  // namespace
 
+struct WindowPortMus::PendingLayerTreeFrameSinkArgs {
+  scoped_refptr<viz::ContextProvider> context_provider;
+  scoped_refptr<viz::RasterContextProvider> raster_context_provider;
+  gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager;
+};
+
 WindowPortMus::WindowMusChangeDataImpl::WindowMusChangeDataImpl() = default;
 
 WindowPortMus::WindowMusChangeDataImpl::~WindowMusChangeDataImpl() = default;
@@ -176,45 +183,31 @@
                      std::move(callback)));
 }
 
-std::unique_ptr<cc::mojo_embedder::AsyncLayerTreeFrameSink>
-WindowPortMus::RequestLayerTreeFrameSink(
+void WindowPortMus::CreateLayerTreeFrameSink(
     scoped_refptr<viz::ContextProvider> context_provider,
     scoped_refptr<viz::RasterContextProvider> raster_context_provider,
     gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) {
-  viz::mojom::CompositorFrameSinkPtrInfo sink_info;
-  viz::mojom::CompositorFrameSinkRequest sink_request =
-      mojo::MakeRequest(&sink_info);
-  viz::mojom::CompositorFrameSinkClientPtr client;
-  viz::mojom::CompositorFrameSinkClientRequest client_request =
-      mojo::MakeRequest(&client);
-
-  cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams params;
-  ui::Compositor* compositor = window_->layer()->GetCompositor();
-  DCHECK(compositor);
-  params.compositor_task_runner = compositor->task_runner();
-  params.gpu_memory_buffer_manager = gpu_memory_buffer_manager;
-  params.pipes.compositor_frame_sink_info = std::move(sink_info);
-  params.pipes.client_request = std::move(client_request);
-  bool root_accepts_events =
-      (window_->event_targeting_policy() ==
-       ws::mojom::EventTargetingPolicy::TARGET_ONLY) ||
-      (window_->event_targeting_policy() ==
-       ws::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS);
-  if (features::IsVizHitTestingDrawQuadEnabled()) {
-    params.hit_test_data_provider =
-        std::make_unique<viz::HitTestDataProviderDrawQuad>(
-            /* should_ask_for_child_regions */ false, root_accepts_events);
+  if (did_set_frame_sink_) {
+    pending_layer_tree_frame_sink_args_ =
+        std::make_unique<PendingLayerTreeFrameSinkArgs>();
+    pending_layer_tree_frame_sink_args_->context_provider =
+        std::move(context_provider);
+    pending_layer_tree_frame_sink_args_->raster_context_provider =
+        std::move(raster_context_provider);
+    pending_layer_tree_frame_sink_args_->gpu_memory_buffer_manager =
+        gpu_memory_buffer_manager;
+    window_tree_client_->RequestNewLocalSurfaceId(this);
+    return;
   }
-  params.enable_surface_synchronization = true;
-  params.client_name = kMus;
-
-  auto layer_tree_frame_sink =
-      std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
-          std::move(context_provider), std::move(raster_context_provider),
-          &params);
-  window_tree_client_->AttachCompositorFrameSink(
-      server_id(), std::move(sink_request), std::move(client));
-  return layer_tree_frame_sink;
+  // The second time a LayerTreeFrameSink is set LayerTreeHostImpl expects a
+  // new LocalSurfaceId. Request one from the server, and when received create
+  // the LayerTreeFrameSink.
+  // TODO(sky): this is temporary and can be removed once
+  // https://crbug.com/921129 is fixed.
+  did_set_frame_sink_ = true;
+  CreateLayerTreeFrameSinkImpl(std::move(context_provider),
+                               std::move(raster_context_provider),
+                               gpu_memory_buffer_manager);
 }
 
 viz::FrameSinkId WindowPortMus::GenerateFrameSinkIdFromServerId() const {
@@ -591,6 +584,16 @@
   return window_->layer()->device_scale_factor();
 }
 
+void WindowPortMus::DidSetWindowTreeHostBoundsFromServer() {
+  if (!pending_layer_tree_frame_sink_args_)
+    return;
+
+  auto args = std::move(pending_layer_tree_frame_sink_args_);
+  CreateLayerTreeFrameSinkImpl(std::move(args->context_provider),
+                               std::move(args->raster_context_provider),
+                               args->gpu_memory_buffer_manager);
+}
+
 void WindowPortMus::OnPreInit(Window* window) {
   window_ = window;
   window_tree_client_->OnWindowMusCreated(this);
@@ -802,4 +805,45 @@
   }
 }
 
+void WindowPortMus::CreateLayerTreeFrameSinkImpl(
+    scoped_refptr<viz::ContextProvider> context_provider,
+    scoped_refptr<viz::RasterContextProvider> raster_context_provider,
+    gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager) {
+  viz::mojom::CompositorFrameSinkPtrInfo sink_info;
+  viz::mojom::CompositorFrameSinkRequest sink_request =
+      mojo::MakeRequest(&sink_info);
+  viz::mojom::CompositorFrameSinkClientPtr client;
+  viz::mojom::CompositorFrameSinkClientRequest client_request =
+      mojo::MakeRequest(&client);
+
+  cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams params;
+  ui::Compositor* compositor = window_->layer()->GetCompositor();
+  DCHECK(compositor);
+  params.compositor_task_runner = compositor->task_runner();
+  params.gpu_memory_buffer_manager = gpu_memory_buffer_manager;
+  params.pipes.compositor_frame_sink_info = std::move(sink_info);
+  params.pipes.client_request = std::move(client_request);
+  bool root_accepts_events =
+      (window_->event_targeting_policy() ==
+       ws::mojom::EventTargetingPolicy::TARGET_ONLY) ||
+      (window_->event_targeting_policy() ==
+       ws::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS);
+  if (features::IsVizHitTestingDrawQuadEnabled()) {
+    params.hit_test_data_provider =
+        std::make_unique<viz::HitTestDataProviderDrawQuad>(
+            /* should_ask_for_child_regions */ false, root_accepts_events);
+  }
+  params.enable_surface_synchronization = true;
+  params.client_name = kMus;
+
+  auto layer_tree_frame_sink =
+      std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
+          std::move(context_provider), std::move(raster_context_provider),
+          &params);
+  window_tree_client_->AttachCompositorFrameSink(
+      server_id(), std::move(sink_request), std::move(client));
+  window_->GetHost()->compositor()->SetLayerTreeFrameSink(
+      std::move(layer_tree_frame_sink));
+}
+
 }  // namespace aura
diff --git a/ui/aura/mus/window_port_mus.h b/ui/aura/mus/window_port_mus.h
index adf7804..2904949 100644
--- a/ui/aura/mus/window_port_mus.h
+++ b/ui/aura/mus/window_port_mus.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -27,12 +28,6 @@
 #include "ui/gfx/geometry/rect.h"
 #include "ui/platform_window/mojo/text_input_state.mojom.h"
 
-namespace cc {
-namespace mojo_embedder {
-class AsyncLayerTreeFrameSink;
-}
-}
-
 namespace gpu {
 class GpuMemoryBufferManager;
 }
@@ -94,8 +89,10 @@
                        uint32_t flags,
                        ws::mojom::WindowTree::EmbedCallback callback);
 
-  std::unique_ptr<cc::mojo_embedder::AsyncLayerTreeFrameSink>
-  RequestLayerTreeFrameSink(
+  // First time called creates a new LayerTreeFrameSink and sets it on the
+  // Compositor. Subsequent calls delay creation until a new
+  // LocalSurfaceId has been generated by the server.
+  void CreateLayerTreeFrameSink(
       scoped_refptr<viz::ContextProvider> context_provider,
       scoped_refptr<viz::RasterContextProvider> raster_context_provider,
       gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);
@@ -178,6 +175,9 @@
     ServerChangeData data;
   };
 
+  // Used when delaying a request to CreateLayerTreeFrameSink.
+  struct PendingLayerTreeFrameSinkArgs;
+
   using ServerChanges = std::vector<ServerChange>;
 
   // Convenience for adding/removing a ScopedChange.
@@ -272,6 +272,7 @@
   void PrepareForDestroy() override;
   void NotifyEmbeddedAppDisconnected() override;
   float GetDeviceScaleFactor() override;
+  void DidSetWindowTreeHostBoundsFromServer() override;
 
   // WindowPort:
   void OnPreInit(Window* window) override;
@@ -313,6 +314,12 @@
   // capture the visibility change from |window_| and its ancestors.
   void UpdateOcclusionStateAfterVisiblityChange(bool visible);
 
+  // Creates and sets a LayerTreeFrameSink on the Compositor.
+  void CreateLayerTreeFrameSinkImpl(
+      scoped_refptr<viz::ContextProvider> context_provider,
+      scoped_refptr<viz::RasterContextProvider> raster_context_provider,
+      gpu::GpuMemoryBufferManager* gpu_memory_buffer_manager);
+
   WindowTreeClient* window_tree_client_;
 
   Window* window_ = nullptr;
@@ -354,6 +361,11 @@
   // Window Service will send back the real occlusion state later.
   base::Optional<Window::OcclusionState> occlusion_state_before_hidden_;
 
+  bool did_set_frame_sink_ = false;
+
+  std::unique_ptr<PendingLayerTreeFrameSinkArgs>
+      pending_layer_tree_frame_sink_args_;
+
   base::WeakPtrFactory<WindowPortMus> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(WindowPortMus);
diff --git a/ui/aura/mus/window_tree_client.cc b/ui/aura/mus/window_tree_client.cc
index 54578c1..fc635f48 100644
--- a/ui/aura/mus/window_tree_client.cc
+++ b/ui/aura/mus/window_tree_client.cc
@@ -229,12 +229,6 @@
   return it != windows_.end() ? it->second : nullptr;
 }
 
-void WindowTreeClient::SetCanFocus(Window* window, bool can_focus) {
-  DCHECK(tree_);
-  DCHECK(window);
-  tree_->SetCanFocus(WindowMus::Get(window)->server_id(), can_focus);
-}
-
 void WindowTreeClient::SetCursor(WindowMus* window,
                                  const ui::Cursor& old_cursor,
                                  const ui::Cursor& new_cursor) {
@@ -636,6 +630,8 @@
                          gfx::ScaleToCeiledSize(revert_bounds.size(), dsf));
     GetWindowTreeHostMus(window)->SetBoundsFromServerInPixels(
         rect, local_surface_id ? *local_surface_id : viz::LocalSurfaceId());
+    if (local_surface_id)
+      window->DidSetWindowTreeHostBoundsFromServer();
     return;
   }
 
@@ -879,6 +875,10 @@
                            transport_value_mojo);
 }
 
+void WindowTreeClient::RequestNewLocalSurfaceId(WindowMus* window) {
+  tree_->AllocateLocalSurfaceId(window->server_id());
+}
+
 std::set<Window*> WindowTreeClient::GetRoots() {
   std::set<Window*> roots;
   for (WindowMus* window : roots_)
diff --git a/ui/aura/mus/window_tree_client.h b/ui/aura/mus/window_tree_client.h
index 429be3b..6e2b375 100644
--- a/ui/aura/mus/window_tree_client.h
+++ b/ui/aura/mus/window_tree_client.h
@@ -132,7 +132,6 @@
 
   WindowMus* GetWindowByServerId(ws::Id id);
 
-  void SetCanFocus(Window* window, bool can_focus);
   void SetCanAcceptDrops(WindowMus* window, bool can_accept_drops);
   void SetEventTargetingPolicy(WindowMus* window,
                                ws::mojom::EventTargetingPolicy policy);
@@ -372,6 +371,8 @@
                                   int64_t old_value,
                                   std::unique_ptr<ui::PropertyData> data);
 
+  void RequestNewLocalSurfaceId(WindowMus* window);
+
   // Overridden from WindowTreeClient:
   void OnClientId(uint32_t client_id) override;
   void OnEmbed(
diff --git a/ui/aura/mus/window_tree_host_mus.cc b/ui/aura/mus/window_tree_host_mus.cc
index 6b8c42b..e1f578e5 100644
--- a/ui/aura/mus/window_tree_host_mus.cc
+++ b/ui/aura/mus/window_tree_host_mus.cc
@@ -127,7 +127,9 @@
   const bool are_events_in_pixels = false;
   CreateCompositor(window_mus->GenerateFrameSinkIdFromServerId(),
                    force_software_compositor, external_begin_frame_client,
-                   are_events_in_pixels);
+                   are_events_in_pixels,
+                   /* trace_environment_name */ nullptr,
+                   /* automatically_allocate_surface_ids */ false);
   gfx::AcceleratedWidget accelerated_widget;
 // We need accelerated widget numbers to be different for each window and
 // fit in the smallest sizeof(AcceleratedWidget) uint32_t has this property.
diff --git a/ui/aura/test/mus/test_window_tree.cc b/ui/aura/test/mus/test_window_tree.cc
index 98bf5a3..c789a14 100644
--- a/ui/aura/test/mus/test_window_tree.cc
+++ b/ui/aura/test/mus/test_window_tree.cc
@@ -332,8 +332,6 @@
   OnChangeReceived(change_id, WindowTreeChangeType::FOCUS);
 }
 
-void TestWindowTree::SetCanFocus(ws::Id window_id, bool can_focus) {}
-
 void TestWindowTree::SetEventTargetingPolicy(
     ws::Id window_id,
     ws::mojom::EventTargetingPolicy policy) {}
diff --git a/ui/aura/test/mus/test_window_tree.h b/ui/aura/test/mus/test_window_tree.h
index 3d15e2be..0863c99 100644
--- a/ui/aura/test/mus/test_window_tree.h
+++ b/ui/aura/test/mus/test_window_tree.h
@@ -183,6 +183,7 @@
       ws::Id window_id,
       const gfx::Rect& bounds,
       const base::Optional<viz::LocalSurfaceId>& local_surface_id) override;
+  void AllocateLocalSurfaceId(uint64_t window_id) override {}
   void SetWindowTransform(uint32_t change_id,
                           ws::Id window_id,
                           const gfx::Transform& transform) override;
@@ -245,7 +246,6 @@
                          const viz::FrameSinkId& frame_sink_id) override;
   void UnattachFrameSinkId(uint64_t window_id) override;
   void SetFocus(uint32_t change_id, ws::Id window_id) override;
-  void SetCanFocus(ws::Id window_id, bool can_focus) override;
   void SetEventTargetingPolicy(ws::Id window_id,
                                ws::mojom::EventTargetingPolicy policy) override;
   void SetCursor(uint32_t change_id,
diff --git a/ui/aura/window_tree_host.cc b/ui/aura/window_tree_host.cc
index 08ffa5e..f4cd24b4 100644
--- a/ui/aura/window_tree_host.cc
+++ b/ui/aura/window_tree_host.cc
@@ -392,7 +392,8 @@
     bool force_software_compositor,
     ui::ExternalBeginFrameClient* external_begin_frame_client,
     bool are_events_in_pixels,
-    const char* trace_environment_name) {
+    const char* trace_environment_name,
+    bool automatically_allocate_surface_ids) {
   DCHECK(window()->env());
   Env* env = window()->env();
   ui::ContextFactory* context_factory = env->context_factory();
@@ -406,7 +407,7 @@
       context_factory, context_factory_private,
       base::ThreadTaskRunnerHandle::Get(), ui::IsPixelCanvasRecordingEnabled(),
       external_begin_frame_client, force_software_compositor,
-      trace_environment_name);
+      trace_environment_name, automatically_allocate_surface_ids);
 #if defined(OS_CHROMEOS)
   compositor_->AddObserver(this);
 #endif
diff --git a/ui/aura/window_tree_host.h b/ui/aura/window_tree_host.h
index 7730ef6a..e7cb65a7 100644
--- a/ui/aura/window_tree_host.h
+++ b/ui/aura/window_tree_host.h
@@ -264,13 +264,15 @@
   // If frame_sink_id is not passed in, one will be grabbed from
   // ContextFactoryPrivate. |are_events_in_pixels| indicates if events are
   // received in pixels. If |are_events_in_pixels| is false, events are
-  // received in DIPs. See Compositor() for details on |trace_environment_name|.
+  // received in DIPs. See Compositor() for details on |trace_environment_name|
+  // and |automatically_allocate_surface_ids|.
   void CreateCompositor(
       const viz::FrameSinkId& frame_sink_id = viz::FrameSinkId(),
       bool force_software_compositor = false,
       ui::ExternalBeginFrameClient* external_begin_frame_client = nullptr,
       bool are_events_in_pixels = true,
-      const char* trace_environment_name = nullptr);
+      const char* trace_environment_name = nullptr,
+      bool automatically_allocate_surface_ids = true);
 
   void InitCompositor();
   void OnAcceleratedWidgetAvailable();
diff --git a/ui/base/BUILD.gn b/ui/base/BUILD.gn
index cc59e98..f913d76 100644
--- a/ui/base/BUILD.gn
+++ b/ui/base/BUILD.gn
@@ -249,6 +249,9 @@
     "ui_base_switches_util.h",
     "ui_base_types.cc",
     "ui_base_types.h",
+    "user_activity/user_activity_detector.cc",
+    "user_activity/user_activity_detector.h",
+    "user_activity/user_activity_observer.h",
     "view_prop.cc",
     "view_prop.h",
     "webui/i18n_source_stream.cc",
@@ -867,7 +870,9 @@
       sources += [ "ime/composition_text_util_pango_unittest.cc" ]
     }
     if (is_chromeos || use_ozone) {
-      sources += [ "ime/character_composer_unittest.cc" ]
+      sources += [
+        "ime/character_composer_unittest.cc",
+      ]
     }
   }
 
@@ -888,7 +893,6 @@
     "//ui/base:ui_data_pack",
     "//ui/base/clipboard:clipboard_test",
     "//ui/base/clipboard:clipboard_types",
-    "//ui/base/user_activity",
     "//ui/display",
     "//ui/events:events_base",
     "//ui/events:test_support",
diff --git a/ui/base/clipboard/clipboard_win.cc b/ui/base/clipboard/clipboard_win.cc
index 49654c9..17dee83 100644
--- a/ui/base/clipboard/clipboard_win.cc
+++ b/ui/base/clipboard/clipboard_win.cc
@@ -422,6 +422,9 @@
   if (!bitmap)
     return SkBitmap();
   int color_table_length = 0;
+
+  // For more information on BITMAPINFOHEADER and biBitCount definition,
+  // see https://docs.microsoft.com/en-us/previous-versions//dd183376(v=vs.85)
   switch (bitmap->bmiHeader.biBitCount) {
     case 1:
     case 4:
diff --git a/ui/base/dragdrop/os_exchange_data_provider_builder_mac.mm b/ui/base/dragdrop/os_exchange_data_provider_builder_mac.mm
index e8d108a..a115b31 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_builder_mac.mm
+++ b/ui/base/dragdrop/os_exchange_data_provider_builder_mac.mm
@@ -9,7 +9,7 @@
 namespace ui {
 
 std::unique_ptr<OSExchangeData::Provider> BuildOSExchangeDataProviderMac() {
-  return std::make_unique<OSExchangeDataProviderMac>();
+  return OSExchangeDataProviderMac::CreateProvider();
 }
 
 }  // namespace ui
diff --git a/ui/base/dragdrop/os_exchange_data_provider_mac.h b/ui/base/dragdrop/os_exchange_data_provider_mac.h
index 5ba2a69..11dff74 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_mac.h
+++ b/ui/base/dragdrop/os_exchange_data_provider_mac.h
@@ -5,6 +5,9 @@
 #ifndef UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_MAC_H_
 #define UI_BASE_DRAGDROP_OS_EXCHANGE_DATA_PROVIDER_MAC_H_
 
+#include <memory>
+
+#include "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #import "ui/base/clipboard/clipboard_util_mac.h"
@@ -18,17 +21,22 @@
 @class NSString;
 
 namespace ui {
-class UniquePasteboard;
 
 // OSExchangeData::Provider implementation for Mac.
 class UI_BASE_EXPORT OSExchangeDataProviderMac
     : public OSExchangeData::Provider {
  public:
-  OSExchangeDataProviderMac();
   ~OSExchangeDataProviderMac() override;
 
+  // Creates a stand-alone OSExchangeDataProviderMac.
+  static std::unique_ptr<OSExchangeDataProviderMac> CreateProvider();
+
+  // Creates an OSExchangeDataProviderMac object wrapping the given NSPasteboard
+  // object.
+  static std::unique_ptr<OSExchangeDataProviderMac>
+  CreateProviderWrappingPasteboard(NSPasteboard* pasteboard);
+
   // Overridden from OSExchangeData::Provider:
-  std::unique_ptr<Provider> Clone() const override;
   void MarkOriginatedFromRenderer() override;
   bool DidOriginateFromRenderer() const override;
   void SetString(const base::string16& data) override;
@@ -58,29 +66,25 @@
   NSData* GetNSDataForType(NSString* type) const;
 
   // Gets the underlying pasteboard.
-  NSPasteboard* GetPasteboard() const;
+  virtual NSPasteboard* GetPasteboard() const = 0;
 
   // Returns the union of SupportedPasteboardTypes() and the types in the
   // current pasteboard.
   NSArray* GetAvailableTypes() const;
 
-  // Creates an OSExchangeData object from the given NSPasteboard object.
-  static std::unique_ptr<OSExchangeData> CreateDataFromPasteboard(
-      NSPasteboard* pasteboard);
-
   // Returns an array of pasteboard types that can be supported by
   // OSExchangeData.
   static NSArray* SupportedPasteboardTypes();
 
- private:
-  explicit OSExchangeDataProviderMac(scoped_refptr<ui::UniquePasteboard>);
-  scoped_refptr<ui::UniquePasteboard> pasteboard_;
+ protected:
+  OSExchangeDataProviderMac();
+  OSExchangeDataProviderMac(const OSExchangeDataProviderMac&);
+  OSExchangeDataProviderMac& operator=(const OSExchangeDataProviderMac&);
 
+ private:
   // Drag image and offset data.
   gfx::ImageSkia drag_image_;
   gfx::Vector2d cursor_offset_;
-
-  DISALLOW_COPY_AND_ASSIGN(OSExchangeDataProviderMac);
 };
 
 }  // namespace ui
diff --git a/ui/base/dragdrop/os_exchange_data_provider_mac.mm b/ui/base/dragdrop/os_exchange_data_provider_mac.mm
index 68e89f0..1ba06641 100644
--- a/ui/base/dragdrop/os_exchange_data_provider_mac.mm
+++ b/ui/base/dragdrop/os_exchange_data_provider_mac.mm
@@ -22,20 +22,64 @@
 
 namespace ui {
 
-OSExchangeDataProviderMac::OSExchangeDataProviderMac()
-    : pasteboard_(new ui::UniquePasteboard) {}
+namespace {
 
+class OwningProvider : public OSExchangeDataProviderMac {
+ public:
+  OwningProvider()
+      : OSExchangeDataProviderMac(),
+        owned_pasteboard_(new ui::UniquePasteboard) {}
+  OwningProvider(const OwningProvider& provider) = default;
+
+  std::unique_ptr<OSExchangeData::Provider> Clone() const override {
+    return std::make_unique<OwningProvider>(*this);
+  }
+
+  NSPasteboard* GetPasteboard() const override {
+    return owned_pasteboard_->get();
+  }
+
+ private:
+  scoped_refptr<ui::UniquePasteboard> owned_pasteboard_;
+};
+
+class WrappingProvider : public OSExchangeDataProviderMac {
+ public:
+  WrappingProvider(NSPasteboard* pasteboard)
+      : OSExchangeDataProviderMac(), wrapped_pasteboard_([pasteboard retain]) {}
+  WrappingProvider(const WrappingProvider& provider) = default;
+
+  std::unique_ptr<OSExchangeData::Provider> Clone() const override {
+    return std::make_unique<WrappingProvider>(*this);
+  }
+
+  NSPasteboard* GetPasteboard() const override { return wrapped_pasteboard_; }
+
+ private:
+  base::scoped_nsobject<NSPasteboard> wrapped_pasteboard_;
+};
+
+}  // namespace
+
+OSExchangeDataProviderMac::OSExchangeDataProviderMac() = default;
 OSExchangeDataProviderMac::OSExchangeDataProviderMac(
-    scoped_refptr<ui::UniquePasteboard> pb)
-    : pasteboard_(pb) {}
+    const OSExchangeDataProviderMac&) = default;
+OSExchangeDataProviderMac& OSExchangeDataProviderMac::operator=(
+    const OSExchangeDataProviderMac&) = default;
 
-OSExchangeDataProviderMac::~OSExchangeDataProviderMac() {
+OSExchangeDataProviderMac::~OSExchangeDataProviderMac() = default;
+
+// static
+std::unique_ptr<OSExchangeDataProviderMac>
+OSExchangeDataProviderMac::CreateProvider() {
+  return std::make_unique<OwningProvider>();
 }
 
-std::unique_ptr<OSExchangeData::Provider>
-OSExchangeDataProviderMac::Clone() const {
-  return std::unique_ptr<OSExchangeData::Provider>(
-      new OSExchangeDataProviderMac(pasteboard_));
+// static
+std::unique_ptr<OSExchangeDataProviderMac>
+OSExchangeDataProviderMac::CreateProviderWrappingPasteboard(
+    NSPasteboard* pasteboard) {
+  return std::make_unique<WrappingProvider>(pasteboard);
 }
 
 void OSExchangeDataProviderMac::MarkOriginatedFromRenderer() {
@@ -48,8 +92,8 @@
 }
 
 void OSExchangeDataProviderMac::SetString(const base::string16& string) {
-  [pasteboard_->get() setString:base::SysUTF16ToNSString(string)
-                        forType:NSPasteboardTypeString];
+  [GetPasteboard() setString:base::SysUTF16ToNSString(string)
+                     forType:NSPasteboardTypeString];
 }
 
 void OSExchangeDataProviderMac::SetURL(const GURL& url,
@@ -57,12 +101,12 @@
   base::scoped_nsobject<NSPasteboardItem> item =
       ClipboardUtil::PasteboardItemFromUrl(base::SysUTF8ToNSString(url.spec()),
                                            base::SysUTF16ToNSString(title));
-  ui::ClipboardUtil::AddDataToPasteboard(pasteboard_->get(), item);
+  ui::ClipboardUtil::AddDataToPasteboard(GetPasteboard(), item);
 }
 
 void OSExchangeDataProviderMac::SetFilename(const base::FilePath& path) {
-  [pasteboard_->get() setPropertyList:@[ base::SysUTF8ToNSString(path.value()) ]
-                              forType:NSFilenamesPboardType];
+  [GetPasteboard() setPropertyList:@[ base::SysUTF8ToNSString(path.value()) ]
+                           forType:NSFilenamesPboardType];
 }
 
 void OSExchangeDataProviderMac::SetFilenames(
@@ -76,19 +120,19 @@
     NSString* path = base::SysUTF8ToNSString(filename.path.value());
     [paths addObject:path];
   }
-  [pasteboard_->get() setPropertyList:paths forType:NSFilenamesPboardType];
+  [GetPasteboard() setPropertyList:paths forType:NSFilenamesPboardType];
 }
 
 void OSExchangeDataProviderMac::SetPickledData(
     const ClipboardFormatType& format,
     const base::Pickle& data) {
   NSData* ns_data = [NSData dataWithBytes:data.data() length:data.size()];
-  [pasteboard_->get() setData:ns_data forType:format.ToNSString()];
+  [GetPasteboard() setData:ns_data forType:format.ToNSString()];
 }
 
 bool OSExchangeDataProviderMac::GetString(base::string16* data) const {
   DCHECK(data);
-  NSString* item = [pasteboard_->get() stringForType:NSPasteboardTypeString];
+  NSString* item = [GetPasteboard() stringForType:NSPasteboardTypeString];
   if (item) {
     *data = base::SysNSStringToUTF16(item);
     return true;
@@ -112,7 +156,7 @@
   DCHECK(url);
   DCHECK(title);
 
-  if (ui::PopulateURLAndTitleFromPasteboard(url, title, pasteboard_->get(),
+  if (ui::PopulateURLAndTitleFromPasteboard(url, title, GetPasteboard(),
                                             false)) {
     return true;
   }
@@ -138,8 +182,7 @@
 }
 
 bool OSExchangeDataProviderMac::GetFilename(base::FilePath* path) const {
-  NSArray* paths =
-      [pasteboard_->get() propertyListForType:NSFilenamesPboardType];
+  NSArray* paths = [GetPasteboard() propertyListForType:NSFilenamesPboardType];
   if ([paths count] == 0)
     return false;
 
@@ -149,8 +192,7 @@
 
 bool OSExchangeDataProviderMac::GetFilenames(
     std::vector<FileInfo>* filenames) const {
-  NSArray* paths =
-      [pasteboard_->get() propertyListForType:NSFilenamesPboardType];
+  NSArray* paths = [GetPasteboard() propertyListForType:NSFilenamesPboardType];
   if ([paths count] == 0)
     return false;
 
@@ -165,7 +207,7 @@
     const ClipboardFormatType& format,
     base::Pickle* data) const {
   DCHECK(data);
-  NSData* ns_data = [pasteboard_->get() dataForType:format.ToNSString()];
+  NSData* ns_data = [GetPasteboard() dataForType:format.ToNSString()];
   if (!ns_data)
     return false;
 
@@ -187,12 +229,12 @@
 }
 
 bool OSExchangeDataProviderMac::HasFile() const {
-  return [[pasteboard_->get() types] containsObject:NSFilenamesPboardType];
+  return [[GetPasteboard() types] containsObject:NSFilenamesPboardType];
 }
 
 bool OSExchangeDataProviderMac::HasCustomFormat(
     const ClipboardFormatType& format) const {
-  return [[pasteboard_->get() types] containsObject:format.ToNSString()];
+  return [[GetPasteboard() types] containsObject:format.ToNSString()];
 }
 
 void OSExchangeDataProviderMac::SetDragImage(
@@ -211,34 +253,18 @@
 }
 
 NSData* OSExchangeDataProviderMac::GetNSDataForType(NSString* type) const {
-  return [pasteboard_->get() dataForType:type];
-}
-
-NSPasteboard* OSExchangeDataProviderMac::GetPasteboard() const {
-  return pasteboard_->get();
+  return [GetPasteboard() dataForType:type];
 }
 
 NSArray* OSExchangeDataProviderMac::GetAvailableTypes() const {
   NSSet* supportedTypes = [NSSet setWithArray:SupportedPasteboardTypes()];
   NSMutableSet* availableTypes =
-      [NSMutableSet setWithArray:[pasteboard_->get() types]];
+      [NSMutableSet setWithArray:[GetPasteboard() types]];
   [availableTypes unionSet:supportedTypes];
   return [availableTypes allObjects];
 }
 
 // static
-std::unique_ptr<OSExchangeData>
-OSExchangeDataProviderMac::CreateDataFromPasteboard(NSPasteboard* pasteboard) {
-  OSExchangeDataProviderMac* provider = new OSExchangeDataProviderMac();
-
-  for (NSPasteboardItem* item in [pasteboard pasteboardItems])
-    ClipboardUtil::AddDataToPasteboard(provider->pasteboard_->get(), item);
-
-  return std::make_unique<OSExchangeData>(
-      base::WrapUnique<OSExchangeData::Provider>(provider));
-}
-
-// static
 NSArray* OSExchangeDataProviderMac::SupportedPasteboardTypes() {
   return @[
     kWebCustomDataPboardType, ui::ClipboardUtil::UTIForWebURLsAndTitles(),
diff --git a/ui/base/idle/BUILD.gn b/ui/base/idle/BUILD.gn
index 5311a54a..bfa6e5d 100644
--- a/ui/base/idle/BUILD.gn
+++ b/ui/base/idle/BUILD.gn
@@ -52,7 +52,6 @@
     deps += [
       "//chromeos",
       "//chromeos/dbus",
-      "//ui/base/user_activity",
     ]
     sources -= [ "idle_linux.cc" ]
   }
diff --git a/ui/base/ui_base_features.cc b/ui/base/ui_base_features.cc
index 07f73f8..70cb362 100644
--- a/ui/base/ui_base_features.cc
+++ b/ui/base/ui_base_features.cc
@@ -128,8 +128,13 @@
 const base::Feature kMashOopViz = {"MashOopViz",
                                    base::FEATURE_DISABLED_BY_DEFAULT};
 
+// Runs the window service in-process. Launch bug https://crbug.com/909816
 const base::Feature kSingleProcessMash = {"SingleProcessMash",
+#if defined(OS_CHROMEOS)
+                                          base::FEATURE_ENABLED_BY_DEFAULT};
+#else
                                           base::FEATURE_DISABLED_BY_DEFAULT};
+#endif
 
 bool IsUsingWindowService() {
   return IsSingleProcessMash() || IsMultiProcessMash();
diff --git a/ui/base/user_activity/BUILD.gn b/ui/base/user_activity/BUILD.gn
deleted file mode 100644
index b4a768b..0000000
--- a/ui/base/user_activity/BUILD.gn
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2019 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-import("//build/config/jumbo.gni")
-import("//build/config/ui.gni")
-import("//testing/test.gni")
-
-if (is_android) {
-  import("//build/config/android/rules.gni")
-} else if (is_mac) {
-  import("//build/config/mac/rules.gni")
-}
-
-jumbo_source_set("user_activity") {
-  sources = [
-    "user_activity_detector.cc",
-    "user_activity_detector.h",
-    "user_activity_observer.h",
-  ]
-
-  deps = [
-    "//ui/events:events",
-  ]
-}
diff --git a/ui/base/user_activity/user_activity_detector.h b/ui/base/user_activity/user_activity_detector.h
index a3689168..0f2a9a9e 100644
--- a/ui/base/user_activity/user_activity_detector.h
+++ b/ui/base/user_activity/user_activity_detector.h
@@ -9,6 +9,7 @@
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "base/time/time.h"
+#include "ui/base/ui_base_export.h"
 #include "ui/events/event.h"
 #include "ui/events/platform/platform_event_observer.h"
 
@@ -17,7 +18,7 @@
 class UserActivityObserver;
 
 // Watches for input events and notifies observers that the user is active.
-class UserActivityDetector : public PlatformEventObserver {
+class UI_BASE_EXPORT UserActivityDetector : public PlatformEventObserver {
  public:
   // Minimum amount of time between notifications to observers.
   static const int kNotifyIntervalMs;
diff --git a/ui/base/user_activity/user_activity_observer.h b/ui/base/user_activity/user_activity_observer.h
index 18e30dd..f6b98d21 100644
--- a/ui/base/user_activity/user_activity_observer.h
+++ b/ui/base/user_activity/user_activity_observer.h
@@ -6,6 +6,7 @@
 #define UI_BASE_USER_ACTIVITY_USER_ACTIVITY_OBSERVER_H_
 
 #include "base/macros.h"
+#include "ui/base/ui_base_export.h"
 
 namespace ui {
 class Event;
@@ -15,7 +16,7 @@
 
 // Interface for classes that want to be notified about user activity.
 // Implementations should register themselves with UserActivityDetector.
-class UserActivityObserver {
+class UI_BASE_EXPORT UserActivityObserver {
  public:
   // Invoked periodically while the user is active (i.e. generating input
   // events). |event| is the event that triggered the notification; it may
diff --git a/ui/chromeos/BUILD.gn b/ui/chromeos/BUILD.gn
index d3c1128..d1075fc 100644
--- a/ui/chromeos/BUILD.gn
+++ b/ui/chromeos/BUILD.gn
@@ -43,7 +43,6 @@
     "//ui/aura",
     "//ui/base",
     "//ui/base/ime",
-    "//ui/base/user_activity",
     "//ui/chromeos/resources",
     "//ui/chromeos/strings",
     "//ui/display",
diff --git a/ui/compositor/compositor.cc b/ui/compositor/compositor.cc
index b4639d8..ab5f0631 100644
--- a/ui/compositor/compositor.cc
+++ b/ui/compositor/compositor.cc
@@ -71,7 +71,8 @@
     bool enable_pixel_canvas,
     ui::ExternalBeginFrameClient* external_begin_frame_client,
     bool force_software_compositor,
-    const char* trace_environment_name)
+    const char* trace_environment_name,
+    bool automatically_allocate_surface_ids)
     : context_factory_(context_factory),
       context_factory_private_(context_factory_private),
       frame_sink_id_(frame_sink_id),
@@ -113,6 +114,9 @@
   // Disable edge anti-aliasing in order to increase support for HW overlays.
   settings.enable_edge_anti_aliasing = false;
 
+  settings.automatically_allocate_surface_ids =
+      automatically_allocate_surface_ids;
+
   if (command_line->HasSwitch(cc::switches::kUIShowCompositedLayerBorders)) {
     std::string layer_borders_string = command_line->GetSwitchValueASCII(
         cc::switches::kUIShowCompositedLayerBorders);
@@ -371,14 +375,9 @@
   bool device_scale_factor_changed = device_scale_factor_ != scale;
   device_scale_factor_ = scale;
 
-  if (size_ != size_in_pixel && local_surface_id_allocation.IsValid()) {
-    // A new LocalSurfaceId must be set when the compositor size changes.
-    DCHECK_NE(
-        local_surface_id_allocation.local_surface_id(),
-        host_->local_surface_id_allocation_from_parent().local_surface_id());
-    DCHECK_NE(local_surface_id_allocation,
-              host_->local_surface_id_allocation_from_parent());
-  }
+  if (size_ != size_in_pixel && local_surface_id_allocation.IsValid())
+    DCHECK_NE(local_surface_id_allocation, last_local_surface_id_allocation_);
+  last_local_surface_id_allocation_ = local_surface_id_allocation;
 
   if (!size_in_pixel.IsEmpty()) {
     bool size_changed = size_ != size_in_pixel;
diff --git a/ui/compositor/compositor.h b/ui/compositor/compositor.h
index 99f547c..2b4a2034 100644
--- a/ui/compositor/compositor.h
+++ b/ui/compositor/compositor.h
@@ -23,7 +23,7 @@
 #include "cc/trees/layer_tree_host_single_thread_client.h"
 #include "components/viz/common/frame_sinks/begin_frame_args.h"
 #include "components/viz/common/surfaces/frame_sink_id.h"
-#include "components/viz/common/surfaces/local_surface_id.h"
+#include "components/viz/common/surfaces/local_surface_id_allocation.h"
 #include "components/viz/host/host_frame_sink_client.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "third_party/skia/include/core/SkMatrix44.h"
@@ -205,6 +205,8 @@
   // |trace_environment_name| is passed to trace events so that tracing
   // can identify the environment the trace events are from. Examples are,
   // "ash", and "browser". If no value is supplied, "browser" is used.
+  // See |LayerTreeSettings::automatically_allocate_surface_ids| for details on
+  // |automatically_allocate_surface_ids|.
   Compositor(const viz::FrameSinkId& frame_sink_id,
              ui::ContextFactory* context_factory,
              ui::ContextFactoryPrivate* context_factory_private,
@@ -212,7 +214,8 @@
              bool enable_pixel_canvas,
              ExternalBeginFrameClient* external_begin_frame_client = nullptr,
              bool force_software_compositor = false,
-             const char* trace_environment_name = nullptr);
+             const char* trace_environment_name = nullptr,
+             bool automatically_allocate_surface_ids = true);
   ~Compositor() override;
 
   ui::ContextFactory* context_factory() { return context_factory_; }
@@ -504,6 +507,8 @@
 
   const char* trace_environment_name_;
 
+  viz::LocalSurfaceIdAllocation last_local_surface_id_allocation_;
+
   base::WeakPtrFactory<Compositor> context_creation_weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(Compositor);
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index 2a92165..0e3bc3285 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -961,6 +961,9 @@
 }
 
 void Layer::SendDamagedRects() {
+  if (layer_mask_)
+    layer_mask_->SendDamagedRects();
+
   if (damaged_region_.IsEmpty())
     return;
   if (!delegate_ && transfer_resource_.mailbox_holder.mailbox.IsZero())
@@ -970,8 +973,6 @@
 
   for (gfx::Rect damaged_rect : damaged_region_)
     cc_layer_->SetNeedsDisplayRect(damaged_rect);
-  if (layer_mask_)
-    layer_mask_->SendDamagedRects();
 
   if (content_layer_)
     paint_region_.Union(damaged_region_);
diff --git a/ui/compositor/layer_unittest.cc b/ui/compositor/layer_unittest.cc
index c710f7a..fdde61a 100644
--- a/ui/compositor/layer_unittest.cc
+++ b/ui/compositor/layer_unittest.cc
@@ -1277,6 +1277,25 @@
   EXPECT_EQ(bound_union, LastInvalidation());
 }
 
+// Tests that Layer::SendDamagedRects() always recurses into its mask layer, if
+// present, even if it shouldn't send its damaged regions itself.
+TEST_F(LayerWithNullDelegateTest, AlwaysSendsMaskDamagedRects) {
+  gfx::Rect bound(gfx::Rect(2, 2));
+  std::unique_ptr<Layer> mask(CreateTextureLayer(bound));
+  std::unique_ptr<Layer> root(CreateTextureRootLayer(bound));
+  root->SetMaskLayer(mask.get());
+
+  WaitForCommit();
+  EXPECT_EQ(root->damaged_region_for_testing().bounds(), gfx::Rect());
+  EXPECT_EQ(mask->damaged_region_for_testing().bounds(), gfx::Rect());
+
+  const gfx::Rect invalid_rect(gfx::Size(1, 1));
+  mask->SchedulePaint(invalid_rect);
+  EXPECT_EQ(mask->damaged_region_for_testing().bounds(), invalid_rect);
+  root->SendDamagedRects();
+  EXPECT_EQ(mask->damaged_region_for_testing().bounds(), gfx::Rect());
+}
+
 void ExpectRgba(int x, int y, SkColor expected_color, SkColor actual_color) {
   EXPECT_EQ(expected_color, actual_color)
       << "Pixel error at x=" << x << " y=" << y << "; "
diff --git a/ui/display/manager/BUILD.gn b/ui/display/manager/BUILD.gn
index 201de69..fda805f 100644
--- a/ui/display/manager/BUILD.gn
+++ b/ui/display/manager/BUILD.gn
@@ -62,7 +62,6 @@
     "//base",
     "//third_party/re2",
     "//ui/base",
-    "//ui/base/user_activity",
     "//ui/display/mojo:interfaces",
     "//ui/display/util",
     "//ui/events:platform_event",
diff --git a/ui/file_manager/audio_player/OWNERS b/ui/file_manager/audio_player/OWNERS
new file mode 100644
index 0000000..161e6197
--- /dev/null
+++ b/ui/file_manager/audio_player/OWNERS
@@ -0,0 +1 @@
+tapted@chromium.org
\ No newline at end of file
diff --git a/ui/file_manager/base/OWNERS b/ui/file_manager/base/OWNERS
new file mode 100644
index 0000000..161e6197
--- /dev/null
+++ b/ui/file_manager/base/OWNERS
@@ -0,0 +1 @@
+tapted@chromium.org
\ No newline at end of file
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
index 0677859a..3733c63 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager_commands.js
@@ -1731,21 +1731,19 @@
               console.error(
                   'Error sharing with linux: ' +
                   chrome.runtime.lastError.message);
-            } else {
-              fileManager.crostini.registerSharedPath(dir);
-              // Show toast with link to manage sharing.
-              fileManager.ui.toast.show(str('FOLDER_SHARED_WITH_CROSTINI'), {
-                text: str('MANAGE_LINUX_SHARING_BUTTON_LABEL'),
-                callback: () => {
-                  chrome.fileManagerPrivate.openSettingsSubpage(
-                      'crostini/sharedPaths');
-                  CommandHandler.recordMenuItemSelected(
-                      CommandHandler.MenuCommandsForUMA
-                          .MANAGE_LINUX_SHARING_TOAST);
-                }
-              });
             }
           });
+      // Register the share and show the 'Manage Linux sharing' toast
+      // immediately, since the container may take 10s or more to start.
+      fileManager.crostini.registerSharedPath(dir);
+      fileManager.ui.toast.show(str('FOLDER_SHARED_WITH_CROSTINI'), {
+        text: str('MANAGE_LINUX_SHARING_BUTTON_LABEL'),
+        callback: () => {
+          chrome.fileManagerPrivate.openSettingsSubpage('crostini/sharedPaths');
+          CommandHandler.recordMenuItemSelected(
+              CommandHandler.MenuCommandsForUMA.MANAGE_LINUX_SHARING_TOAST);
+        }
+      });
     }
     // Show a confirmation dialog if we are sharing the root of a volume.
     // Non-Drive volume roots are always '/'.
@@ -1788,8 +1786,34 @@
 });
 
 /**
- * Link to settings page to manage files and folders shared with crostini
- * container.
+ * Link to settings page from gear menu.  Allows the user to manage files and
+ * folders shared with the crostini container.
+ * @type {Command}
+ */
+CommandHandler.COMMANDS_['manage-linux-sharing-gear'] =
+    /** @type {Command} */ ({
+      /**
+       * @param {!Event} event Command event.
+       * @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
+       */
+      execute: function(event, fileManager) {
+        chrome.fileManagerPrivate.openSettingsSubpage('crostini/sharedPaths');
+        CommandHandler.recordMenuItemSelected(
+            CommandHandler.MenuCommandsForUMA.MANAGE_LINUX_SHARING);
+      },
+      /**
+       * @param {!Event} event Command event.
+       * @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
+       */
+      canExecute: function(event, fileManager) {
+        event.canExecute = fileManager.crostini.isEnabled();
+        event.command.setHidden(!event.canExecute);
+      }
+    });
+
+/**
+ * Link to settings page from file context menus (not gear menu).  Allows
+ * the user to manage files and folders shared with the crostini container.
  * @type {Command}
  */
 CommandHandler.COMMANDS_['manage-linux-sharing'] = /** @type {Command} */ ({
@@ -1807,7 +1831,9 @@
    * @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
    */
   canExecute: function(event, fileManager) {
-    event.canExecute = fileManager.crostini.isEnabled();
+    const entries = CommandUtil.getCommandEntries(event.target);
+    event.canExecute = entries.length === 1 && entries[0].isDirectory &&
+        fileManager.crostini.isPathShared(entries[0]);
     event.command.setHidden(!event.canExecute);
   }
 });
diff --git a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
index 4e2795c..634f718 100644
--- a/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
+++ b/ui/file_manager/file_manager/foreground/js/ui/directory_tree.js
@@ -904,8 +904,7 @@
       for (let i = 0; i < results.length; i++) {
         const entry = results[i];
         if (entry.isDirectory) {
-          // For VolumeEntry we want to display its root.
-          entries.push(util.unwrapEntry(entry));
+          entries.push(entry);
         }
       }
       readEntry();
diff --git a/ui/file_manager/file_manager/main.html b/ui/file_manager/file_manager/main.html
index 558abedf..9b88cdd 100644
--- a/ui/file_manager/file_manager/main.html
+++ b/ui/file_manager/file_manager/main.html
@@ -170,6 +170,7 @@
                label="$i18n{MANAGE_IN_DRIVE_BUTTON_LABEL}" disabled hidden>
       <command id="share-with-linux" label="$i18n{SHARE_WITH_LINUX_BUTTON_LABEL}" disabled hidden>
       <command id="manage-linux-sharing" label="$i18n{MANAGE_LINUX_SHARING_BUTTON_LABEL}" disabled hidden>
+      <command id="manage-linux-sharing-gear" label="$i18n{MANAGE_LINUX_SHARING_BUTTON_LABEL}" disabled hidden>
 
       <command id="zoom-in" shortcut="=|Ctrl">
       <command id="zoom-out" shortcut="-|Ctrl">
@@ -214,6 +215,7 @@
       <cr-menu-item command="#delete" class="hide-on-toolbar">$i18n{DELETE_BUTTON_LABEL}</cr-menu-item>
       <cr-menu-item command="#zip-selection"></cr-menu-item>
       <cr-menu-item command="#share-with-linux"></cr-menu-item>
+      <cr-menu-item command="#manage-linux-sharing"></cr-menu-item>
       <cr-menu-item command="#set-wallpaper"></cr-menu-item>
       <hr visibleif="saveas-file full-page" class="hide-on-toolbar">
       <cr-menu-item command="#new-folder" visibleif="saveas-file full-page"
@@ -228,6 +230,7 @@
       <cr-menu-item command="#rename"></cr-menu-item>
       <cr-menu-item command="#remove-folder-shortcut"></cr-menu-item>
       <cr-menu-item command="#share-with-linux"></cr-menu-item>
+      <cr-menu-item command="#manage-linux-sharing"></cr-menu-item>
     </cr-menu>
 
     <cr-menu id="directory-tree-context-menu" class="chrome-menu files-menu"
@@ -236,6 +239,7 @@
       <cr-menu-item command="#copy" visibleif="full-page"></cr-menu-item>
       <cr-menu-item command="#paste-into-folder" visibleif="full-page"></cr-menu-item>
       <cr-menu-item command="#share-with-linux"></cr-menu-item>
+      <cr-menu-item command="#manage-linux-sharing"></cr-menu-item>
       <hr visibleif="full-page">
       <cr-menu-item command="#rename"></cr-menu-item>
       <cr-menu-item command="#create-folder-shortcut"></cr-menu-item>
@@ -271,7 +275,7 @@
       <cr-menu-item id="gear-menu-toggle-hidden-android-folders"
                     command="#toggle-hidden-android-folders"></cr-menu-item>
       <cr-menu-item id="gear-menu-manage-linux-sharing"
-                    command="#manage-linux-sharing"></cr-menu-item>
+                    command="#manage-linux-sharing-gear"></cr-menu-item>
       <cr-menu-item id="gear-menu-drive-sync-settings"
                     command="#drive-sync-settings"></cr-menu-item>
       <hr id="help-separator">
@@ -302,6 +306,7 @@
              menu-item-selector="cr-menu-item, hr">
       <cr-menu-item command="#share"></cr-menu-item>
       <cr-menu-item command="#share-with-linux"></cr-menu-item>
+      <cr-menu-item command="#manage-linux-sharing"></cr-menu-item>
       <hr id="drive-share-separator">
       <hr id="more-actions-separator">
       <cr-menu-item command="#show-submenu"
diff --git a/ui/file_manager/file_manager/test/crostini_share.js b/ui/file_manager/file_manager/test/crostini_share.js
index 094a544..70dda02 100644
--- a/ui/file_manager/file_manager/test/crostini_share.js
+++ b/ui/file_manager/file_manager/test/crostini_share.js
@@ -9,6 +9,10 @@
       '[command="#share-with-linux"][hidden][disabled="disabled"]';
   const menuShareWithLinux = '#file-context-menu:not([hidden]) ' +
       '[command="#share-with-linux"]:not([hidden]):not([disabled])';
+  const menuNoManageLinuxSharing = '#file-context-menu:not([hidden]) ' +
+      '[command="#manage-linux-sharing"][hidden][disabled="disabled"]';
+  const menuManageLinuxSharing = '#file-context-menu:not([hidden]) ' +
+      '[command="#manage-linux-sharing"]:not([hidden]):not([disabled])';
   const shareWithLinux = '#file-context-menu [command="#share-with-linux"]';
   const menuShareWithLinuxDirTree =
       '#directory-tree-context-menu:not([hidden]) ' +
@@ -45,6 +49,8 @@
         return test.waitForElement(menuShareWithLinux);
       })
       .then(() => {
+        // Check 'Manage Linux sharing' is not shown in menu.
+        assertTrue(!!document.querySelector(menuNoManageLinuxSharing));
         // Click on 'Share with Linux'.
         assertTrue(test.fakeMouseClick(shareWithLinux, 'Share with Linux'));
         // Check sharePathsWithCrostini is called.
@@ -61,13 +67,15 @@
         });
       })
       .then(() => {
-
         // Right-click 'photos' directory.
         // Check 'Share with Linux' is not shown in menu.
         assertTrue(test.fakeMouseRightClick(photos), 'right-click photos');
         return test.waitForElement(menuNoShareWithLinux);
       })
       .then(() => {
+        // Check 'Manage Linux sharing' is shown in menu.
+        assertTrue(!!document.querySelector(menuManageLinuxSharing));
+
         // Share should persist when right-click > Share with Linux.
         assertTrue(sharePathsPersist);
         // Validate UMAs.
diff --git a/ui/file_manager/file_manager/test/js/strings.js b/ui/file_manager/file_manager/test/js/strings.js
index 644117f..476815c2 100644
--- a/ui/file_manager/file_manager/test/js/strings.js
+++ b/ui/file_manager/file_manager/test/js/strings.js
@@ -13,6 +13,8 @@
   'DRIVE_FS_ENABLED': false,
   'GOOGLE_DRIVE_REDEEM_URL': 'http://www.google.com/intl/en/chrome/devices' +
       '/goodies.html?utm_source=filesapp&utm_medium=banner&utm_campaign=gsg',
+  'GOOGLE_DRIVE_OVERVIEW_URL':
+      'https://support.google.com/chromebook/?p=filemanager_drive',
   'HIDE_SPACE_INFO': false,
   'UI_LOCALE': 'en_US',
   'language': 'en-US',
diff --git a/ui/file_manager/file_manager/test/scripts/create_test_main.py b/ui/file_manager/file_manager/test/scripts/create_test_main.py
index 1ec8f45..40f7162 100755
--- a/ui/file_manager/file_manager/test/scripts/create_test_main.py
+++ b/ui/file_manager/file_manager/test/scripts/create_test_main.py
@@ -93,10 +93,41 @@
       if tag not in scripts:
         scripts.append(tag)
 
+# Get strings from grdp files.  Remove any ph/ex elements before getting text.
+# Parse private_api_strings.cc to match the string name to the grdp message.
+strings = {
+    'fontFamily': 'Roboto, sans-serif',
+    'fontSize': '75%',
+    'language': 'en',
+    'textDirection': 'ltr',
+    }
+grdp_files = [
+    '../../../chrome/app/chromeos_strings.grdp',
+    '../../../chrome/app/file_manager_strings.grdp',
+    ]
+resource_bundle = {}
+for grdp in grdp_files:
+  for msg in ET.fromstring(read(grdp)):
+    for ph in msg.findall('ph'):
+      for ex in ph.findall('ex'):
+        ph.remove(ex)
+    resource_bundle[msg.attrib['name']] = ''.join(msg.itertext()).strip()
+private_api_strings = read('../../../chrome/browser/chromeos/'
+                           'file_manager/file_manager_string_util.cc')
+for m in re.finditer(r'SET_STRING\(\"(.*?)\",\s+(\w+)\);', private_api_strings):
+  strings[m.group(1)] = resource_bundle.get(m.group(2), m.group(2))
+
+
+# Substitute $i18n{} and $i18nRaw{} in template with strings from grdp files.
+def i18n(template):
+  repl = lambda x: strings.get(x.group(1), x.group())
+  return re.sub(r'\$i18n(?:Raw)?\{(.*?)\}', repl, template)
+
+# Substitute $i18n{}.
 # Update relative paths.
 # Fix link to action_link.css and text_defaults.css.
 # Fix stylesheet from extension.
-main_html = (read('main.html')
+main_html = (i18n(read('main.html'))
              .replace('chrome://resources/css/action_link.css',
                       '../../webui/resources/css/action_link.css')
              .replace('<link rel="import" '
@@ -109,15 +140,11 @@
              .split('\n'))
 
 # Fix text_defaults.css.  Copy and replace placeholders.
-text_defaults = (read('../../webui/resources/css/text_defaults.css')
-                 .replace('$i18n{textDirection}', 'ltr')
-                 .replace('$i18nRaw{fontFamily}', 'Roboto, sans-serif')
-                 .replace('$i18nRaw{fontSize}', '75%'))
+text_defaults = i18n(read('../../webui/resources/css/text_defaults.css'))
 write('test/gen/css/text_defaults.css', text_defaults)
 
 # Add scripts required for testing, and the test files (test/*.js).
-scripts.append('<!-- required for testing -->')
-scripts += ['<script src="%s%s"></script>' % (ROOT, s) for s in [
+src = [
     'test/js/chrome_api_test_impl.js',
     '../../webui/resources/js/assert.js',
     '../../webui/resources/js/cr.js',
@@ -137,8 +164,11 @@
     'foreground/js/constants.js',
     'test/js/chrome_file_manager_private_test_impl.js',
     'test/js/test_util.js',
-] + ['test/' + s for s in os.listdir(os.path.join(ROOT_SRC, 'test'))
-     if s.endswith('.js')]]
+]
+src += ['test/' + s for s in os.listdir(os.path.join(ROOT_SRC, 'test'))
+        if s.endswith('.js')]
+scripts.append('<!-- required for testing -->')
+scripts += ['<script src="%s%s"></script>' % (ROOT, s) for s in src]
 
 # Convert all includes from:
 #  * foreground/js/main_scripts.js
@@ -161,25 +191,6 @@
     "<script>var FILE_MANAGER_ROOT = '%s';</script>" % ROOT,
     ] + scripts)
 
-# Get strings from grdp files.  Remove any ph/ex elements before getting text.
-# Parse private_api_strings.cc to match the string name to the grdp message.
-strings = {}
-grdp_files = [
-    '../../../chrome/app/chromeos_strings.grdp',
-    '../../../chrome/app/file_manager_strings.grdp',
-    ]
-resource_bundle = {}
-for grdp in grdp_files:
-  for msg in ET.fromstring(read(grdp)):
-    for ph in msg.findall('ph'):
-      for ex in ph.findall('ex'):
-        ph.remove(ex)
-    resource_bundle[msg.attrib['name']] = ''.join(msg.itertext()).strip()
-private_api_strings = read('../../../chrome/browser/chromeos/'
-                           'file_manager/file_manager_string_util.cc')
-for m in re.finditer(r'SET_STRING\(\"(.*?)\",\s+(\w+)\);', private_api_strings):
-  strings[m.group(1)] = resource_bundle.get(m.group(2), m.group(2))
-
 
 def elements_path(elements_filename):
   return '="../../../../%sforeground/elements/%s' % (ROOT, elements_filename)
@@ -232,5 +243,5 @@
   main_html = replaceline(main_html, filename,
                           ['<script src="test/gen/%s"></script>' % filename])
 
-test_html = GENERATED_HTML + '\n'.join(main_html)
+test_html = GENERATED_HTML + '\n'.join(main_html).encode('utf-8')
 write('test.html', test_html)
diff --git a/ui/file_manager/gallery/OWNERS b/ui/file_manager/gallery/OWNERS
new file mode 100644
index 0000000..161e6197
--- /dev/null
+++ b/ui/file_manager/gallery/OWNERS
@@ -0,0 +1 @@
+tapted@chromium.org
\ No newline at end of file
diff --git a/ui/file_manager/integration_tests/audio_player/OWNERS b/ui/file_manager/integration_tests/audio_player/OWNERS
new file mode 100644
index 0000000..161e6197
--- /dev/null
+++ b/ui/file_manager/integration_tests/audio_player/OWNERS
@@ -0,0 +1 @@
+tapted@chromium.org
\ No newline at end of file
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 b3c1e04..aedec286 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
@@ -12,14 +12,14 @@
   const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
 
   // Add destination directory.
-  await new addEntries(['local'], [new TestEntryInfo({
-                         type: EntryType.DIRECTORY,
-                         targetPath: 'destination',
-                         lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
-                         nameText: 'destination',
-                         sizeText: '--',
-                         typeText: 'Folder'
-                       })]);
+  await addEntries(['local'], [new TestEntryInfo({
+                     type: EntryType.DIRECTORY,
+                     targetPath: 'destination',
+                     lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
+                     nameText: 'destination',
+                     sizeText: '--',
+                     typeText: 'Folder'
+                   })]);
   return appId;
 }
 
@@ -358,14 +358,14 @@
   const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
 
   // Add child-folder inside /photos/
-  await new addEntries(['local'], [new TestEntryInfo({
-                         type: EntryType.DIRECTORY,
-                         targetPath: 'photos/child-folder',
-                         lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
-                         nameText: 'child-folder',
-                         sizeText: '--',
-                         typeText: 'Folder'
-                       })]);
+  await addEntries(['local'], [new TestEntryInfo({
+                     type: EntryType.DIRECTORY,
+                     targetPath: 'photos/child-folder',
+                     lastModifiedTime: 'Jan 1, 1980, 11:59 PM',
+                     nameText: 'child-folder',
+                     sizeText: '--',
+                     typeText: 'Folder'
+                   })]);
 
   // Navigate to child folder.
   await remoteCall.navigateWithDirectoryTree(
diff --git a/ui/file_manager/integration_tests/file_manager/my_files.js b/ui/file_manager/integration_tests/file_manager/my_files.js
index 4a676fc7..f87784a0 100644
--- a/ui/file_manager/integration_tests/file_manager/my_files.js
+++ b/ui/file_manager/integration_tests/file_manager/my_files.js
@@ -9,8 +9,8 @@
     'Recent: FakeItem',
     'My files: EntryListItem',
     'Downloads: SubDirectoryItem',
-    'Play files: SubDirectoryItem',
     'Linux files: FakeItem',
+    'Play files: SubDirectoryItem',
     'Google Drive: DriveVolumeItem',
     'My Drive: SubDirectoryItem',
     'Shared with me: SubDirectoryItem',
diff --git a/ui/file_manager/integration_tests/file_manager/providers.js b/ui/file_manager/integration_tests/file_manager/providers.js
index e4c9882..21614324 100644
--- a/ui/file_manager/integration_tests/file_manager/providers.js
+++ b/ui/file_manager/integration_tests/file_manager/providers.js
@@ -130,27 +130,39 @@
 
   await confirmVolume(appId, false /* ejectExpected */);
 
-  // If multipleMounts we display the providers menu, otherwise we display the
-  // gear menu and check the "add new service" menu item.
+  // If multipleMounts we display the providers menu and check the provider is
+  // still listed.
   if (multipleMounts) {
     await showProvidersMenu(appId);
-  } else {
-    await clickGearMenu(appId);
+    var selector =
+        '#add-new-services-menu:not([hidden]) cr-menu-item:first-child ' +
+        'span';
+    result = await remoteCall.waitForElement(appId, selector);
+    chrome.test.assertEq(providerName, result.text);
+    return;
   }
 
-  // When multiple mounts are supported, then the "new service" menu item should
-  // open the providers menu. Otherwise it should go directly to
+  // If !multipleMounts and !isSmbEnabled, we open the gear menu and check the
+  // "add new service" menu item. add-new-servuces goes directly to
   // install-new-extension, however install-new-service command uses webview
   // which doesn't work in the integration tests.
-  var selector = multipleMounts ?
-      '#add-new-services-menu:not([hidden]) cr-menu-item:first-child ' +
-          'span' :
-      '#gear-menu:not([hidden]) ' +
-          'cr-menu-item[command="#install-new-extension"]';
-  result = await remoteCall.waitForElement(appId, selector);
-  if (multipleMounts) {
-    chrome.test.assertEq(providerName, result.text);
+  const isSmbEnabled = await sendTestMessage({name: 'isSmbEnabled'}) === 'true';
+  if (!isSmbEnabled) {
+    await clickGearMenu(appId);
+    var selector = '#gear-menu:not([hidden]) ' +
+        'cr-menu-item[command="#install-new-extension"]';
+    result = await remoteCall.waitForElement(appId, selector);
+    return;
   }
+
+  // If !multipleMounts but isSmbEnabled, we display the provider menu and check
+  // the provider is not listed.
+  await showProvidersMenu(appId);
+  var selector =
+      '#add-new-services-menu:not([hidden]) cr-menu-item:first-child ' +
+      'span';
+  result = await remoteCall.waitForElement(appId, selector);
+  chrome.test.assertFalse(providerName === result.text);
 }
 
 /**
@@ -165,11 +177,35 @@
   await confirmVolume(appId, true /* ejectExpected */);
   const element = await clickGearMenu(appId);
 
-  // clickGearMenu returns the "Add new service" menu item.
-  // Here we only check these attributes because the menu item calls
-  // Webstore using webview which doesn't work in the integration test.
-  chrome.test.assertEq('Install new service', element.text);
-  chrome.test.assertEq('#install-new-extension', element.attributes.command);
+  const isSmbEnabled = await sendTestMessage({name: 'isSmbEnabled'}) === 'true';
+
+  if (!isSmbEnabled) {
+    // Here we only check these attributes because the menu item calls
+    // Webstore using webview which doesn't work in the integration test.
+    chrome.test.assertEq('Install new service', element.text);
+    // Since there is no FSP provider, there should be no add-new-service
+    // sub-menu, it should instead point to CWS install-new-extension.
+    chrome.test.assertEq('#install-new-extension', element.attributes.command);
+    return;
+  }
+
+  // Since a provider is installed (here isSmbEnabled), we need to test that
+  // 'add-new-service' sub-menu does not contain the |manifest| provider.
+  chrome.test.assertTrue(isSmbEnabled);
+  chrome.test.assertEq('Add new service', element.text);
+  chrome.test.assertEq('#new-service', element.attributes.command);
+  chrome.test.assertEq(
+      '#add-new-services-menu', element.attributes['sub-menu']);
+
+  // Extract 'add-new-service' sub-menu items.
+  const selector = ['#add-new-services-menu[hidden] cr-menu-item'];
+  const submenu =
+      await remoteCall.callRemoteTestUtil('queryAllElements', appId, selector);
+
+  // Check the sub-menu do not contain the |manifest| provider.
+  chrome.test.assertEq(2, submenu.length);
+  chrome.test.assertEq('SMB file share', submenu[0].text);
+  chrome.test.assertEq('Install new service', submenu[1].text);
 }
 
 /**
diff --git a/ui/file_manager/integration_tests/gallery/OWNERS b/ui/file_manager/integration_tests/gallery/OWNERS
new file mode 100644
index 0000000..161e6197
--- /dev/null
+++ b/ui/file_manager/integration_tests/gallery/OWNERS
@@ -0,0 +1 @@
+tapted@chromium.org
\ No newline at end of file
diff --git a/ui/file_manager/integration_tests/remote_call.js b/ui/file_manager/integration_tests/remote_call.js
index 0e6a89e..4810c2d 100644
--- a/ui/file_manager/integration_tests/remote_call.js
+++ b/ui/file_manager/integration_tests/remote_call.js
@@ -40,18 +40,14 @@
  * @private
  * @return {Promise<bool>}
  */
-RemoteCall.prototype.isStepByStepEnabled_ = function() {
-  if (this.cachedStepByStepEnabled_ != null) {
-    return Promise.resolve(this.cachedStepByStepEnabled_);
+RemoteCall.prototype.isStepByStepEnabled_ = async function() {
+  if (this.cachedStepByStepEnabled_ === null) {
+    this.cachedStepByStepEnabled_ = await new Promise((fulfill) => {
+      chrome.commandLinePrivate.hasSwitch(
+          'enable-file-manager-step-by-step-tests', fulfill);
+    });
   }
-
-  return new Promise((fulfill) => {
-    chrome.commandLinePrivate.hasSwitch(
-        'enable-file-manager-step-by-step-tests', (/** bool */ result) => {
-          this.cachedStepByStepEnabled_ = result;
-          fulfill(result);
-        });
-  });
+  return this.cachedStepByStepEnabled_;
 };
 
 /**
@@ -66,44 +62,38 @@
  * @return {Promise} Promise to be fulfilled with the result of the remote
  *     utility.
  */
-RemoteCall.prototype.callRemoteTestUtil = function(
-    func, appId, args, opt_callback) {
-  return this.isStepByStepEnabled_()
-      .then((stepByStep) => {
-        if (!stepByStep) {
-          return false;
-        }
-        return new Promise((onFulfilled) => {
-          console.info('Executing: ' + func + ' on ' + appId + ' with args: ');
-          console.info(args);
-          if (window.autostep !== true) {
-            console.info('Type step() to continue...');
-            window.step = function() {
-              window.step = null;
-              onFulfilled(stepByStep);
-            };
-          } else {
-            console.info('Auto calling step() ...');
-            onFulfilled(stepByStep);
-          }
-        });
-      })
-      .then((stepByStep) => {
-        return new Promise((onFulfilled) => {
-          chrome.runtime.sendMessage(
-              this.extensionId_, {func: func, appId: appId, args: args}, {},
-              function(var_args) {
-                if (stepByStep) {
-                  console.info('Returned value:');
-                  console.info(JSON.stringify(var_args));
-                }
-                if (opt_callback) {
-                  opt_callback.apply(null, arguments);
-                }
-                onFulfilled(arguments[0]);
-              });
-        });
+RemoteCall.prototype.callRemoteTestUtil =
+    async function(func, appId, args, opt_callback) {
+  const stepByStep = await this.isStepByStepEnabled_();
+  if (stepByStep) {
+    console.info('Executing: ' + func + ' on ' + appId + ' with args: ');
+    console.info(args);
+    if (window.autostep !== true) {
+      await new Promise((onFulfilled) => {
+        console.info('Type step() to continue...');
+        window.step = function() {
+          window.step = null;
+          onFulfilled();
+        };
       });
+    } else {
+      console.info('Auto calling step() ...');
+    }
+  }
+  const response = await new Promise((onFulfilled) => {
+    chrome.runtime.sendMessage(
+        this.extensionId_, {func: func, appId: appId, args: args}, {},
+        onFulfilled);
+  });
+
+  if (stepByStep) {
+    console.info('Returned value:');
+    console.info(JSON.stringify(response));
+  }
+  if (opt_callback) {
+    opt_callback(response);
+  }
+  return response;
 };
 
 /**
@@ -113,18 +103,16 @@
  */
 RemoteCall.prototype.waitForWindow = function(windowIdPrefix) {
   var caller = getCaller();
-  return repeatUntil(function() {
-    return this.callRemoteTestUtil('getWindows', null, []).
-        then(function(windows) {
-      for (var id in windows) {
-        if (id.indexOf(windowIdPrefix) === 0) {
-          return id;
-        }
+  return repeatUntil(async () => {
+    const windows = await this.callRemoteTestUtil('getWindows', null, []);
+    for (var id in windows) {
+      if (id.indexOf(windowIdPrefix) === 0) {
+        return id;
       }
-      return pending(
-          caller, 'Window with the prefix %s is not found.', windowIdPrefix);
-    });
-  }.bind(this));
+    }
+    return pending(
+        caller, 'Window with the prefix %s is not found.', windowIdPrefix);
+  });
 };
 
 /**
@@ -134,35 +122,27 @@
  * @return {Promise} promise Promise to be fulfilled with the result (true:
  *     success, false: failed).
  */
-RemoteCall.prototype.closeWindowAndWait = function(windowId) {
+RemoteCall.prototype.closeWindowAndWait = async function(windowId) {
   var caller = getCaller();
 
   // Closes the window.
-  return this.callRemoteTestUtil('closeWindow', null, [windowId]).then(
-      function(result) {
-        // Returns false when the closing is failed.
-        if (!result) {
-          return false;
-        }
+  if (!await this.callRemoteTestUtil('closeWindow', null, [windowId])) {
+    // Returns false when the closing is failed.
+    return false;
+  }
 
-        return repeatUntil(function() {
-          return this.callRemoteTestUtil('getWindows', null, []).then(
-              function(windows) {
-                for (var id in windows) {
-                  if (id === windowId) {
-                    // Window is still available. Continues waiting.
-                    return pending(
-                        caller, 'Window with the prefix %s is not found.',
-                        windowId);
-                  }
-                }
-                // Window is not available. Closing is done successfully.
-                return true;
-              }
-          );
-        }.bind(this));
-      }.bind(this)
-  );
+  return repeatUntil(async () => {
+    const windows = await this.callRemoteTestUtil('getWindows', null, []);
+    for (var id in windows) {
+      if (id === windowId) {
+        // Window is still available. Continues waiting.
+        return pending(
+            caller, 'Window with the prefix %s is not found.', windowId);
+      }
+    }
+    // Window is not available. Closing is done successfully.
+    return true;
+  });
 };
 
 /**
@@ -173,20 +153,18 @@
  */
 RemoteCall.prototype.waitForWindowGeometry = function(windowId, width, height) {
   var caller = getCaller();
-  return repeatUntil(function() {
-    return this.callRemoteTestUtil('getWindows', null, [])
-        .then(function(windows) {
-          if (!windows[windowId]) {
-            return pending(caller, 'Window %s is not found.', windowId);
-          }
-          if (windows[windowId].outerWidth !== width ||
-              windows[windowId].outerHeight !== height) {
-            return pending(
-                caller, 'Expected window size is %j, but it is %j',
-                {width: width, height: height}, windows[windowId]);
-          }
-        });
-  }.bind(this));
+  return repeatUntil(async () => {
+    const windows = await this.callRemoteTestUtil('getWindows', null, []);
+    if (!windows[windowId]) {
+      return pending(caller, 'Window %s is not found.', windowId);
+    }
+    if (windows[windowId].outerWidth !== width ||
+        windows[windowId].outerHeight !== height) {
+      return pending(
+          caller, 'Expected window size is %j, but it is %j',
+          {width: width, height: height}, windows[windowId]);
+    }
+  });
 };
 
 /**
@@ -216,16 +194,13 @@
 RemoteCall.prototype.waitForElementStyles = function(
     windowId, query, styleNames) {
   var caller = getCaller();
-  return repeatUntil(() => {
-    return this
-        .callRemoteTestUtil(
-            'deepQueryAllElements', windowId, [query, styleNames])
-        .then(function(elements) {
-          if (elements.length > 0) {
-            return elements[0];
-          }
-          return pending(caller, 'Element %s is not found.', query);
-        });
+  return repeatUntil(async () => {
+    const elements = await this.callRemoteTestUtil(
+        'deepQueryAllElements', windowId, [query, styleNames]);
+    if (elements.length > 0) {
+      return elements[0];
+    }
+    return pending(caller, 'Element %s is not found.', query);
   });
 };
 
@@ -247,19 +222,18 @@
     funcName, windowId, expectedResult, args) {
   const caller = getCaller();
   args = args || [];
-  return repeatUntil(() => {
-    return this.callRemoteTestUtil(funcName, windowId, args).then((result) => {
-      if (typeof expectedResult === 'function' && expectedResult(result)) {
-        return result;
-      }
-      if (expectedResult === result) {
-        return result;
-      }
-      const msg = 'waitFor: Waiting for ' +
-          `${funcName} to return ${expectedResult}, ` +
-          `but got ${JSON.stringify(result)}.`;
-      return pending(caller, msg);
-    });
+  return repeatUntil(async () => {
+    const result = await this.callRemoteTestUtil(funcName, windowId, args);
+    if (typeof expectedResult === 'function' && expectedResult(result)) {
+      return result;
+    }
+    if (expectedResult === result) {
+      return result;
+    }
+    const msg = 'waitFor: Waiting for ' +
+        `${funcName} to return ${expectedResult}, ` +
+        `but got ${JSON.stringify(result)}.`;
+    return pending(caller, msg);
   });
 };
 
@@ -274,15 +248,14 @@
  */
 RemoteCall.prototype.waitForElementLost = function(windowId, query) {
   var caller = getCaller();
-  return repeatUntil(function() {
-    return this.callRemoteTestUtil('deepQueryAllElements', windowId, [query])
-        .then(function(elements) {
-          if (elements.length > 0) {
-            return pending(caller, 'Elements %j is still exists.', elements);
-          }
-          return true;
-        });
-  }.bind(this));
+  return repeatUntil(async () => {
+    const elements = await this.callRemoteTestUtil(
+        'deepQueryAllElements', windowId, [query]);
+    if (elements.length > 0) {
+      return pending(caller, 'Elements %j is still exists.', elements);
+    }
+    return true;
+  });
 };
 
 /**
@@ -300,16 +273,14 @@
  *     result.
  */
 RemoteCall.prototype.fakeKeyDown =
-    function(windowId, query, key, ctrlKey, shiftKey, altKey) {
-  var resultPromise = this.callRemoteTestUtil(
+    async function(windowId, query, key, ctrlKey, shiftKey, altKey) {
+  const result = await this.callRemoteTestUtil(
       'fakeKeyDown', windowId, [query, key, ctrlKey, shiftKey, altKey]);
-  return resultPromise.then(function(result) {
-    if (result) {
-      return true;
-    } else {
-      return Promise.reject('Fail to fake key down.');
-    }
-  });
+  if (result) {
+    return true;
+  } else {
+    throw new Error('Fail to fake key down.');
+  }
 };
 
 /**
@@ -333,14 +304,12 @@
  */
 RemoteCall.prototype.waitForAFile = function(volumeType, name) {
   var caller = getCaller();
-  return repeatUntil(function() {
-    return this.getFilesUnderVolume(volumeType, [name]).then(function(urls) {
-      if (urls.length === 1) {
-        return true;
-      }
-      return pending(caller, '"' + name + '" is not found.');
-    });
-  }.bind(this));
+  return repeatUntil(async () => {
+    if ((await this.getFilesUnderVolume(volumeType, [name])).length === 1) {
+      return true;
+    }
+    return pending(caller, '"' + name + '" is not found.');
+  });
 };
 
 /**
@@ -352,14 +321,12 @@
  *     the first element, and so on.
  * @param {Promise} Promise to be fulfilled with the clicked element.
  */
-RemoteCall.prototype.waitAndClickElement = function(windowId, query) {
-  return this.waitForElement(windowId, query).then(element => {
-    return this.callRemoteTestUtil('fakeMouseClick', windowId, [query])
-        .then((result) => {
-          chrome.test.assertTrue(result, 'mouse click failed.');
-          return element;
-        });
-  });
+RemoteCall.prototype.waitAndClickElement = async function(windowId, query) {
+  const element = await this.waitForElement(windowId, query);
+  const result =
+      await this.callRemoteTestUtil('fakeMouseClick', windowId, [query]);
+  chrome.test.assertTrue(result, 'mouse click failed.');
+  return element;
 };
 
 /**
@@ -390,34 +357,32 @@
     function(windowId, expected, opt_options) {
   var options = opt_options || {};
   var caller = getCaller();
-  return repeatUntil(function() {
-    return this.callRemoteTestUtil(
-        'getFileList', windowId, []).then(function(files) {
-      if (!options.orderCheck) {
-        files.sort();
-        expected.sort();
+  return repeatUntil(async () => {
+    const files = await this.callRemoteTestUtil('getFileList', windowId, []);
+    if (!options.orderCheck) {
+      files.sort();
+      expected.sort();
+    }
+    for (var i = 0; i < Math.min(files.length, expected.length); i++) {
+      // Change the value received from the UI to match when comparing.
+      if (options.ignoreFileSize) {
+        files[i][1] = expected[i][1];
       }
-      for (var i = 0; i < Math.min(files.length, expected.length); i++) {
-        // Change the value received from the UI to match when comparing.
-        if (options.ignoreFileSize) {
-          files[i][1] = expected[i][1];
-        }
-        if (options.ignoreLastModifiedTime) {
-          if (expected[i].length < 4) {
-            // expected sometimes doesn't include the modified time at all, so
-            // just remove from the data from UI.
-            files[i].splice(3, 1);
-          } else {
-            files[i][3] = expected[i][3];
-          }
+      if (options.ignoreLastModifiedTime) {
+        if (expected[i].length < 4) {
+          // expected sometimes doesn't include the modified time at all, so
+          // just remove from the data from UI.
+          files[i].splice(3, 1);
+        } else {
+          files[i][3] = expected[i][3];
         }
       }
-      if (!chrome.test.checkDeepEq(expected, files)) {
-        return pending(
-            caller, 'waitForFiles: expected: %j actual %j.', expected, files);
-      }
-    });
-  }.bind(this));
+    }
+    if (!chrome.test.checkDeepEq(expected, files)) {
+      return pending(
+          caller, 'waitForFiles: expected: %j actual %j.', expected, files);
+    }
+  });
 };
 
 /**
@@ -432,27 +397,21 @@
 RemoteCallFilesApp.prototype.waitForFileListChange = function(
     windowId, lengthBefore) {
   var caller = getCaller();
-  return repeatUntil(function() {
-    return this.callRemoteTestUtil(
-        'getFileList', windowId, []).then(function(files) {
-      files.sort();
-      var notReadyRows = files.filter(function(row) {
-        return row
-            .filter(function(cell) {
-              return cell == '...';
-            })
-            .length;
-      });
-      if (notReadyRows.length === 0 &&
-          files.length !== lengthBefore &&
-          files.length !== 0) {
-        return files;
-      } else {
-        return pending(
-            caller, 'The number of file is %d. Not changed.', lengthBefore);
-      }
-    });
-  }.bind(this));
+  return repeatUntil(async () => {
+    const files = await this.callRemoteTestUtil('getFileList', windowId, []);
+    files.sort();
+
+    var notReadyRows =
+        files.filter((row) => row.filter((cell) => cell == '...').length);
+
+    if (notReadyRows.length === 0 && files.length !== lengthBefore &&
+        files.length !== 0) {
+      return files;
+    } else {
+      return pending(
+          caller, 'The number of file is %d. Not changed.', lengthBefore);
+    }
+  });
 };
 
 /**
@@ -465,14 +424,13 @@
 RemoteCallFilesApp.prototype.waitUntilTaskExecutes = function(
     windowId, taskId) {
   var caller = getCaller();
-  return repeatUntil(function() {
-    return this.callRemoteTestUtil('getExecutedTasks', windowId, [])
-        .then(function(executedTasks) {
-          if (executedTasks.indexOf(taskId) === -1) {
-            return pending(caller, 'Executed task is %j', executedTasks);
-          }
-        });
-  }.bind(this));
+  return repeatUntil(async () => {
+    const executedTasks =
+        await this.callRemoteTestUtil('getExecutedTasks', windowId, []);
+    if (executedTasks.indexOf(taskId) === -1) {
+      return pending(caller, 'Executed task is %j', executedTasks);
+    }
+  });
 };
 
 /**
@@ -482,28 +440,25 @@
  *     element should have.
  * @return {Promise} Promise to be fulfilled with the result.
  */
-RemoteCallFilesApp.prototype.checkNextTabFocus = function(windowId, elementId) {
-  return remoteCall
-      .callRemoteTestUtil(
-          'fakeKeyDown', windowId, ['body', 'Tab', false, false, false])
-      .then(function(result) {
-        chrome.test.assertTrue(result);
-        return remoteCall.callRemoteTestUtil('getActiveElement', windowId, []);
-      })
-      .then(function(element) {
-        if (!element || !element.attributes['id']) {
-          return false;
-        }
+RemoteCallFilesApp.prototype.checkNextTabFocus =
+    async function(windowId, elementId) {
+  const result = await remoteCall.callRemoteTestUtil(
+      'fakeKeyDown', windowId, ['body', 'Tab', false, false, false]);
+  chrome.test.assertTrue(result);
+  const element =
+      await remoteCall.callRemoteTestUtil('getActiveElement', windowId, []);
+  if (!element || !element.attributes['id']) {
+    return false;
+  }
 
-        if (element.attributes['id'] === elementId) {
-          return true;
-        } else {
-          console.error(
-              'The ID of the element should be "' + elementId + '", but "' +
-              element.attributes['id'] + '"');
-          return false;
-        }
-      });
+  if (element.attributes['id'] === elementId) {
+    return true;
+  } else {
+    console.error(
+        'The ID of the element should be "' + elementId + '", but "' +
+        element.attributes['id'] + '"');
+    return false;
+  }
 };
 
 /**
@@ -516,15 +471,13 @@
 RemoteCallFilesApp.prototype.waitUntilCurrentDirectoryIsChanged = function(
     windowId, expectedPath) {
   var caller = getCaller();
-  return repeatUntil(function() {
-    return this.callRemoteTestUtil('getBreadcrumbPath', windowId, [])
-        .then(function(path) {
-          if (path !== expectedPath) {
-            return pending(
-                caller, 'Expected path is %s got %s', expectedPath, path);
-          }
-        });
-  }.bind(this));
+  return repeatUntil(async () => {
+    const path =
+        await this.callRemoteTestUtil('getBreadcrumbPath', windowId, []);
+    if (path !== expectedPath) {
+      return pending(caller, 'Expected path is %s got %s', expectedPath, path);
+    }
+  });
 };
 
 /**
@@ -532,37 +485,27 @@
  * @param {string} windowId Target window ID.
  * @param {string} query Query to the <tree-item> element.
  */
-RemoteCallFilesApp.prototype.expandTreeItemInDirectoryTree = function(
-    windowId, query) {
-  return this.waitForElement(windowId, query)
-      .then(() => {
-        return this.callRemoteTestUtil(
-            'queryAllElements', windowId, [`${query}[expanded]`]);
-      })
-      .then(elements => {
-        // If it's already expanded just set the focus on directory tree.
-        if (elements.length > 0) {
-          return this.callRemoteTestUtil(
-              'focus', windowId, ['#directory-tree']);
-        }
+RemoteCallFilesApp.prototype.expandTreeItemInDirectoryTree =
+    async function(windowId, query) {
+  await this.waitForElement(windowId, query);
+  const elements = await this.callRemoteTestUtil(
+      'queryAllElements', windowId, [`${query}[expanded]`]);
+  // If it's already expanded just set the focus on directory tree.
+  if (elements.length > 0) {
+    return this.callRemoteTestUtil('focus', windowId, ['#directory-tree']);
+  }
 
-        // We must wait until <tree-item> has attribute [has-children=true]
-        // otherwise it won't expand. We must also to account for the case
-        // :not([expanded]) to ensure it has NOT been expanded by some async
-        // operation since the [expanded] checks above.
-        const expandIcon = query +
-            ':not([expanded]) > .tree-row[has-children=true] > .expand-icon';
-        return this.waitAndClickElement(windowId, expandIcon)
-            .then(() => {
-              // Wait for the expansion to finish.
-              return this.waitForElement(windowId, query + '[expanded]');
-            })
-            .then(() => {
-              // Force the focus on directory tree.
-              return this.callRemoteTestUtil(
-                  'focus', windowId, ['#directory-tree']);
-            });
-      });
+  // We must wait until <tree-item> has attribute [has-children=true]
+  // otherwise it won't expand. We must also to account for the case
+  // :not([expanded]) to ensure it has NOT been expanded by some async
+  // operation since the [expanded] checks above.
+  const expandIcon =
+      query + ':not([expanded]) > .tree-row[has-children=true] > .expand-icon';
+  await this.waitAndClickElement(windowId, expandIcon);
+  // Wait for the expansion to finish.
+  await this.waitForElement(windowId, query + '[expanded]');
+  // Force the focus on directory tree.
+  await this.callRemoteTestUtil('focus', windowId, ['#directory-tree']);
 };
 
 /**
@@ -577,27 +520,23 @@
 /**
  * Internal function for expanding directory tree for specified path.
  */
-RemoteCallFilesApp.prototype.expandDirectoryTreeForInternal_ = function(
-    windowId, components, index, volumeType) {
+RemoteCallFilesApp.prototype.expandDirectoryTreeForInternal_ =
+    async function(windowId, components, index, volumeType) {
   if (index >= components.length - 1) {
-    return Promise.resolve();
+    return;
   }
 
   // First time we should expand the root/volume first.
   if (index === 0) {
-    return this.expandVolumeInDirectoryTree(windowId, volumeType).then(() => {
-      return this.expandDirectoryTreeForInternal_(
-          windowId, components, index + 1, volumeType);
-    });
+    await this.expandVolumeInDirectoryTree(windowId, volumeType);
+    return this.expandDirectoryTreeForInternal_(
+        windowId, components, index + 1, volumeType);
   }
   const path = '/' + components.slice(1, index + 1).join('/');
-  return this
-      .expandTreeItemInDirectoryTree(
-          windowId, `[full-path-for-testing="${path}"]`)
-      .then(() => {
-        return this.expandDirectoryTreeForInternal_(
-            windowId, components, index + 1, volumeType);
-      });
+  await this.expandTreeItemInDirectoryTree(
+      windowId, `[full-path-for-testing="${path}"]`);
+  await this.expandDirectoryTreeForInternal_(
+      windowId, components, index + 1, volumeType);
 };
 
 /**
@@ -621,32 +560,30 @@
  * Navigates to specified directory on the specified volume by using directory
  * tree.
  */
-RemoteCallFilesApp.prototype.navigateWithDirectoryTree = function(
-    windowId, path, rootLabel, volumeType = 'downloads') {
-  return this.expandDirectoryTreeFor(windowId, path, volumeType)
-      .then(() => {
-        // Select target path.
-        return this.callRemoteTestUtil(
-            'fakeMouseClick', windowId, [`[full-path-for-testing="${path}"]`]);
-      })
-      .then(() => {
-        // Entries within Drive starts with /root/ but it isn't displayed in the
-        // breadcrubms used by waitUntilCurrentDirectoryIsChanged.
-        path = path.replace(/^\/root/, '')
-                   .replace(/^\/team_drives/, '')
-                   .replace(/^\/Computers/, '');
+RemoteCallFilesApp.prototype.navigateWithDirectoryTree =
+    async function(windowId, path, rootLabel, volumeType = 'downloads') {
+  await this.expandDirectoryTreeFor(windowId, path, volumeType);
 
-        // TODO(lucmult): Remove this once MyFilesVolume is rolled out.
-        // Remove /Downloads duplication when MyFilesVolume is enabled.
-        if (volumeType == 'downloads' && path.startsWith('/Downloads') &&
-            rootLabel.endsWith('/Downloads')) {
-          rootLabel = rootLabel.replace('/Downloads', '');
-        }
+  // Select target path.
+  await this.callRemoteTestUtil(
+      'fakeMouseClick', windowId, [`[full-path-for-testing="${path}"]`]);
 
-        // Wait until the Files app is navigated to the path.
-        return this.waitUntilCurrentDirectoryIsChanged(
-            windowId, `/${rootLabel}${path}`);
-      });
+  // Entries within Drive starts with /root/ but it isn't displayed in the
+  // breadcrubms used by waitUntilCurrentDirectoryIsChanged.
+  path = path.replace(/^\/root/, '')
+             .replace(/^\/team_drives/, '')
+             .replace(/^\/Computers/, '');
+
+  // TODO(lucmult): Remove this once MyFilesVolume is rolled out.
+  // Remove /Downloads duplication when MyFilesVolume is enabled.
+  if (volumeType == 'downloads' && path.startsWith('/Downloads') &&
+      rootLabel.endsWith('/Downloads')) {
+    rootLabel = rootLabel.replace('/Downloads', '');
+  }
+
+  // Wait until the Files app is navigated to the path.
+  return this.waitUntilCurrentDirectoryIsChanged(
+      windowId, `/${rootLabel}${path}`);
 };
 
 /**
@@ -685,42 +622,36 @@
   }
   var caller = getCaller();
 
-  return repeatUntil(function() {
+  return repeatUntil(async () => {
     var query = '.gallery[mode="slide"] .image-container > .image';
-    return Promise
-        .all([
-          this.waitForElement(windowId, '#rename-input'),
-          this.waitForElementStyles(windowId, query, ['any'])
-        ])
-        .then(function(args) {
-          var nameBox = args[0];
-          var image = args[1];
-          var actual = {};
-          if (width && image) {
-            actual.width = image.imageWidth;
-          }
-          if (height && image) {
-            actual.height = image.imageHeight;
-          }
-          if (name && nameBox) {
-            actual.name = nameBox.value;
-          }
+    const [nameBox, image] = await Promise.all([
+      this.waitForElement(windowId, '#rename-input'),
+      this.waitForElementStyles(windowId, query, ['any'])
+    ]);
+    var actual = {};
+    if (width && image) {
+      actual.width = image.imageWidth;
+    }
+    if (height && image) {
+      actual.height = image.imageHeight;
+    }
+    if (name && nameBox) {
+      actual.name = nameBox.value;
+    }
 
-          if (!chrome.test.checkDeepEq(expected, actual)) {
-            return pending(
-                caller, 'Slide mode state, expected is %j, actual is %j.',
-                expected, actual);
-          }
-          return actual;
-        });
-  }.bind(this));
+    if (!chrome.test.checkDeepEq(expected, actual)) {
+      return pending(
+          caller, 'Slide mode state, expected is %j, actual is %j.', expected,
+          actual);
+    }
+    return actual;
+  });
 };
 
-RemoteCallGallery.prototype.changeNameAndWait = function(windowId, newName) {
-  return this.callRemoteTestUtil('changeName', windowId, [newName]
-  ).then(function() {
-    return this.waitForSlideImage(windowId, 0, 0, newName);
-  }.bind(this));
+RemoteCallGallery.prototype.changeNameAndWait =
+    async function(windowId, newName) {
+  await this.callRemoteTestUtil('changeName', windowId, [newName]);
+  return this.waitForSlideImage(windowId, 0, 0, newName);
 };
 
 /**
@@ -729,12 +660,9 @@
  * @param {AppWindow} appWindow App window.
  * @return {Promise} Promise to be fulfilled when the element appears.
  */
-RemoteCallGallery.prototype.waitForPressEnterMessage = function(appId) {
-  return this.waitForElement(appId, '.prompt-wrapper .prompt').
-      then(function(element) {
-        chrome.test.assertEq(
-            'Press Enter when done', element.text.trim());
-      });
+RemoteCallGallery.prototype.waitForPressEnterMessage = async function(appId) {
+  const element = await this.waitForElement(appId, '.prompt-wrapper .prompt');
+  chrome.test.assertEq('Press Enter when done', element.text.trim());
 };
 
 /**
diff --git a/ui/file_manager/integration_tests/test_util.js b/ui/file_manager/integration_tests/test_util.js
index f53f46e..cbced7ebd 100644
--- a/ui/file_manager/integration_tests/test_util.js
+++ b/ui/file_manager/integration_tests/test_util.js
@@ -10,11 +10,11 @@
  * the 'name' of the command, and any required or optional arguments of the
  * command, e.g.,
  *
- *   sendTestMessage({
+ *   await sendTestMessage({
  *     name: 'addEntries', // command with volume and entries arguments
  *     volume: volume,
  *     entries: entries
- *   }).then(...);
+ *   });
  *
  * @param {Object} command Test command to send. The object is converted to
  *     a JSON string prior to sending.
@@ -51,14 +51,12 @@
  * @param {function()} callback Completion callback.
  * @return {Promise} Promise to be fulfilled on completion.
  */
-function checkIfNoErrorsOccuredOnApp(app, callback) {
-  var countPromise = app.callRemoteTestUtil('getErrorCount', null, []);
-  return countPromise.then(function(count) {
-    chrome.test.assertEq(0, count, 'The error count is not 0.');
-    if (callback) {
-      callback();
-    }
-  });
+async function checkIfNoErrorsOccuredOnApp(app, callback) {
+  const count = await app.callRemoteTestUtil('getErrorCount', null, []);
+  chrome.test.assertEq(0, count, 'The error count is not 0.');
+  if (callback) {
+    callback();
+  }
 }
 
 /**
@@ -66,18 +64,19 @@
  * @param {Promise} promise Promise to add the check to.
  * @param {Array<!RemoteCall>} apps An array of RemoteCall interfaces.
  */
-function testPromiseAndApps(promise, apps) {
-  promise.then(function() {
-    return Promise.all(
-        apps.map(function(app) {
-          return new Promise(checkIfNoErrorsOccuredOnApp.bind(null, app));
-        }));
-  }).then(chrome.test.callbackPass(function() {
+async function testPromiseAndApps(promise, apps) {
+  const finished = chrome.test.callbackPass(function() {
     // The callbackPass is necessary to avoid prematurely finishing tests.
     // Don't use chrome.test.succeed() here to avoid doubled success log.
-  }), function(error) {
-    chrome.test.fail(error.stack || error);
   });
+  try {
+    await promise;
+    await Promise.all(apps.map(app => checkIfNoErrorsOccuredOnApp(app)));
+  } catch (error) {
+    chrome.test.fail(error.stack || error);
+    return;
+  }
+  finished();
 }
 
 /**
@@ -150,22 +149,19 @@
  *     checkFunction when the checkFunction reutrns a value but a pending
  *     marker.
  */
-function repeatUntil(checkFunction) {
+async function repeatUntil(checkFunction) {
   var logTime = Date.now() + LOG_INTERVAL;
-  var step = function() {
-    return Promise.resolve(checkFunction()).then(function(result) {
-      if (result instanceof pending) {
-        if (Date.now() > logTime) {
-          console.warn(result.message);
-          logTime += LOG_INTERVAL;
-        }
-        return wait(REPEAT_UNTIL_INTERVAL).then(step);
-      } else {
-        return result;
-      }
-    });
-  };
-  return step();
+  while (true) {
+    const result = await checkFunction();
+    if (!(result instanceof pending)) {
+      return result;
+    }
+    if (Date.now() > logTime) {
+      console.warn(result.message);
+      logTime += LOG_INTERVAL;
+    }
+    await wait(REPEAT_UNTIL_INTERVAL);
+  }
 }
 
 /**
@@ -176,28 +172,28 @@
  * @param {function(string)} callback Completion callback.
  * @param {Object=} opt_debug If truthy, log the result.
  */
-function sendBrowserTestCommand(command, callback, opt_debug) {
+async function sendBrowserTestCommand(command, callback, opt_debug) {
   const caller = getCaller();
   if (typeof command.name !== 'string') {
     chrome.test.fail('Invalid test command: ' + JSON.stringify(command));
   }
-  repeatUntil(function sendTestCommand() {
+  const result = await repeatUntil(async () => {
     const tryAgain = pending(caller, 'Sent BrowserTest ' + command.name);
-    return sendTestMessage(command)
-        .then((result) => {
-          if (typeof result !== 'string') {
-            return tryAgain;
-          }
-          if (opt_debug) {
-            console.log('BrowserTest ' + command.name + ': ' + result);
-          }
-          callback(result);
-        })
-        .catch((error) => {
-          console.log(error.stack || error);
-          return tryAgain;
-        });
+    try {
+      const result = await sendTestMessage(command);
+      if (typeof result !== 'string') {
+        return tryAgain;
+      }
+      return result;
+    } catch (error) {
+      console.log(error.stack || error);
+      return tryAgain;
+    }
   });
+  if (opt_debug) {
+    console.log('BrowserTest ' + command.name + ': ' + result);
+  }
+  callback(result);
 }
 
 /**
@@ -209,13 +205,12 @@
 function waitForAppWindow(windowUrl) {
   const caller = getCaller();
   const command = {'name': 'getAppWindowId', 'windowUrl': windowUrl};
-  return repeatUntil(function() {
-    return sendTestMessage(command).then((result) => {
-      if (result == 'none') {
-        return pending(caller, 'getAppWindowId ' + windowUrl);
-      }
-      return result;
-    });
+  return repeatUntil(async () => {
+    const result = await sendTestMessage(command);
+    if (result == 'none') {
+      return pending(caller, 'getAppWindowId ' + windowUrl);
+    }
+    return result;
   });
 }
 
@@ -229,13 +224,11 @@
 function waitForAppWindowCount(appId, expectedCount) {
   const caller = getCaller();
   const command = {'name': 'countAppWindows', 'appId': appId};
-  return repeatUntil(function() {
-    return sendTestMessage(command).then((result) => {
-      if (result != expectedCount) {
-        return pending(caller, 'waitForAppWindowCount ' + appId + ' ' + result);
-      }
-      return true;
-    });
+  return repeatUntil(async () => {
+    if (await sendTestMessage(command) != expectedCount) {
+      return pending(caller, 'waitForAppWindowCount ' + appId + ' ' + result);
+    }
+    return true;
   });
 }
 
@@ -247,7 +240,7 @@
  *     result of function. The argument is true on success.
  * @return {Promise} Promise to be fulfilled when the entries are added.
  */
-function addEntries(volumeNames, entries, opt_callback) {
+async function addEntries(volumeNames, entries, opt_callback) {
   if (volumeNames.length == 0) {
     callback(true);
     return;
@@ -259,12 +252,16 @@
       entries: entries
     });
   });
-  var resultPromise = Promise.all(volumeResultPromises);
-  if (opt_callback) {
-    resultPromise.then(opt_callback.bind(null, true),
-                       opt_callback.bind(null, false));
+  if (!opt_callback) {
+    return volumeResultPromises;
   }
-  return resultPromise;
+  try {
+    await Promise.all(volumeResultPromises);
+  } catch (error) {
+    opt_callback(false);
+    throw error;
+  }
+  opt_callback(true);
 }
 
 /**
diff --git a/ui/file_manager/integration_tests/video_player/OWNERS b/ui/file_manager/integration_tests/video_player/OWNERS
new file mode 100644
index 0000000..161e6197
--- /dev/null
+++ b/ui/file_manager/integration_tests/video_player/OWNERS
@@ -0,0 +1 @@
+tapted@chromium.org
\ No newline at end of file
diff --git a/ui/file_manager/video_player/OWNERS b/ui/file_manager/video_player/OWNERS
index f199533..161e6197 100644
--- a/ui/file_manager/video_player/OWNERS
+++ b/ui/file_manager/video_player/OWNERS
@@ -1 +1 @@
-yoshiki@chromium.org
\ No newline at end of file
+tapted@chromium.org
\ No newline at end of file
diff --git a/ui/gfx/platform_font_win.cc b/ui/gfx/platform_font_win.cc
index a904ec3..4623463 100644
--- a/ui/gfx/platform_font_win.cc
+++ b/ui/gfx/platform_font_win.cc
@@ -29,6 +29,7 @@
 #include "base/win/scoped_select_object.h"
 #include "base/win/win_client_metrics.h"
 #include "third_party/skia/include/core/SkFontLCDConfig.h"
+#include "third_party/skia/include/core/SkFontMetrics.h"
 #include "third_party/skia/include/core/SkRefCnt.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 #include "ui/gfx/canvas.h"
diff --git a/ui/gfx/win/direct_write.cc b/ui/gfx/win/direct_write.cc
index 56d0d3c..bc38edd 100644
--- a/ui/gfx/win/direct_write.cc
+++ b/ui/gfx/win/direct_write.cc
@@ -11,7 +11,7 @@
 #include "base/metrics/field_trial.h"
 #include "base/win/registry.h"
 #include "base/win/windows_version.h"
-#include "skia/ext/fontmgr_default_win.h"
+#include "skia/ext/fontmgr_default.h"
 #include "third_party/skia/include/core/SkFontMgr.h"
 #include "third_party/skia/include/ports/SkTypeface_win.h"
 #include "ui/gfx/platform_font_win.h"
@@ -59,7 +59,7 @@
       SkFontMgr_New_DirectWrite(factory.Get());
   if (!direct_write_font_mgr)
     return;
-  SetDefaultSkiaFactory(std::move(direct_write_font_mgr));
+  skia::OverrideDefaultSkFontMgr(std::move(direct_write_font_mgr));
   gfx::PlatformFontWin::SetDirectWriteFactory(factory.Get());
 }
 
diff --git a/ui/gl/android/surface_texture.cc b/ui/gl/android/surface_texture.cc
index a781d502..5c6f425 100644
--- a/ui/gl/android/surface_texture.cc
+++ b/ui/gl/android/surface_texture.cc
@@ -5,6 +5,7 @@
 #include "ui/gl/android/surface_texture.h"
 
 #include <android/native_window_jni.h>
+#include <utility>
 
 #include "base/android/jni_android.h"
 #include "base/logging.h"
@@ -31,19 +32,22 @@
   Java_SurfaceTexturePlatformWrapper_destroy(env, j_surface_texture_);
 }
 
-void SurfaceTexture::SetFrameAvailableCallback(const base::Closure& callback) {
+void SurfaceTexture::SetFrameAvailableCallback(
+    base::RepeatingClosure callback) {
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_SurfaceTexturePlatformWrapper_setFrameAvailableCallback(
       env, j_surface_texture_,
-      reinterpret_cast<intptr_t>(new SurfaceTextureListener(callback, false)));
+      reinterpret_cast<intptr_t>(
+          new SurfaceTextureListener(std::move(callback), false)));
 }
 
 void SurfaceTexture::SetFrameAvailableCallbackOnAnyThread(
-    const base::Closure& callback) {
+    base::RepeatingClosure callback) {
   JNIEnv* env = base::android::AttachCurrentThread();
   Java_SurfaceTexturePlatformWrapper_setFrameAvailableCallback(
       env, j_surface_texture_,
-      reinterpret_cast<intptr_t>(new SurfaceTextureListener(callback, true)));
+      reinterpret_cast<intptr_t>(
+          new SurfaceTextureListener(std::move(callback), true)));
 }
 
 void SurfaceTexture::UpdateTexImage() {
diff --git a/ui/gl/android/surface_texture.h b/ui/gl/android/surface_texture.h
index 451fc751..b2ad7af 100644
--- a/ui/gl/android/surface_texture.h
+++ b/ui/gl/android/surface_texture.h
@@ -29,13 +29,13 @@
   // Note: Since callbacks come in from Java objects that might outlive objects
   // being referenced from the callback, the only robust way here is to create
   // the callback from a weak pointer to your object.
-  void SetFrameAvailableCallback(const base::Closure& callback);
+  void SetFrameAvailableCallback(base::RepeatingClosure callback);
 
   // Set the listener callback, but allow it to be invoked on any thread.  The
   // same caveats apply as SetFrameAvailableCallback, plus whatever other issues
   // show up due to multithreading (e.g., don't bind the Closure to a method
   // via a weak ref).
-  void SetFrameAvailableCallbackOnAnyThread(const base::Closure& callback);
+  void SetFrameAvailableCallbackOnAnyThread(base::RepeatingClosure callback);
 
   // Update the texture image to the most recent frame from the image stream.
   void UpdateTexImage();
diff --git a/ui/gl/android/surface_texture_listener.cc b/ui/gl/android/surface_texture_listener.cc
index 091facc..74be484 100644
--- a/ui/gl/android/surface_texture_listener.cc
+++ b/ui/gl/android/surface_texture_listener.cc
@@ -4,6 +4,8 @@
 
 #include "ui/gl/android/surface_texture_listener.h"
 
+#include <utility>
+
 #include "base/location.h"
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
@@ -13,9 +15,9 @@
 
 namespace gl {
 
-SurfaceTextureListener::SurfaceTextureListener(const base::Closure& callback,
+SurfaceTextureListener::SurfaceTextureListener(base::RepeatingClosure callback,
                                                bool use_any_thread)
-    : callback_(callback),
+    : callback_(std::move(callback)),
       browser_loop_(base::ThreadTaskRunnerHandle::Get()),
       use_any_thread_(use_any_thread) {}
 
diff --git a/ui/gl/android/surface_texture_listener.h b/ui/gl/android/surface_texture_listener.h
index d022185..bedfb33 100644
--- a/ui/gl/android/surface_texture_listener.h
+++ b/ui/gl/android/surface_texture_listener.h
@@ -38,12 +38,12 @@
   // If use_any_thread is true, then the FrameAvailable callback will happen
   // on whatever thread calls us.  Otherwise, we will call it back on the same
   // thread that was used to construct us.
-  SurfaceTextureListener(const base::Closure& callback, bool use_any_thread);
+  SurfaceTextureListener(base::RepeatingClosure callback, bool use_any_thread);
   ~SurfaceTextureListener();
 
   friend class SurfaceTexture;
 
-  base::Closure callback_;
+  base::RepeatingClosure callback_;
 
   scoped_refptr<base::SingleThreadTaskRunner> browser_loop_;
 
diff --git a/ui/gl/gl_context_cgl.cc b/ui/gl/gl_context_cgl.cc
index 1ee50da..a4072b6e1 100644
--- a/ui/gl/gl_context_cgl.cc
+++ b/ui/gl/gl_context_cgl.cc
@@ -159,7 +159,8 @@
       // Delay releasing the pixel format for 10 seconds to reduce the number of
       // unnecessary GPU switches.
       base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-          FROM_HERE, base::Bind(&CGLReleasePixelFormat, discrete_pixelformat_),
+          FROM_HERE,
+          base::BindOnce(&CGLReleasePixelFormat, discrete_pixelformat_),
           base::TimeDelta::FromSeconds(10));
     } else {
       CGLReleasePixelFormat(discrete_pixelformat_);
diff --git a/ui/gl/gl_switches.cc b/ui/gl/gl_switches.cc
index 5c5d44a..490cdd8 100644
--- a/ui/gl/gl_switches.cc
+++ b/ui/gl/gl_switches.cc
@@ -137,12 +137,6 @@
 
 namespace features {
 
-// Allow putting a video swapchain underneath the main swapchain, so overlays
-// can be used even if there are controls on top of the video. This requires
-// the DirectCompositionOverlays feature to be enabled.
-const base::Feature kDirectCompositionUnderlays{
-    "DirectCompositionUnderlays", base::FEATURE_DISABLED_BY_DEFAULT};
-
 // Allow putting content with complex transforms (e.g. rotations) into an
 // overlay.
 const base::Feature kDirectCompositionComplexOverlays{
diff --git a/ui/gl/gl_switches.h b/ui/gl/gl_switches.h
index 2772ec8..755d0a2 100644
--- a/ui/gl/gl_switches.h
+++ b/ui/gl/gl_switches.h
@@ -69,7 +69,6 @@
 
 namespace features {
 
-GL_EXPORT extern const base::Feature kDirectCompositionUnderlays;
 GL_EXPORT extern const base::Feature kDirectCompositionComplexOverlays;
 GL_EXPORT extern const base::Feature kDirectCompositionNonrootOverlays;
 GL_EXPORT extern const base::Feature kDefaultANGLEOpenGL;
diff --git a/ui/gl/gpu_timing.cc b/ui/gl/gpu_timing.cc
index dbaf95e1..78a06f7 100644
--- a/ui/gl/gpu_timing.cc
+++ b/ui/gl/gpu_timing.cc
@@ -4,6 +4,8 @@
 
 #include "ui/gl/gpu_timing.h"
 
+#include <utility>
+
 #include "base/containers/circular_deque.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
@@ -30,7 +32,7 @@
 
 class GPUTimingImpl : public GPUTiming {
  public:
-   GPUTimingImpl(GLContextReal* context);
+  explicit GPUTimingImpl(GLContextReal* context);
   ~GPUTimingImpl() override;
 
   void ForceTimeElapsedQuery() { force_time_elapsed_query_ = true; }
@@ -51,8 +53,8 @@
            ? (base::TimeTicks::Now() - base::TimeTicks()).InMicroseconds()
            : cpu_time_for_testing_.Run();
   }
-  void SetCpuTimeForTesting(const base::Callback<int64_t(void)>& cpu_time) {
-    cpu_time_for_testing_ = cpu_time;
+  void SetCpuTimeForTesting(base::RepeatingCallback<int64_t(void)> cpu_time) {
+    cpu_time_for_testing_ = std::move(cpu_time);
   }
 
   void UpdateQueryResults();
@@ -75,7 +77,7 @@
  private:
   scoped_refptr<GPUTimingClient> CreateGPUTimingClient() override;
 
-  base::Callback<int64_t(void)> cpu_time_for_testing_;
+  base::RepeatingCallback<int64_t(void)> cpu_time_for_testing_;
   GPUTiming::TimerType timer_type_ = GPUTiming::kTimerTypeInvalid;
   uint32_t disjoint_counter_ = 0;
   int64_t offset_ = 0;  // offset cache when timer_type_ == kTimerTypeARB
@@ -84,7 +86,7 @@
   int32_t timestamp_bit_count_gl_ = -1;  // gl implementation timestamp bits
 
   uint32_t next_timer_query_id_ = 0;
-  uint32_t next_good_timer_query_id_ = 0; // identify bad ids for disjoints.
+  uint32_t next_good_timer_query_id_ = 0;  // identify bad ids for disjoints.
   uint32_t query_disjoint_count_ = 0;
 
   // Extra state tracking data for elapsed timer queries.
@@ -122,7 +124,7 @@
 
 class TimerQuery : public base::RefCounted<TimerQuery> {
  public:
-  TimerQuery(uint32_t next_id);
+  explicit TimerQuery(uint32_t next_id);
   virtual void Destroy() = 0;
 
   // Returns true when UpdateQueryResults() is ready to be called.
@@ -135,7 +137,7 @@
   virtual void PrepareNextUpdate(scoped_refptr<TimerQuery> prev) {}
 
   uint32_t timer_query_id_ = 0;
-  int64_t time_stamp_ = 0; // Timestamp of the query, could be estimated.
+  int64_t time_stamp_ = 0;  // Timestamp of the query, could be estimated.
 
  protected:
   friend class base::RefCounted<TimerQuery>;
@@ -271,8 +273,7 @@
 
 class TimeStampTimerQuery : public TimerQuery {
  public:
-  TimeStampTimerQuery(uint32_t next_id)
-      : TimerQuery(next_id) {
+  explicit TimeStampTimerQuery(uint32_t next_id) : TimerQuery(next_id) {
     glGenQueries(1, &gl_query_id_);
   }
 
@@ -649,9 +650,9 @@
 }
 
 void GPUTimingClient::SetCpuTimeForTesting(
-    const base::Callback<int64_t(void)>& cpu_time) {
+    base::RepeatingCallback<int64_t(void)> cpu_time) {
   DCHECK(gpu_timing_);
-  gpu_timing_->SetCpuTimeForTesting(cpu_time);
+  gpu_timing_->SetCpuTimeForTesting(std::move(cpu_time));
 }
 
 bool GPUTimingClient::IsForceTimeElapsedQuery() {
diff --git a/ui/gl/gpu_timing.h b/ui/gl/gpu_timing.h
index 561f8a7..e9b7fcd 100644
--- a/ui/gl/gpu_timing.h
+++ b/ui/gl/gpu_timing.h
@@ -141,7 +141,7 @@
   bool CheckAndResetTimerErrors();
 
   int64_t GetCurrentCPUTime();
-  void SetCpuTimeForTesting(const base::Callback<int64_t(void)>& cpu_time);
+  void SetCpuTimeForTesting(base::RepeatingCallback<int64_t(void)> cpu_time);
 
   bool IsForceTimeElapsedQuery();
   void ForceTimeElapsedQuery();
diff --git a/ui/gl/gpu_timing_unittest.cc b/ui/gl/gpu_timing_unittest.cc
index 0eb14c8..2384ce2 100644
--- a/ui/gl/gpu_timing_unittest.cc
+++ b/ui/gl/gpu_timing_unittest.cc
@@ -71,7 +71,8 @@
 
     scoped_refptr<GPUTimingClient> client = context_->CreateGPUTimingClient();
     if (!cpu_time_bounded_) {
-      client->SetCpuTimeForTesting(base::Bind(&GPUTimingFake::GetFakeCPUTime));
+      client->SetCpuTimeForTesting(
+          base::BindRepeating(&GPUTimingFake::GetFakeCPUTime));
       cpu_time_bounded_ = true;
     }
     return client;
@@ -93,7 +94,7 @@
   gpu_timing_fake_queries_.SetCurrentCPUTime(123);
   EXPECT_EQ(123, gpu_timing_client->GetCurrentCPUTime());
 
-  base::Callback<int64_t(void)> empty;
+  base::RepeatingCallback<int64_t(void)> empty;
   gpu_timing_client->SetCpuTimeForTesting(empty);
   EXPECT_NE(123, gpu_timing_client->GetCurrentCPUTime());
 }
diff --git a/ui/ozone/platform/wayland/BUILD.gn b/ui/ozone/platform/wayland/BUILD.gn
index e19abd7..1644e69 100644
--- a/ui/ozone/platform/wayland/BUILD.gn
+++ b/ui/ozone/platform/wayland/BUILD.gn
@@ -153,8 +153,6 @@
   testonly = true
 
   sources = [
-    "fake_server.cc",
-    "fake_server.h",
     "test/constants.h",
     "test/global_object.cc",
     "test/global_object.h",
@@ -200,6 +198,8 @@
     "test/test_seat.h",
     "test/test_touch.cc",
     "test/test_touch.h",
+    "test/test_wayland_server_thread.cc",
+    "test/test_wayland_server_thread.h",
     "test/test_zwp_text_input_manager.cc",
     "test/test_zwp_text_input_manager.h",
   ]
diff --git a/ui/ozone/platform/wayland/fake_server.cc b/ui/ozone/platform/wayland/fake_server.cc
deleted file mode 100644
index 61410c37..0000000
--- a/ui/ozone/platform/wayland/fake_server.cc
+++ /dev/null
@@ -1,124 +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 "ui/ozone/platform/wayland/fake_server.h"
-
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <wayland-server.h>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_file.h"
-#include "base/run_loop.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/task_runner_util.h"
-
-namespace wl {
-
-void DisplayDeleter::operator()(wl_display* display) {
-  wl_display_destroy(display);
-}
-
-FakeServer::FakeServer()
-    : Thread("fake_wayland_server"),
-      pause_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
-                   base::WaitableEvent::InitialState::NOT_SIGNALED),
-      resume_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
-                    base::WaitableEvent::InitialState::NOT_SIGNALED),
-      controller_(FROM_HERE) {}
-
-FakeServer::~FakeServer() {
-  Resume();
-  Stop();
-}
-
-bool FakeServer::Start(uint32_t shell_version) {
-  display_.reset(wl_display_create());
-  if (!display_)
-    return false;
-  event_loop_ = wl_display_get_event_loop(display_.get());
-
-  int fd[2];
-  if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fd) < 0)
-    return false;
-  base::ScopedFD server_fd(fd[0]);
-  base::ScopedFD client_fd(fd[1]);
-
-  // If client has not specified rect before, user standard ones.
-  if (output_.GetRect().IsEmpty())
-    output_.SetRect(gfx::Rect(0, 0, 800, 600));
-
-  if (wl_display_init_shm(display_.get()) < 0)
-    return false;
-  if (!compositor_.Initialize(display_.get()))
-    return false;
-  if (!output_.Initialize(display_.get()))
-    return false;
-  if (!data_device_manager_.Initialize(display_.get()))
-    return false;
-  if (!seat_.Initialize(display_.get()))
-    return false;
-  if (shell_version == 5) {
-    if (!xdg_shell_.Initialize(display_.get()))
-      return false;
-  } else {
-    if (!zxdg_shell_v6_.Initialize(display_.get()))
-      return false;
-  }
-  if (!zwp_text_input_manager_v1_.Initialize(display_.get()))
-    return false;
-  if (!zwp_linux_dmabuf_v1_.Initialize(display_.get()))
-    return false;
-
-  client_ = wl_client_create(display_.get(), server_fd.get());
-  if (!client_)
-    return false;
-  (void)server_fd.release();
-
-  base::Thread::Options options;
-  options.message_pump_factory = base::BindRepeating(
-      &FakeServer::CreateMessagePump, base::Unretained(this));
-  if (!base::Thread::StartWithOptions(options))
-    return false;
-
-  setenv("WAYLAND_SOCKET", base::UintToString(client_fd.release()).c_str(), 1);
-
-  return true;
-}
-
-void FakeServer::Pause() {
-  task_runner()->PostTask(
-      FROM_HERE, base::BindOnce(&FakeServer::DoPause, base::Unretained(this)));
-  pause_event_.Wait();
-}
-
-void FakeServer::Resume() {
-  if (display_)
-    wl_display_flush_clients(display_.get());
-  resume_event_.Signal();
-}
-
-void FakeServer::DoPause() {
-  base::RunLoop().RunUntilIdle();
-  pause_event_.Signal();
-  resume_event_.Wait();
-}
-
-std::unique_ptr<base::MessagePump> FakeServer::CreateMessagePump() {
-  auto pump = std::make_unique<base::MessagePumpLibevent>();
-  pump->WatchFileDescriptor(wl_event_loop_get_fd(event_loop_), true,
-                            base::MessagePumpLibevent::WATCH_READ, &controller_,
-                            this);
-  return std::move(pump);
-}
-
-void FakeServer::OnFileCanReadWithoutBlocking(int fd) {
-  wl_event_loop_dispatch(event_loop_, 0);
-  wl_display_flush_clients(display_.get());
-}
-
-void FakeServer::OnFileCanWriteWithoutBlocking(int fd) {}
-
-}  // namespace wl
diff --git a/ui/ozone/platform/wayland/fake_server.h b/ui/ozone/platform/wayland/fake_server.h
deleted file mode 100644
index 11a4ebe0..0000000
--- a/ui/ozone/platform/wayland/fake_server.h
+++ /dev/null
@@ -1,116 +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 UI_OZONE_PLATFORM_WAYLAND_FAKE_SERVER_H_
-#define UI_OZONE_PLATFORM_WAYLAND_FAKE_SERVER_H_
-
-#include <memory>
-#include <vector>
-
-#include <wayland-server-core.h>
-
-#include "base/message_loop/message_pump_libevent.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
-#include "ui/ozone/platform/wayland/test/global_object.h"
-#include "ui/ozone/platform/wayland/test/mock_xdg_shell.h"
-#include "ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h"
-#include "ui/ozone/platform/wayland/test/test_compositor.h"
-#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
-#include "ui/ozone/platform/wayland/test/test_output.h"
-#include "ui/ozone/platform/wayland/test/test_seat.h"
-#include "ui/ozone/platform/wayland/test/test_zwp_text_input_manager.h"
-
-struct wl_client;
-struct wl_display;
-struct wl_event_loop;
-struct wl_resource;
-
-namespace wl {
-
-struct DisplayDeleter {
-  void operator()(wl_display* display);
-};
-
-class FakeServer : public base::Thread, base::MessagePumpLibevent::FdWatcher {
- public:
-  FakeServer();
-  ~FakeServer() override;
-
-  // Start the fake Wayland server. If this succeeds, the WAYLAND_SOCKET
-  // environment variable will be set to the string representation of a file
-  // descriptor that a client can connect to. The caller is responsible for
-  // ensuring that this file descriptor gets closed (for example, by calling
-  // wl_display_connect). Start instantiates an xdg_shell version 5 or 6
-  // according to |shell_version| passed.
-  bool Start(uint32_t shell_version);
-
-  // Pause the server when it becomes idle.
-  void Pause();
-
-  // Resume the server after flushing client connections.
-  void Resume();
-
-  template <typename T>
-  T* GetObject(uint32_t id) {
-    wl_resource* resource = wl_client_get_object(client_, id);
-    return resource ? T::FromResource(resource) : nullptr;
-  }
-
-  TestOutput* CreateAndInitializeOutput() {
-    auto output = std::make_unique<TestOutput>();
-    output->Initialize(display());
-
-    TestOutput* output_ptr = output.get();
-    globals_.push_back(std::move(output));
-    return output_ptr;
-  }
-
-  TestDataDeviceManager* data_device_manager() { return &data_device_manager_; }
-  TestSeat* seat() { return &seat_; }
-  MockXdgShell* xdg_shell() { return &xdg_shell_; }
-  TestOutput* output() { return &output_; }
-  TestZwpTextInputManagerV1* text_input_manager_v1() {
-    return &zwp_text_input_manager_v1_;
-  }
-  MockZwpLinuxDmabufV1* zwp_linux_dmabuf_v1() { return &zwp_linux_dmabuf_v1_; }
-
-  wl_display* display() const { return display_.get(); }
-
- private:
-  void DoPause();
-
-  std::unique_ptr<base::MessagePump> CreateMessagePump();
-
-  // base::MessagePumpLibevent::FdWatcher
-  void OnFileCanReadWithoutBlocking(int fd) override;
-  void OnFileCanWriteWithoutBlocking(int fd) override;
-
-  std::unique_ptr<wl_display, DisplayDeleter> display_;
-  wl_client* client_ = nullptr;
-  wl_event_loop* event_loop_ = nullptr;
-
-  base::WaitableEvent pause_event_;
-  base::WaitableEvent resume_event_;
-
-  // Represent Wayland global objects
-  TestCompositor compositor_;
-  TestDataDeviceManager data_device_manager_;
-  TestOutput output_;
-  TestSeat seat_;
-  MockXdgShell xdg_shell_;
-  MockZxdgShellV6 zxdg_shell_v6_;
-  TestZwpTextInputManagerV1 zwp_text_input_manager_v1_;
-  MockZwpLinuxDmabufV1 zwp_linux_dmabuf_v1_;
-
-  std::vector<std::unique_ptr<GlobalObject>> globals_;
-
-  base::MessagePumpLibevent::FdWatchController controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(FakeServer);
-};
-
-}  // namespace wl
-
-#endif  // UI_OZONE_PLATFORM_WAYLAND_FAKE_SERVER_H_
diff --git a/ui/ozone/platform/wayland/test/mock_xdg_shell.cc b/ui/ozone/platform/wayland/test/mock_xdg_shell.cc
index e009d3cc..a3cb6653 100644
--- a/ui/ozone/platform/wayland/test/mock_xdg_shell.cc
+++ b/ui/ozone/platform/wayland/test/mock_xdg_shell.cc
@@ -72,15 +72,9 @@
     return;
   }
 
-  wl_resource* popup_resource = wl_resource_create(
-      client, &xdg_popup_interface, wl_resource_get_version(resource), id);
-  if (!popup_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  SetImplementation(popup_resource, &kXdgPopupImpl,
-                    std::make_unique<MockXdgPopup>(popup_resource));
+  CreateResourceWithImpl<MockXdgPopup>(client, &xdg_popup_interface,
+                                       wl_resource_get_version(resource),
+                                       &kXdgPopupImpl, id);
 }
 
 void Pong(wl_client* client, wl_resource* resource, uint32_t serial) {
@@ -90,16 +84,9 @@
 void CreatePositioner(wl_client* client,
                       struct wl_resource* resource,
                       uint32_t id) {
-  wl_resource* positioner_resource =
-      wl_resource_create(client, &zxdg_positioner_v6_interface,
-                         wl_resource_get_version(resource), id);
-  if (!positioner_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  SetImplementation(positioner_resource, &kTestZxdgPositionerV6Impl,
-                    std::make_unique<TestPositioner>(positioner_resource));
+  CreateResourceWithImpl<TestPositioner>(client, &zxdg_positioner_v6_interface,
+                                         wl_resource_get_version(resource),
+                                         &kTestZxdgPositionerV6Impl, id);
 }
 
 void GetXdgSurfaceV6(wl_client* client,
diff --git a/ui/ozone/platform/wayland/test/mock_xdg_surface.cc b/ui/ozone/platform/wayland/test/mock_xdg_surface.cc
index 5d853cc..8797a94c 100644
--- a/ui/ozone/platform/wayland/test/mock_xdg_surface.cc
+++ b/ui/ozone/platform/wayland/test/mock_xdg_surface.cc
@@ -112,15 +112,9 @@
     return;
   }
 
-  wl_resource* popup_resource = wl_resource_create(
-      client, &zxdg_popup_v6_interface, wl_resource_get_version(resource), id);
-  if (!popup_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  SetImplementation(popup_resource, &kZxdgPopupV6Impl,
-                    std::make_unique<MockXdgPopup>(popup_resource));
+  CreateResourceWithImpl<MockXdgPopup>(client, &zxdg_popup_v6_interface,
+                                       wl_resource_get_version(resource),
+                                       &kZxdgPopupV6Impl, id);
 }
 
 const struct xdg_surface_interface kMockXdgSurfaceImpl = {
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc
index e740c8c..4e48727 100644
--- a/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_buffer_params.cc
@@ -26,24 +26,23 @@
 }
 
 void Create(wl_client* client,
-            wl_resource* resource,
+            wl_resource* buffer_params_resource,
             int32_t width,
             int32_t height,
             uint32_t format,
             uint32_t flags) {
-  auto* buffer_params = GetUserDataAs<MockZwpLinuxBufferParamsV1>(resource);
+  auto* buffer_params =
+      GetUserDataAs<MockZwpLinuxBufferParamsV1>(buffer_params_resource);
 
-  wl_resource* buffer_resource =
-      wl_resource_create(client, &wl_buffer_interface, 1, 0);
+  wl_resource* buffer_resource = CreateResourceWithImpl<MockBuffer>(
+      client, &wl_buffer_interface, 1, &kMockWlBufferImpl, 0,
+      std::move(buffer_params->fds_));
 
-  SetImplementation(buffer_resource, &kMockWlBufferImpl,
-                    std::make_unique<MockBuffer>(
-                        buffer_resource, std::move(buffer_params->fds_)));
+  zwp_linux_buffer_params_v1_send_created(buffer_params_resource,
+                                          buffer_resource);
 
-  zwp_linux_buffer_params_v1_send_created(resource, buffer_resource);
-
-  GetUserDataAs<MockZwpLinuxBufferParamsV1>(resource)->Create(
-      client, resource, width, height, format, flags);
+  buffer_params->Create(client, buffer_params_resource, width, height, format,
+                        flags);
 }
 
 void CreateImmed(wl_client* client,
diff --git a/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc
index 79a852c..b82975d 100644
--- a/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc
+++ b/ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.cc
@@ -17,12 +17,9 @@
 constexpr uint32_t kLinuxDmabufVersion = 1;
 
 void CreateParams(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* buffer_params_resource =
-      wl_resource_create(client, &zwp_linux_buffer_params_v1_interface,
-                         wl_resource_get_version(resource), id);
-  SetImplementation(
-      buffer_params_resource, &kMockZwpLinuxBufferParamsV1Impl,
-      std::make_unique<MockZwpLinuxBufferParamsV1>(buffer_params_resource));
+  CreateResourceWithImpl<MockZwpLinuxBufferParamsV1>(
+      client, &zwp_linux_buffer_params_v1_interface,
+      wl_resource_get_version(resource), &kMockZwpLinuxBufferParamsV1Impl, id);
 
   GetUserDataAs<MockZwpLinuxDmabufV1>(resource)->CreateParams(client, resource,
                                                               id);
diff --git a/ui/ozone/platform/wayland/test/server_object.h b/ui/ozone/platform/wayland/test/server_object.h
index 5d670d48..1f4d9cdd 100644
--- a/ui/ozone/platform/wayland/test/server_object.h
+++ b/ui/ozone/platform/wayland/test/server_object.h
@@ -6,6 +6,8 @@
 #define UI_OZONE_PLATFORM_WAYLAND_TEST_SERVER_OBJECT_H_
 
 #include <memory>
+#include <type_traits>
+#include <utility>
 
 #include "base/macros.h"
 
@@ -40,8 +42,10 @@
 template <class T>
 std::unique_ptr<T> TakeUserDataAs(wl_resource* resource) {
   std::unique_ptr<T> user_data(GetUserDataAs<T>(resource));
-  // Make sure ServerObject doesn't try to destroy the resource twice.
-  ServerObject::OnResourceDestroyed(resource);
+  if (std::is_base_of<ServerObject, T>::value) {
+    // Make sure ServerObject doesn't try to destroy the resource twice.
+    ServerObject::OnResourceDestroyed(resource);
+  }
   wl_resource_set_user_data(resource, nullptr);
   return user_data;
 }
@@ -59,6 +63,24 @@
                                  DestroyUserData<T>);
 }
 
+template <typename ImplClass, typename... ImplArgs>
+wl_resource* CreateResourceWithImpl(wl_client* client,
+                                    const struct wl_interface* interface,
+                                    int version,
+                                    const void* implementation,
+                                    uint32_t id,
+                                    ImplArgs&&... impl_args) {
+  wl_resource* resource = wl_resource_create(client, interface, version, id);
+  if (!resource) {
+    wl_client_post_no_memory(client);
+    return nullptr;
+  }
+  SetImplementation(resource, implementation,
+                    std::make_unique<ImplClass>(
+                        resource, std::forward<ImplArgs&&>(impl_args)...));
+  return resource;
+}
+
 // Does not transfer ownership of the user_data.  Use with caution.  The only
 // legitimate purpose is setting more than one implementation to the same user
 // data.
@@ -66,6 +88,8 @@
 void SetImplementationUnretained(wl_resource* resource,
                                  const void* implementation,
                                  T* user_data) {
+  static_assert(std::is_base_of<ServerObject, T>::value,
+                "Only types derived from ServerObject are supported!");
   wl_resource_set_implementation(resource, implementation, user_data,
                                  &ServerObject::OnResourceDestroyed);
 }
diff --git a/ui/ozone/platform/wayland/test/test_compositor.cc b/ui/ozone/platform/wayland/test/test_compositor.cc
index 0d22217..6345232d 100644
--- a/ui/ozone/platform/wayland/test/test_compositor.cc
+++ b/ui/ozone/platform/wayland/test/test_compositor.cc
@@ -17,18 +17,14 @@
 
 constexpr uint32_t kCompositorVersion = 4;
 
-void CreateSurface(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* surface_resource = wl_resource_create(
-      client, &wl_surface_interface, wl_resource_get_version(resource), id);
-  if (!surface_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  SetImplementation(surface_resource, &kMockSurfaceImpl,
-                    std::make_unique<MockSurface>(surface_resource));
-
-  auto* compositor = GetUserDataAs<TestCompositor>(resource);
-  compositor->AddSurface(GetUserDataAs<MockSurface>(surface_resource));
+void CreateSurface(wl_client* client,
+                   wl_resource* compositor_resource,
+                   uint32_t id) {
+  wl_resource* resource = CreateResourceWithImpl<MockSurface>(
+      client, &wl_surface_interface,
+      wl_resource_get_version(compositor_resource), &kMockSurfaceImpl, id);
+  GetUserDataAs<TestCompositor>(compositor_resource)
+      ->AddSurface(GetUserDataAs<MockSurface>(resource));
 }
 
 void CreateRegion(wl_client* client, wl_resource* resource, uint32_t id) {
diff --git a/ui/ozone/platform/wayland/test/test_data_device.cc b/ui/ozone/platform/wayland/test/test_data_device.cc
index 11b5807568..2c6d2ad 100644
--- a/ui/ozone/platform/wayland/test/test_data_device.cc
+++ b/ui/ozone/platform/wayland/test/test_data_device.cc
@@ -40,7 +40,7 @@
 const struct wl_data_device_interface kTestDataDeviceImpl = {
     &DataDeviceStartDrag, &DataDeviceSetSelection, &DataDeviceRelease};
 
-TestDataDevice::TestDataDevice(wl_client* client, wl_resource* resource)
+TestDataDevice::TestDataDevice(wl_resource* resource, wl_client* client)
     : ServerObject(resource), client_(client) {}
 
 TestDataDevice::~TestDataDevice() {}
@@ -51,11 +51,9 @@
 }
 
 TestDataOffer* TestDataDevice::OnDataOffer() {
-  wl_resource* data_offer_resource =
-      wl_resource_create(client_, &wl_data_offer_interface,
-                         wl_resource_get_version(resource()), 0);
-  SetImplementation(data_offer_resource, &kTestDataOfferImpl,
-                    std::make_unique<TestDataOffer>(data_offer_resource));
+  wl_resource* data_offer_resource = CreateResourceWithImpl<TestDataOffer>(
+      client_, &wl_data_offer_interface, wl_resource_get_version(resource()),
+      &kTestDataOfferImpl, 0);
   data_offer_ = GetUserDataAs<TestDataOffer>(data_offer_resource);
   wl_data_device_send_data_offer(resource(), data_offer_resource);
 
diff --git a/ui/ozone/platform/wayland/test/test_data_device.h b/ui/ozone/platform/wayland/test/test_data_device.h
index 91a90c1..f4f275e 100644
--- a/ui/ozone/platform/wayland/test/test_data_device.h
+++ b/ui/ozone/platform/wayland/test/test_data_device.h
@@ -22,7 +22,7 @@
 
 class TestDataDevice : public ServerObject {
  public:
-  TestDataDevice(wl_client* client, wl_resource* resource);
+  TestDataDevice(wl_resource* resource, wl_client* client);
   ~TestDataDevice() override;
 
   void SetSelection(TestDataSource* data_source, uint32_t serial);
diff --git a/ui/ozone/platform/wayland/test/test_data_device_manager.cc b/ui/ozone/platform/wayland/test/test_data_device_manager.cc
index 2ae86abb..c26f4f4 100644
--- a/ui/ozone/platform/wayland/test/test_data_device_manager.cc
+++ b/ui/ozone/platform/wayland/test/test_data_device_manager.cc
@@ -16,39 +16,23 @@
 constexpr uint32_t kDataDeviceManagerVersion = 3;
 
 void CreateDataSource(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* data_source_resource = wl_resource_create(
-      client, &wl_data_source_interface, wl_resource_get_version(resource), id);
-  if (!data_source_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  SetImplementation(data_source_resource, &kTestDataSourceImpl,
-                    std::make_unique<TestDataSource>(data_source_resource));
-
-  auto* data_device_manager = GetUserDataAs<TestDataDeviceManager>(resource);
-  data_device_manager->set_data_source(
+  wl_resource* data_source_resource = CreateResourceWithImpl<TestDataSource>(
+      client, &wl_data_source_interface, wl_resource_get_version(resource),
+      &kTestDataSourceImpl, id);
+  GetUserDataAs<TestDataDeviceManager>(resource)->set_data_source(
       GetUserDataAs<TestDataSource>(data_source_resource));
 }
 
 void GetDataDevice(wl_client* client,
-                   wl_resource* resource,
+                   wl_resource* data_device_manager_resource,
                    uint32_t id,
                    wl_resource* seat_resource) {
-  wl_resource* data_device_resource = wl_resource_create(
-      client, &wl_data_device_interface, wl_resource_get_version(resource), id);
-  if (!data_device_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-
-  SetImplementation(
-      data_device_resource, &kTestDataDeviceImpl,
-      std::make_unique<TestDataDevice>(client, data_device_resource));
-
-  auto* data_device_manager = GetUserDataAs<TestDataDeviceManager>(resource);
-  data_device_manager->set_data_device(
-      GetUserDataAs<TestDataDevice>(data_device_resource));
+  wl_resource* resource = CreateResourceWithImpl<TestDataDevice>(
+      client, &wl_data_device_interface,
+      wl_resource_get_version(data_device_manager_resource),
+      &kTestDataDeviceImpl, id, client);
+  GetUserDataAs<TestDataDeviceManager>(data_device_manager_resource)
+      ->set_data_device(GetUserDataAs<TestDataDevice>(resource));
 }
 
 }  // namespace
diff --git a/ui/ozone/platform/wayland/test/test_data_offer.cc b/ui/ozone/platform/wayland/test/test_data_offer.cc
index 43fbdbb8..f2d86fab1 100644
--- a/ui/ozone/platform/wayland/test/test_data_offer.cc
+++ b/ui/ozone/platform/wayland/test/test_data_offer.cc
@@ -5,10 +5,14 @@
 #include "ui/ozone/platform/wayland/test/test_data_offer.h"
 
 #include <wayland-server-core.h>
+#include <utility>
 
 #include "base/bind.h"
 #include "base/files/file_util.h"
 #include "base/logging.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
 #include "ui/ozone/platform/wayland/test/constants.h"
 
 namespace wl {
@@ -58,12 +62,9 @@
 
 TestDataOffer::TestDataOffer(wl_resource* resource)
     : ServerObject(resource),
-      io_thread_("Worker thread"),
-      write_data_weak_ptr_factory_(this) {
-  base::Thread::Options options;
-  options.message_loop_type = base::MessageLoop::TYPE_IO;
-  io_thread_.StartWithOptions(options);
-}
+      task_runner_(
+          base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
+      write_data_weak_ptr_factory_(this) {}
 
 TestDataOffer::~TestDataOffer() {}
 
@@ -75,9 +76,8 @@
   else if (mime_type == kTextMimeTypeText)
     text_data = kSampleTextForDragAndDrop;
 
-  io_thread_.task_runner()->PostTask(
-      FROM_HERE,
-      base::BindOnce(&WriteDataOnWorkerThread, std::move(fd), text_data));
+  task_runner_->PostTask(FROM_HERE, base::BindOnce(&WriteDataOnWorkerThread,
+                                                   std::move(fd), text_data));
 }
 
 void TestDataOffer::OnOffer(const std::string& mime_type) {
diff --git a/ui/ozone/platform/wayland/test/test_data_offer.h b/ui/ozone/platform/wayland/test/test_data_offer.h
index 54bc5239..7595e4e 100644
--- a/ui/ozone/platform/wayland/test/test_data_offer.h
+++ b/ui/ozone/platform/wayland/test/test_data_offer.h
@@ -5,6 +5,7 @@
 #ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_OFFER_H_
 #define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_OFFER_H_
 
+#include <memory>
 #include <string>
 
 #include <wayland-server-protocol-core.h>
@@ -17,6 +18,10 @@
 
 struct wl_resource;
 
+namespace base {
+class SequencedTaskRunner;
+}
+
 namespace wl {
 
 extern const struct wl_data_offer_interface kTestDataOfferImpl;
@@ -30,9 +35,10 @@
   void OnOffer(const std::string& mime_type);
 
  private:
-  // TODO(adunaev): get rid of this in favor of using a task runner.
-  base::Thread io_thread_;
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
   base::WeakPtrFactory<TestDataOffer> write_data_weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestDataOffer);
 };
 
 }  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_data_source.cc b/ui/ozone/platform/wayland/test/test_data_source.cc
index a6d15fee..657ce29 100644
--- a/ui/ozone/platform/wayland/test/test_data_source.cc
+++ b/ui/ozone/platform/wayland/test/test_data_source.cc
@@ -10,6 +10,9 @@
 #include "base/files/file_util.h"
 #include "base/files/scoped_file.h"
 #include "base/logging.h"
+#include "base/sequenced_task_runner.h"
+#include "base/task/post_task.h"
+#include "base/task/task_traits.h"
 #include "base/task_runner_util.h"
 #include "ui/ozone/platform/wayland/test/constants.h"
 
@@ -66,12 +69,9 @@
 
 TestDataSource::TestDataSource(wl_resource* resource)
     : ServerObject(resource),
-      io_thread_("Worker thread"),
-      read_data_weak_ptr_factory_(this) {
-  base::Thread::Options options;
-  options.message_loop_type = base::MessageLoop::TYPE_IO;
-  io_thread_.StartWithOptions(options);
-}
+      task_runner_(
+          base::CreateSequencedTaskRunnerWithTraits({base::MayBlock()})),
+      read_data_weak_ptr_factory_(this) {}
 
 TestDataSource::~TestDataSource() {}
 
@@ -86,8 +86,8 @@
 
   wl_data_source_send_send(resource(), kTextMimeTypeUtf8, write_fd.get());
 
-  base::PostTaskAndReplyWithResult(
-      io_thread_.task_runner().get(), FROM_HERE,
+  PostTaskAndReplyWithResult(
+      task_runner_.get(), FROM_HERE,
       base::BindOnce(&ReadDataOnWorkerThread, std::move(read_fd)),
       base::BindOnce(&TestDataSource::DataReadCb,
                      read_data_weak_ptr_factory_.GetWeakPtr(),
diff --git a/ui/ozone/platform/wayland/test/test_data_source.h b/ui/ozone/platform/wayland/test/test_data_source.h
index ff9467c..4d62962 100644
--- a/ui/ozone/platform/wayland/test/test_data_source.h
+++ b/ui/ozone/platform/wayland/test/test_data_source.h
@@ -5,6 +5,7 @@
 #ifndef UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_SOURCE_H_
 #define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_DATA_SOURCE_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -12,11 +13,14 @@
 
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
-#include "base/threading/thread.h"
 #include "ui/ozone/platform/wayland/test/server_object.h"
 
 struct wl_resource;
 
+namespace base {
+class SequencedTaskRunner;
+}
+
 namespace wl {
 
 extern const struct wl_data_source_interface kTestDataSourceImpl;
@@ -37,7 +41,7 @@
  private:
   void DataReadCb(ReadDataCallback callback, const std::vector<uint8_t>& data);
 
-  base::Thread io_thread_;
+  const scoped_refptr<base::SequencedTaskRunner> task_runner_;
   base::WeakPtrFactory<TestDataSource> read_data_weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(TestDataSource);
diff --git a/ui/ozone/platform/wayland/test/test_seat.cc b/ui/ozone/platform/wayland/test/test_seat.cc
index 2f2071a..b67dfdd 100644
--- a/ui/ozone/platform/wayland/test/test_seat.cc
+++ b/ui/ozone/platform/wayland/test/test_seat.cc
@@ -15,45 +15,27 @@
 constexpr uint32_t kSeatVersion = 4;
 
 void GetPointer(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* pointer_resource = wl_resource_create(
-      client, &wl_pointer_interface, wl_resource_get_version(resource), id);
-  if (!pointer_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  SetImplementation(pointer_resource, &kTestPointerImpl,
-                    std::make_unique<TestPointer>(pointer_resource));
-
-  auto* seat = GetUserDataAs<TestSeat>(resource);
-  seat->set_pointer(GetUserDataAs<TestPointer>(pointer_resource));
+  wl_resource* pointer_resource = CreateResourceWithImpl<TestPointer>(
+      client, &wl_pointer_interface, wl_resource_get_version(resource),
+      &kTestPointerImpl, id);
+  GetUserDataAs<TestSeat>(resource)->set_pointer(
+      GetUserDataAs<TestPointer>(pointer_resource));
 }
 
 void GetKeyboard(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* keyboard_resource = wl_resource_create(
-      client, &wl_keyboard_interface, wl_resource_get_version(resource), id);
-  if (!keyboard_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  SetImplementation(keyboard_resource, &kTestKeyboardImpl,
-                    std::make_unique<TestKeyboard>(keyboard_resource));
-
-  auto* seat = GetUserDataAs<TestSeat>(resource);
-  seat->set_keyboard(GetUserDataAs<TestKeyboard>(keyboard_resource));
+  wl_resource* keyboard_resource = CreateResourceWithImpl<TestKeyboard>(
+      client, &wl_keyboard_interface, wl_resource_get_version(resource),
+      &kTestKeyboardImpl, id);
+  GetUserDataAs<TestSeat>(resource)->set_keyboard(
+      GetUserDataAs<TestKeyboard>(keyboard_resource));
 }
 
 void GetTouch(wl_client* client, wl_resource* resource, uint32_t id) {
-  wl_resource* touch_resource = wl_resource_create(
-      client, &wl_touch_interface, wl_resource_get_version(resource), id);
-  if (!touch_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  SetImplementation(touch_resource, &kTestTouchImpl,
-                    std::make_unique<TestTouch>(touch_resource));
-
-  auto* seat = GetUserDataAs<TestSeat>(resource);
-  seat->set_touch(GetUserDataAs<TestTouch>(touch_resource));
+  wl_resource* touch_resource = CreateResourceWithImpl<TestTouch>(
+      client, &wl_touch_interface, wl_resource_get_version(resource),
+      &kTestTouchImpl, id);
+  GetUserDataAs<TestSeat>(resource)->set_touch(
+      GetUserDataAs<TestTouch>(touch_resource));
 }
 
 }  // namespace
diff --git a/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc b/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc
new file mode 100644
index 0000000..fa008a00
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_wayland_server_thread.cc
@@ -0,0 +1,127 @@
+// 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 "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <wayland-server.h>
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/task_runner_util.h"
+
+namespace wl {
+
+void DisplayDeleter::operator()(wl_display* display) {
+  wl_display_destroy(display);
+}
+
+TestWaylandServerThread::TestWaylandServerThread()
+    : Thread("test_wayland_server"),
+      pause_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                   base::WaitableEvent::InitialState::NOT_SIGNALED),
+      resume_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
+                    base::WaitableEvent::InitialState::NOT_SIGNALED),
+      controller_(FROM_HERE) {}
+
+TestWaylandServerThread::~TestWaylandServerThread() {
+  Resume();
+  Stop();
+}
+
+bool TestWaylandServerThread::Start(uint32_t shell_version) {
+  display_.reset(wl_display_create());
+  if (!display_)
+    return false;
+  event_loop_ = wl_display_get_event_loop(display_.get());
+
+  int fd[2];
+  if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fd) < 0)
+    return false;
+  base::ScopedFD server_fd(fd[0]);
+  base::ScopedFD client_fd(fd[1]);
+
+  // If client has not specified rect before, user standard ones.
+  if (output_.GetRect().IsEmpty())
+    output_.SetRect(gfx::Rect(0, 0, 800, 600));
+
+  if (wl_display_init_shm(display_.get()) < 0)
+    return false;
+  if (!compositor_.Initialize(display_.get()))
+    return false;
+  if (!output_.Initialize(display_.get()))
+    return false;
+  if (!data_device_manager_.Initialize(display_.get()))
+    return false;
+  if (!seat_.Initialize(display_.get()))
+    return false;
+  if (shell_version == 5) {
+    if (!xdg_shell_.Initialize(display_.get()))
+      return false;
+  } else if (shell_version == 6) {
+    if (!zxdg_shell_v6_.Initialize(display_.get()))
+      return false;
+  } else {
+    NOTREACHED() << "Unsupported shell version: " << shell_version;
+  }
+  if (!zwp_text_input_manager_v1_.Initialize(display_.get()))
+    return false;
+  if (!zwp_linux_dmabuf_v1_.Initialize(display_.get()))
+    return false;
+
+  client_ = wl_client_create(display_.get(), server_fd.release());
+  if (!client_)
+    return false;
+
+  base::Thread::Options options;
+  options.message_pump_factory = base::BindRepeating(
+      &TestWaylandServerThread::CreateMessagePump, base::Unretained(this));
+  if (!base::Thread::StartWithOptions(options))
+    return false;
+
+  setenv("WAYLAND_SOCKET", base::UintToString(client_fd.release()).c_str(), 1);
+
+  return true;
+}
+
+void TestWaylandServerThread::Pause() {
+  task_runner()->PostTask(FROM_HERE,
+                          base::BindOnce(&TestWaylandServerThread::DoPause,
+                                         base::Unretained(this)));
+  pause_event_.Wait();
+}
+
+void TestWaylandServerThread::Resume() {
+  if (display_)
+    wl_display_flush_clients(display_.get());
+  resume_event_.Signal();
+}
+
+void TestWaylandServerThread::DoPause() {
+  base::RunLoop().RunUntilIdle();
+  pause_event_.Signal();
+  resume_event_.Wait();
+}
+
+std::unique_ptr<base::MessagePump>
+TestWaylandServerThread::CreateMessagePump() {
+  auto pump = std::make_unique<base::MessagePumpLibevent>();
+  pump->WatchFileDescriptor(wl_event_loop_get_fd(event_loop_), true,
+                            base::MessagePumpLibevent::WATCH_READ, &controller_,
+                            this);
+  return std::move(pump);
+}
+
+void TestWaylandServerThread::OnFileCanReadWithoutBlocking(int fd) {
+  wl_event_loop_dispatch(event_loop_, 0);
+  wl_display_flush_clients(display_.get());
+}
+
+void TestWaylandServerThread::OnFileCanWriteWithoutBlocking(int fd) {}
+
+}  // namespace wl
diff --git a/ui/ozone/platform/wayland/test/test_wayland_server_thread.h b/ui/ozone/platform/wayland/test/test_wayland_server_thread.h
new file mode 100644
index 0000000..24c00bf5
--- /dev/null
+++ b/ui/ozone/platform/wayland/test/test_wayland_server_thread.h
@@ -0,0 +1,118 @@
+// 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 UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_WAYLAND_SERVER_THREAD_H_
+#define UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_WAYLAND_SERVER_THREAD_H_
+
+#include <memory>
+#include <vector>
+
+#include <wayland-server-core.h>
+
+#include "base/message_loop/message_pump_libevent.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "ui/ozone/platform/wayland/test/global_object.h"
+#include "ui/ozone/platform/wayland/test/mock_xdg_shell.h"
+#include "ui/ozone/platform/wayland/test/mock_zwp_linux_dmabuf.h"
+#include "ui/ozone/platform/wayland/test/test_compositor.h"
+#include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
+#include "ui/ozone/platform/wayland/test/test_output.h"
+#include "ui/ozone/platform/wayland/test/test_seat.h"
+#include "ui/ozone/platform/wayland/test/test_zwp_text_input_manager.h"
+
+struct wl_client;
+struct wl_display;
+struct wl_event_loop;
+struct wl_resource;
+
+namespace wl {
+
+struct DisplayDeleter {
+  void operator()(wl_display* display);
+};
+
+class TestWaylandServerThread : public base::Thread,
+                                base::MessagePumpLibevent::FdWatcher {
+ public:
+  TestWaylandServerThread();
+  ~TestWaylandServerThread() override;
+
+  // Starts the test Wayland server thread. If this succeeds, the WAYLAND_SOCKET
+  // environment variable will be set to the string representation of a file
+  // descriptor that a client can connect to. The caller is responsible for
+  // ensuring that this file descriptor gets closed (for example, by calling
+  // wl_display_connect).
+  // Instantiates an xdg_shell of version |shell_version|; versions 5 and 6 are
+  // supported.
+  bool Start(uint32_t shell_version);
+
+  // Pauses the server thread when it becomes idle.
+  void Pause();
+
+  // Resumes the server thread after flushing client connections.
+  void Resume();
+
+  template <typename T>
+  T* GetObject(uint32_t id) {
+    wl_resource* resource = wl_client_get_object(client_, id);
+    return resource ? T::FromResource(resource) : nullptr;
+  }
+
+  TestOutput* CreateAndInitializeOutput() {
+    auto output = std::make_unique<TestOutput>();
+    output->Initialize(display());
+
+    TestOutput* output_ptr = output.get();
+    globals_.push_back(std::move(output));
+    return output_ptr;
+  }
+
+  TestDataDeviceManager* data_device_manager() { return &data_device_manager_; }
+  TestSeat* seat() { return &seat_; }
+  MockXdgShell* xdg_shell() { return &xdg_shell_; }
+  TestOutput* output() { return &output_; }
+  TestZwpTextInputManagerV1* text_input_manager_v1() {
+    return &zwp_text_input_manager_v1_;
+  }
+  MockZwpLinuxDmabufV1* zwp_linux_dmabuf_v1() { return &zwp_linux_dmabuf_v1_; }
+
+  wl_display* display() const { return display_.get(); }
+
+ private:
+  void DoPause();
+
+  std::unique_ptr<base::MessagePump> CreateMessagePump();
+
+  // base::MessagePumpLibevent::FdWatcher
+  void OnFileCanReadWithoutBlocking(int fd) override;
+  void OnFileCanWriteWithoutBlocking(int fd) override;
+
+  std::unique_ptr<wl_display, DisplayDeleter> display_;
+  wl_client* client_ = nullptr;
+  wl_event_loop* event_loop_ = nullptr;
+
+  base::WaitableEvent pause_event_;
+  base::WaitableEvent resume_event_;
+
+  // Represent Wayland global objects
+  TestCompositor compositor_;
+  TestDataDeviceManager data_device_manager_;
+  TestOutput output_;
+  TestSeat seat_;
+  MockXdgShell xdg_shell_;
+  MockZxdgShellV6 zxdg_shell_v6_;
+  TestZwpTextInputManagerV1 zwp_text_input_manager_v1_;
+  MockZwpLinuxDmabufV1 zwp_linux_dmabuf_v1_;
+
+  std::vector<std::unique_ptr<GlobalObject>> globals_;
+
+  base::MessagePumpLibevent::FdWatchController controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWaylandServerThread);
+};
+
+}  // namespace wl
+
+#endif  // UI_OZONE_PLATFORM_WAYLAND_TEST_TEST_WAYLAND_SERVER_THREAD_H_
diff --git a/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.cc b/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.cc
index 19868a76..30b178a 100644
--- a/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.cc
+++ b/ui/ozone/platform/wayland/test/test_zwp_text_input_manager.cc
@@ -17,18 +17,11 @@
 void CreateTextInput(struct wl_client* client,
                      struct wl_resource* resource,
                      uint32_t id) {
-  auto* im = static_cast<TestZwpTextInputManagerV1*>(
-      wl_resource_get_user_data(resource));
-  wl_resource* text_resource =
-      wl_resource_create(client, &zwp_text_input_v1_interface,
-                         wl_resource_get_version(resource), id);
-  if (!text_resource) {
-    wl_client_post_no_memory(client);
-    return;
-  }
-  SetImplementation(text_resource, &kMockZwpTextInputV1Impl,
-                    std::make_unique<MockZwpTextInput>(text_resource));
-  im->set_text_input(GetUserDataAs<MockZwpTextInput>(text_resource));
+  wl_resource* text_resource = CreateResourceWithImpl<MockZwpTextInput>(
+      client, &zwp_text_input_v1_interface, wl_resource_get_version(resource),
+      &kMockZwpTextInputV1Impl, id);
+  GetUserDataAs<TestZwpTextInputManagerV1>(resource)->set_text_input(
+      GetUserDataAs<MockZwpTextInput>(text_resource));
 }
 
 }  // namespace
diff --git a/ui/ozone/platform/wayland/wayland_connection_unittest.cc b/ui/ozone/platform/wayland/wayland_connection_unittest.cc
index 9c6cc51..9438a32e 100644
--- a/ui/ozone/platform/wayland/wayland_connection_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_connection_unittest.cc
@@ -8,7 +8,7 @@
 #include "base/message_loop/message_loop.h"
 #include "base/run_loop.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
 
 namespace ui {
@@ -19,7 +19,7 @@
 
 TEST(WaylandConnectionTest, UseUnstableVersion) {
   base::MessageLoopForUI message_loop;
-  wl::FakeServer server;
+  wl::TestWaylandServerThread server;
   EXPECT_CALL(*server.xdg_shell(),
               UseUnstableVersion(XDG_SHELL_VERSION_CURRENT));
   ASSERT_TRUE(server.Start(kXdgVersion5));
@@ -33,7 +33,7 @@
 
 TEST(WaylandConnectionTest, Ping) {
   base::MessageLoopForUI message_loop;
-  wl::FakeServer server;
+  wl::TestWaylandServerThread server;
   ASSERT_TRUE(server.Start(kXdgVersion5));
   WaylandConnection connection;
   ASSERT_TRUE(connection.Initialize());
diff --git a/ui/ozone/platform/wayland/wayland_data_device_unittest.cc b/ui/ozone/platform/wayland/wayland_data_device_unittest.cc
index d3e33fa7..219ff760 100644
--- a/ui/ozone/platform/wayland/wayland_data_device_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_data_device_unittest.cc
@@ -9,13 +9,13 @@
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
 #include "ui/events/base_event_utils.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/test/constants.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/test_data_device.h"
 #include "ui/ozone/platform/wayland/test/test_data_device_manager.h"
 #include "ui/ozone/platform/wayland/test/test_data_offer.h"
 #include "ui/ozone/platform/wayland/test/test_data_source.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/public/platform_clipboard.h"
 
@@ -86,6 +86,7 @@
   wl::TestDataDeviceManager* data_device_manager_;
   std::unique_ptr<MockClipboardClient> clipboard_client_;
 
+ private:
   DISALLOW_COPY_AND_ASSIGN(WaylandDataDeviceManagerTest);
 };
 
diff --git a/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc b/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc
index bd19f70..39d7664 100644
--- a/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc
@@ -10,9 +10,9 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/ime/linux/linux_input_method_context.h"
 #include "ui/events/event.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/mock_zwp_text_input.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_input_method_context.h"
 #include "ui/ozone/platform/wayland/wayland_input_method_context_factory.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
diff --git a/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc b/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc
index e33fb33..7d85608 100644
--- a/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_keyboard_unittest.cc
@@ -10,9 +10,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/test_keyboard.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 
 #if BUILDFLAG(USE_XKBCOMMON)
diff --git a/ui/ozone/platform/wayland/wayland_pointer_unittest.cc b/ui/ozone/platform/wayland/wayland_pointer_unittest.cc
index 2461a2b..24233ba1 100644
--- a/ui/ozone/platform/wayland/wayland_pointer_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_pointer_unittest.cc
@@ -8,9 +8,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/test_pointer.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/test/mock_platform_window_delegate.h"
diff --git a/ui/ozone/platform/wayland/wayland_screen_unittest.cc b/ui/ozone/platform/wayland/wayland_screen_unittest.cc
index 723b2e68..2e680a1 100644
--- a/ui/ozone/platform/wayland/wayland_screen_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_screen_unittest.cc
@@ -8,9 +8,9 @@
 
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/display/display_observer.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/test_pointer.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
 #include "ui/ozone/platform/wayland/wayland_output_manager.h"
 #include "ui/ozone/platform/wayland/wayland_screen.h"
diff --git a/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc b/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc
index 876b51c..19650e9 100644
--- a/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_surface_factory_unittest.cc
@@ -5,8 +5,8 @@
 #include "ui/ozone/platform/wayland/wayland_surface_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkSurface.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/public/surface_ozone_canvas.h"
diff --git a/ui/ozone/platform/wayland/wayland_test.cc b/ui/ozone/platform/wayland/wayland_test.cc
index 60e3d19..219a2f5 100644
--- a/ui/ozone/platform/wayland/wayland_test.cc
+++ b/ui/ozone/platform/wayland/wayland_test.cc
@@ -15,12 +15,14 @@
 #include "ui/events/ozone/layout/stub/stub_keyboard_layout_engine.h"
 #endif
 
-using ::testing::SaveArg;
 using ::testing::_;
+using ::testing::SaveArg;
 
 namespace ui {
 
-WaylandTest::WaylandTest() {
+WaylandTest::WaylandTest()
+    : scoped_task_environment_(
+          base::test::ScopedTaskEnvironment::MainThreadType::UI) {
 #if BUILDFLAG(USE_XKBCOMMON)
   KeyboardLayoutEngineManager::SetKeyboardLayoutEngine(
       std::make_unique<WaylandXkbKeyboardLayoutEngine>(
diff --git a/ui/ozone/platform/wayland/wayland_test.h b/ui/ozone/platform/wayland/wayland_test.h
index 22a58d0..4a26306 100644
--- a/ui/ozone/platform/wayland/wayland_test.h
+++ b/ui/ozone/platform/wayland/wayland_test.h
@@ -6,10 +6,11 @@
 #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_TEST_H_
 
 #include "base/message_loop/message_loop.h"
+#include "base/test/scoped_task_environment.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/base/buildflags.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_connection.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/test/mock_platform_window_delegate.h"
@@ -27,7 +28,7 @@
 const uint32_t kXdgShellV5 = 5;
 const uint32_t kXdgShellV6 = 6;
 
-// WaylandTest is a base class that sets up a display, window, and fake server,
+// WaylandTest is a base class that sets up a display, window, and test server,
 // and allows easy synchronization between them.
 class WaylandTest : public ::testing::TestWithParam<uint32_t> {
  public:
@@ -39,12 +40,8 @@
 
   void Sync();
 
- private:
-  base::MessageLoopForUI message_loop_;
-  bool initialized_ = false;
-
  protected:
-  wl::FakeServer server_;
+  wl::TestWaylandServerThread server_;
   wl::MockSurface* surface_;
 
   MockPlatformWindowDelegate delegate_;
@@ -54,6 +51,9 @@
   gfx::AcceleratedWidget widget_ = gfx::kNullAcceleratedWidget;
 
  private:
+  base::test::ScopedTaskEnvironment scoped_task_environment_;
+  bool initialized_ = false;
+
 #if BUILDFLAG(USE_XKBCOMMON)
   XkbEvdevCodes xkb_evdev_code_converter_;
 #endif
diff --git a/ui/ozone/platform/wayland/wayland_touch_unittest.cc b/ui/ozone/platform/wayland/wayland_touch_unittest.cc
index e641fdbe..831dd903 100644
--- a/ui/ozone/platform/wayland/wayland_touch_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_touch_unittest.cc
@@ -8,9 +8,9 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/events/event.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/test_touch.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/platform/wayland/wayland_window.h"
 #include "ui/ozone/test/mock_platform_window_delegate.h"
diff --git a/ui/ozone/platform/wayland/wayland_window_unittest.cc b/ui/ozone/platform/wayland/wayland_window_unittest.cc
index 9e1c0fe..cf0bd24 100644
--- a/ui/ozone/platform/wayland/wayland_window_unittest.cc
+++ b/ui/ozone/platform/wayland/wayland_window_unittest.cc
@@ -16,9 +16,9 @@
 #include "ui/base/hit_test.h"
 #include "ui/events/base_event_utils.h"
 #include "ui/events/event.h"
-#include "ui/ozone/platform/wayland/fake_server.h"
 #include "ui/ozone/platform/wayland/test/mock_surface.h"
 #include "ui/ozone/platform/wayland/test/test_pointer.h"
+#include "ui/ozone/platform/wayland/test/test_wayland_server_thread.h"
 #include "ui/ozone/platform/wayland/wayland_test.h"
 #include "ui/ozone/platform/wayland/wayland_util.h"
 #include "ui/ozone/test/mock_platform_window_delegate.h"
diff --git a/ui/strings/ui_strings.grd b/ui/strings/ui_strings.grd
index 1386a7c..335f623 100644
--- a/ui/strings/ui_strings.grd
+++ b/ui/strings/ui_strings.grd
@@ -776,6 +776,15 @@
       <message name="IDS_ALL_APPS_INDICATOR" desc="Indicator text in the launcher on top of all apps.">
         ALL APPS
       </message>
+      <message name="IDS_APP_LIST_APP_DRAG_LOCATION_ACCESSIBILE_NAME" desc="The spoken feedback which tells the location that a dragged app will drop to.">
+        Move to Page <ph name="PAGE_NUMBER">$1<ex>1</ex></ph>, row <ph name="ROW_NUMBER">$2<ex>4</ex></ph>, column <ph name="COLUMN_NUMBER">$3<ex>2</ex></ph>.
+      </message>
+      <message name="IDS_APP_LIST_APP_DRAG_CREATE_FOLDER_ACCESSIBILE_NAME" desc="The spoken feedback text which describes the app that is currently being dragged over another app, and how to create a folder.">
+        <ph name="DRAGGED_APP_NAME">$1<ex>App Name</ex></ph> on top of <ph name="IN_PLACE_APP">$2<ex>Other App Name</ex></ph>, release to create folder.
+      </message>
+      <message name="IDS_APP_LIST_APP_DRAG_MOVE_TO_FOLDER_ACCESSIBILE_NAME" desc="The spoken feedback given while dragging an app, which describes the folder name the app would drop into if the drag were ended.">
+        Move <ph name="DRAGGED_APP">$1<ex>App Name</ex></ph> to folder <ph name="FOLDER_NAME">$2<ex>Folder Name</ex></ph>.
+      </message>
       <message name="IDS_APP_LIST_BACK" desc="The name of the button that goes to the previous screen in the launcher.">
         Back
       </message>
diff --git a/ui/views/cocoa/drag_drop_client_mac.h b/ui/views/cocoa/drag_drop_client_mac.h
index 1cfc235f..065e730 100644
--- a/ui/views/cocoa/drag_drop_client_mac.h
+++ b/ui/views/cocoa/drag_drop_client_mac.h
@@ -7,8 +7,9 @@
 
 #import <Cocoa/Cocoa.h>
 
+#include <memory>
+
 #include "base/callback.h"
-#import "base/mac/scoped_nsobject.h"
 #include "base/macros.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/dragdrop/os_exchange_data.h"
@@ -16,14 +17,6 @@
 #include "ui/views/widget/drop_helper.h"
 #include "ui/views_bridge_mac/drag_drop_client.h"
 
-// This class acts as a bridge between NSPasteboardItem and OSExchangeData by
-// implementing NSPasteboardItemDataProvider and writing data from
-// OSExchangeData into the pasteboard.
-VIEWS_EXPORT
-@interface CocoaDragDropDataProvider : NSObject<NSPasteboardItemDataProvider>
-- (instancetype)initWithData:(const ui::OSExchangeData&)data;
-@end
-
 namespace gfx {
 class Point;
 }
@@ -66,7 +59,7 @@
   gfx::Point LocationInView(NSPoint point) const;
 
   // Provides the data for the drag and drop session.
-  base::scoped_nsobject<CocoaDragDropDataProvider> data_source_;
+  std::unique_ptr<ui::OSExchangeData> exchange_data_;
 
   // Used to handle drag and drop with Views.
   DropHelper drop_helper_;
diff --git a/ui/views/cocoa/drag_drop_client_mac.mm b/ui/views/cocoa/drag_drop_client_mac.mm
index b2275340..b4c2986d 100644
--- a/ui/views/cocoa/drag_drop_client_mac.mm
+++ b/ui/views/cocoa/drag_drop_client_mac.mm
@@ -14,47 +14,6 @@
 #import "ui/views_bridge_mac/bridged_content_view.h"
 #import "ui/views_bridge_mac/bridged_native_widget_impl.h"
 
-@interface CocoaDragDropDataProvider ()
-- (instancetype)initWithData:(const ui::OSExchangeData&)data;
-- (instancetype)initWithPasteboard:(NSPasteboard*)pasteboard;
-@end
-
-@implementation CocoaDragDropDataProvider {
-  std::unique_ptr<ui::OSExchangeData> data_;
-}
-
-- (instancetype)initWithData:(const ui::OSExchangeData&)data {
-  if ((self = [super init])) {
-    data_.reset(new OSExchangeData(
-        std::unique_ptr<OSExchangeData::Provider>(data.provider().Clone())));
-  }
-  return self;
-}
-
-- (instancetype)initWithPasteboard:(NSPasteboard*)pasteboard {
-  if ((self = [super init])) {
-    data_ = ui::OSExchangeDataProviderMac::CreateDataFromPasteboard(pasteboard);
-  }
-  return self;
-}
-
-- (ui::OSExchangeData*)data {
-  return data_.get();
-}
-
-// NSPasteboardItemDataProvider protocol implementation.
-
-- (void)pasteboard:(NSPasteboard*)sender
-                  item:(NSPasteboardItem*)item
-    provideDataForType:(NSString*)type {
-  const ui::OSExchangeDataProviderMac& provider =
-      static_cast<const ui::OSExchangeDataProviderMac&>(data_->provider());
-  NSData* ns_data = provider.GetNSDataForType(type);
-  [sender setData:ns_data forType:type];
-}
-
-@end
-
 namespace views {
 
 DragDropClientMac::DragDropClientMac(BridgedNativeWidgetImpl* bridge,
@@ -74,12 +33,15 @@
     const ui::OSExchangeData& data,
     int operation,
     ui::DragDropTypes::DragEventSource source) {
-  data_source_.reset([[CocoaDragDropDataProvider alloc] initWithData:data]);
+  // TODO(avi): Why must this data be cloned?
+  exchange_data_ =
+      std::make_unique<ui::OSExchangeData>(data.provider().Clone());
   operation_ = operation;
   is_drag_source_ = true;
 
-  const ui::OSExchangeDataProviderMac& provider =
-      static_cast<const ui::OSExchangeDataProviderMac&>(data.provider());
+  const ui::OSExchangeDataProviderMac& provider_mac =
+      static_cast<const ui::OSExchangeDataProviderMac&>(
+          exchange_data_->provider());
 
   // Release capture before beginning the dragging session. Capture may have
   // been acquired on the mouseDown, but capture is not required during the
@@ -102,7 +64,7 @@
                                       pressure:1.0];
 
   NSImage* image = gfx::NSImageFromImageSkiaWithColorSpace(
-      provider.GetDragImage(), base::mac::GetSRGBColorSpace());
+      provider_mac.GetDragImage(), base::mac::GetSRGBColorSpace());
 
   // TODO(crbug/876201): This shouldn't happen. When a repro for this
   // is identified and the bug is fixed, change the early return to
@@ -110,12 +72,11 @@
   if (!image || NSEqualSizes([image size], NSZeroSize))
     return;
 
-  base::scoped_nsobject<NSPasteboardItem> item([[NSPasteboardItem alloc] init]);
-  [item setDataProvider:data_source_.get()
-               forTypes:provider.GetAvailableTypes()];
+  NSPasteboardItem* item =
+      [[provider_mac.GetPasteboard() pasteboardItems] firstObject];
 
   base::scoped_nsobject<NSDraggingItem> drag_item(
-      [[NSDraggingItem alloc] initWithPasteboardWriter:item.get()]);
+      [[NSDraggingItem alloc] initWithPasteboardWriter:item]);
 
   // Subtract the image's height from the y location so that the mouse will be
   // at the upper left corner of the image.
@@ -137,32 +98,30 @@
 }
 
 NSDragOperation DragDropClientMac::DragUpdate(id<NSDraggingInfo> sender) {
-  if (!data_source_.get()) {
-    data_source_.reset([[CocoaDragDropDataProvider alloc]
-        initWithPasteboard:[sender draggingPasteboard]]);
+  if (!exchange_data_) {
+    exchange_data_ = std::make_unique<OSExchangeData>(
+        ui::OSExchangeDataProviderMac::CreateProviderWrappingPasteboard(
+            [sender draggingPasteboard]));
     operation_ = ui::DragDropTypes::NSDragOperationToDragOperation(
         [sender draggingSourceOperationMask]);
   }
 
   int drag_operation = drop_helper_.OnDragOver(
-      *[data_source_ data], LocationInView([sender draggingLocation]),
-      operation_);
+      *exchange_data_, LocationInView([sender draggingLocation]), operation_);
   return ui::DragDropTypes::DragOperationToNSDragOperation(drag_operation);
 }
 
 NSDragOperation DragDropClientMac::Drop(id<NSDraggingInfo> sender) {
-  // OnDrop may delete |this|, so clear |data_source_| first.
-  base::scoped_nsobject<CocoaDragDropDataProvider> data_source(
-      std::move(data_source_));
+  // OnDrop may delete |this|, so clear |exchange_data_| first.
+  std::unique_ptr<ui::OSExchangeData> exchange_data = std::move(exchange_data_);
 
   int drag_operation = drop_helper_.OnDrop(
-      *[data_source data], LocationInView([sender draggingLocation]),
-      operation_);
+      *exchange_data, LocationInView([sender draggingLocation]), operation_);
   return ui::DragDropTypes::DragOperationToNSDragOperation(drag_operation);
 }
 
 void DragDropClientMac::EndDrag() {
-  data_source_.reset();
+  exchange_data_.reset();
   is_drag_source_ = false;
 
   // Allow a test to invoke EndDrag() without spinning the nested run loop.
@@ -175,7 +134,7 @@
 void DragDropClientMac::DragExit() {
   drop_helper_.OnDragExit();
   if (!is_drag_source_)
-    data_source_.reset();
+    exchange_data_.reset();
 }
 
 gfx::Point DragDropClientMac::LocationInView(NSPoint point) const {
diff --git a/ui/views/cocoa/drag_drop_client_mac_unittest.mm b/ui/views/cocoa/drag_drop_client_mac_unittest.mm
index 7e47378..80160422 100644
--- a/ui/views/cocoa/drag_drop_client_mac_unittest.mm
+++ b/ui/views/cocoa/drag_drop_client_mac_unittest.mm
@@ -177,8 +177,8 @@
   }
 
   void SetData(OSExchangeData& data) {
-    drag_drop_client()->data_source_.reset(
-        [[CocoaDragDropDataProvider alloc] initWithData:data]);
+    drag_drop_client()->exchange_data_ =
+        std::make_unique<ui::OSExchangeData>(data.provider().Clone());
   }
 
   // testing::Test:
diff --git a/ui/views/controls/focus_ring.cc b/ui/views/controls/focus_ring.cc
index 942cecb..55eaacd6 100644
--- a/ui/views/controls/focus_ring.cc
+++ b/ui/views/controls/focus_ring.cc
@@ -29,10 +29,9 @@
 
 // static
 std::unique_ptr<FocusRing> FocusRing::Install(View* parent) {
-  auto ring = base::WrapUnique<FocusRing>(new FocusRing(parent));
+  auto ring = base::WrapUnique<FocusRing>(new FocusRing());
   ring->set_owned_by_client();
   parent->AddChildView(ring.get());
-  parent->AddObserver(ring.get());
   ring->Layout();
   ring->SchedulePaint();
   return ring;
@@ -76,6 +75,22 @@
   SetBoundsRect(focus_bounds);
 }
 
+void FocusRing::ViewHierarchyChanged(
+    const ViewHierarchyChangedDetails& details) {
+  if (details.child != this)
+    return;
+
+  if (details.is_add) {
+    // Need to start observing the parent.
+    details.parent->AddObserver(this);
+  } else {
+    // This view is being removed from its parent. It needs to remove itself
+    // from its parent's observer list. Otherwise, since its |parent_| will
+    // become a nullptr, it won't be able to do so in its destructor.
+    details.parent->RemoveObserver(this);
+  }
+}
+
 void FocusRing::OnPaint(gfx::Canvas* canvas) {
   if (!has_focus_predicate_(parent()))
     return;
@@ -98,7 +113,7 @@
     canvas->sk_canvas()->drawRRect(RingRectFromPathRect(bounds), paint);
   } else if (path.isOval(&bounds)) {
     gfx::RectF rect = gfx::SkRectToRectF(bounds);
-    View::ConvertRectToTarget(view_, this, &rect);
+    View::ConvertRectToTarget(parent(), this, &rect);
     canvas->sk_canvas()->drawRRect(SkRRect::MakeOval(gfx::RectFToSkRect(rect)),
                                    paint);
   } else if (path.isRRect(&rbounds)) {
@@ -114,7 +129,7 @@
   SchedulePaint();
 }
 
-FocusRing::FocusRing(View* parent) : view_(parent) {
+FocusRing::FocusRing() {
   // A layer is necessary to paint beyond the parent's bounds.
   SetPaintToLayer();
   layer()->SetFillsBoundsOpaquely(false);
@@ -138,7 +153,7 @@
 SkRRect FocusRing::RingRectFromPathRect(const SkRRect& rrect) const {
   double thickness = PlatformStyle::kFocusHaloThickness / 2.f;
   gfx::RectF r = gfx::SkRectToRectF(rrect.rect());
-  View::ConvertRectToTarget(view_, this, &r);
+  View::ConvertRectToTarget(parent(), this, &r);
 
   SkRRect skr =
       rrect.makeOffset(r.x() - rrect.rect().x(), r.y() - rrect.rect().y());
diff --git a/ui/views/controls/focus_ring.h b/ui/views/controls/focus_ring.h
index d356c77..226ce07 100644
--- a/ui/views/controls/focus_ring.h
+++ b/ui/views/controls/focus_ring.h
@@ -79,6 +79,8 @@
   // View:
   const char* GetClassName() const override;
   void Layout() override;
+  void ViewHierarchyChanged(
+      const ViewHierarchyChangedDetails& details) override;
   void OnPaint(gfx::Canvas* canvas) override;
 
   // ViewObserver:
@@ -86,7 +88,7 @@
   void OnViewBlurred(View* view) override;
 
  private:
-  explicit FocusRing(View* parent);
+  FocusRing();
 
   // Translates the provided SkRect or SkRRect, which is in the parent's
   // coordinate system, into this view's coordinate system, then insets it
@@ -96,9 +98,6 @@
   SkRRect RingRectFromPathRect(const SkRect& rect) const;
   SkRRect RingRectFromPathRect(const SkRRect& rect) const;
 
-  // The View this focus ring is installed on.
-  View* view_ = nullptr;
-
   // The path to draw this focus ring around. IsPathUseable(path_) is always
   // true.
   SkPath path_;
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_list.html b/ui/webui/resources/cr_components/certificate_manager/certificate_list.html
index 55396b5f..299da91b 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_list.html
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_list.html
@@ -23,11 +23,12 @@
       <span class="flex">
           [[getDescription_(certificateType, certificates)]]</span>
       <paper-button id="import" on-tap="onImportTap_"
-          hidden="[[!canImport_(certificateType)]]">
+          hidden="[[!canImport_(certificateType, importAllowed, isKiosk_)]]">
         [[i18n('certificateManagerImport')]]</paper-button>
 <if expr="chromeos">
       <paper-button id="importAndBind" on-tap="onImportAndBindTap_"
-          hidden="[[!canImportAndBind_(certificateType, isGuest_)]]">
+          hidden="[[!canImportAndBind_(certificateType, importAllowed,
+                 isGuest_)]]">
         [[i18n('certificateManagerImportAndBind')]]</paper-button>
 </if>
     </div>
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_list.js b/ui/webui/resources/cr_components/certificate_manager/certificate_list.js
index fd8dd24d..6f9646e 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_list.js
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_list.js
@@ -21,6 +21,9 @@
     /** @type {!CertificateType} */
     certificateType: String,
 
+    /** @type {boolean} */
+    importAllowed: Boolean,
+
     // 'if expr="chromeos"' here is breaking vulcanize. TODO(stevenjb/dpapad):
     // Restore after migrating to polymer-bundler, crbug.com/731881.
     /** @private */
@@ -72,7 +75,8 @@
    * @private
    */
   canImport_: function() {
-    return !this.isKiosk_ && this.certificateType != CertificateType.OTHER;
+    return !this.isKiosk_ && this.certificateType != CertificateType.OTHER &&
+        this.importAllowed;
   },
 
   // <if expr="chromeos">
@@ -81,7 +85,8 @@
    * @private
    */
   canImportAndBind_: function() {
-    return !this.isGuest_ && this.certificateType == CertificateType.PERSONAL;
+    return !this.isGuest_ && this.certificateType == CertificateType.PERSONAL &&
+        this.importAllowed;
   },
   // </if>
 
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html b/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html
index af03ef1..77b6860 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_manager.html
@@ -63,14 +63,16 @@
       <div>
         <certificate-list id="personalCerts"
             certificates="[[personalCerts]]"
-            certificate-type="[[certificateTypeEnum_.PERSONAL]]">
+            certificate-type="[[certificateTypeEnum_.PERSONAL]]"
+            import-allowed="[[importAllowed]]">
         </certificate-list>
       </div>
       <div>
         <template is="dom-if" if="[[isTabSelected_(selected, 1)]]">
           <certificate-list id="serverCerts"
               certificates="[[serverCerts]]"
-              certificate-type="[[certificateTypeEnum_.SERVER]]">
+              certificate-type="[[certificateTypeEnum_.SERVER]]"
+              import-allowed="[[importAllowed]]">
           </certificate-list>
         </template>
       </div>
@@ -78,7 +80,8 @@
         <template is="dom-if" if="[[isTabSelected_(selected, 2)]]">
           <certificate-list id="caCerts"
               certificates="[[caCerts]]"
-              certificate-type="[[certificateTypeEnum_.CA]]">
+              certificate-type="[[certificateTypeEnum_.CA]]"
+              import-allowed="[[importAllowed]]">
           </certificate-list>
         </template>
       </div>
@@ -86,7 +89,8 @@
         <template is="dom-if" if="[[isTabSelected_(selected, 3)]]">
           <certificate-list id="otherCerts"
               certificates="[[otherCerts]]"
-              certificate-type="[[certificateTypeEnum_.OTHER]]">
+              certificate-type="[[certificateTypeEnum_.OTHER]]"
+              import-allowed="[[importAllowed]]">
           </certificate-list>
         </template>
       </div>
diff --git a/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js b/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js
index 09375d2..c0297fb 100644
--- a/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js
+++ b/ui/webui/resources/cr_components/certificate_manager/certificate_manager.js
@@ -49,6 +49,16 @@
       },
     },
 
+    /**
+     * Indicates if certificate import is allowed by Chrome OS specific policy
+     * CertificateManagementAllowed.
+     * Value exists only for Chrome OS.
+     */
+    importAllowed: {
+      type: Boolean,
+      value: true,
+    },
+
     /** @private */
     certificateTypeEnum_: {
       type: Object,
@@ -110,10 +120,17 @@
   /** @override */
   attached: function() {
     this.addWebUIListener('certificates-changed', this.set.bind(this));
+    this.addWebUIListener(
+        'certificates-model-ready', this.setImportAllowed.bind(this));
     certificate_manager.CertificatesBrowserProxyImpl.getInstance()
         .refreshCertificates();
   },
 
+  /** @private */
+  setImportAllowed: function(allowed) {
+    this.importAllowed = allowed;
+  },
+
   /**
    * @param {number} selectedIndex
    * @param {number} tabIndex
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html
index 654b13cb..c9509f90 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html
@@ -104,8 +104,8 @@
       }
 
       #cameraControls > div {
-        margin: 0 4px;
-        width: 32px;
+        margin: 0 12px;
+        width: 24px;
       }
 
       #cameraControls > button {